mirror of
https://github.com/DarkflameUniverse/DarkflameServer.git
synced 2025-12-19 03:36:56 -06:00
Compare commits
122 Commits
aggregate-
...
components
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6240eefc7e | ||
|
|
949a6db4bc | ||
|
|
49f3d757e5 | ||
|
|
c204ea4009 | ||
|
|
a37ec326b9 | ||
|
|
8678ed03c3 | ||
|
|
37bcc81694 | ||
|
|
4d88f63338 | ||
|
|
59831fc15d | ||
|
|
fe6b279ebb | ||
|
|
598d88b307 | ||
|
|
28637a206d | ||
|
|
87675aa62d | ||
|
|
83780affbd | ||
|
|
7ca9e59ef2 | ||
|
|
d2a7e147cc | ||
|
|
8a512e5c76 | ||
|
|
2528e02b98 | ||
|
|
1b7be5d7db | ||
|
|
790bd6c03b | ||
|
|
28fbe20b97 | ||
|
|
cf53e35af5 | ||
|
|
5301346ed5 | ||
|
|
001f6a75eb | ||
|
|
950a1fe423 | ||
| 197d1bcdee | |||
|
|
68a2a04e5d | ||
|
|
cfec9801a8 | ||
|
|
8ede5b87ca | ||
|
|
c22040c6eb | ||
| 6fb1786cf1 | |||
|
|
81404d9671 | ||
|
|
0544eeba1f | ||
|
|
c2fe7f6205 | ||
|
|
f55bec026d | ||
|
|
478b6ff6a9 | ||
|
|
7c1265911c | ||
|
|
c6063aac66 | ||
|
|
2abcb142ad | ||
|
|
d9a3bea6d5 | ||
|
|
fdcfbdee85 | ||
| fd182d222f | |||
| 68f90b7136 | |||
|
|
d29287f9d9 | ||
|
|
06acd23cb7 | ||
|
|
a5611e9c7f | ||
|
|
34cfd45d40 | ||
|
|
ec9278286b | ||
|
|
9121bf41c5 | ||
|
|
fee1025982 | ||
|
|
3f328a18be | ||
| 485a88dfd4 | |||
| c237c16c33 | |||
| d44b18e38f | |||
| d153d66e26 | |||
| 34d22d2d0d | |||
| bcbc5882dc | |||
| f27e0400e7 | |||
| c78760db59 | |||
| 907e045904 | |||
| dc96fcba85 | |||
|
|
e180430ede | ||
|
|
1bdec00a61 | ||
|
|
68a5cc1d89 | ||
|
|
be17d1a467 | ||
|
|
a992a28088 | ||
|
|
891648288a | ||
|
|
92006123b8 | ||
|
|
9a9b9aa813 | ||
|
|
ea975965ca | ||
|
|
35e5d8497b | ||
|
|
4d57eff946 | ||
|
|
2a8f40f8e8 | ||
|
|
355f4f4df8 | ||
|
|
451f7e76d7 | ||
|
|
83065dfb6f | ||
| fdd98ab825 | |||
|
|
31be1fbe4c | ||
|
|
d8e2e92428 | ||
| e389a619ad | |||
|
|
45bcc80a1b | ||
|
|
b2fee29ee0 | ||
|
|
326c495776 | ||
|
|
d224a86e93 | ||
|
|
f9ac0a9dec | ||
|
|
3f3810519d | ||
|
|
5be9146662 | ||
|
|
262b6ebb58 | ||
|
|
6f38a150d3 | ||
|
|
36c44ecc83 | ||
|
|
fc719cbb0a | ||
|
|
f78ea1bbc9 | ||
|
|
b43e5c2165 | ||
|
|
5f139c75e0 | ||
|
|
77dc6ff312 | ||
|
|
0b5df9f0b1 | ||
|
|
b91f84d884 | ||
|
|
cebe3c732a | ||
|
|
5714ac558e | ||
|
|
2a2799793d | ||
|
|
1c23f3c030 | ||
| a68fa69e7a | |||
|
|
ddc5f0e117 | ||
|
|
e3a716a9cf | ||
|
|
0b37dc1e4d | ||
|
|
f2d28cc0bd | ||
|
|
5da776a084 | ||
|
|
6f057204be | ||
|
|
f555ba8c25 | ||
|
|
e2dfa1809d | ||
|
|
62aa863997 | ||
|
|
ec00f5fd9d | ||
| d11e2db887 | |||
|
|
9e9e4dc087 | ||
|
|
ea9d0d8592 | ||
|
|
716a5fcf37 | ||
| 15988afb4c | |||
| 5e9a956bed | |||
| c2d5be0e5d | |||
| 2ae9a92b55 | |||
| 50978620d8 | |||
| c168f6c970 |
@@ -10,7 +10,7 @@ trim_trailing_whitespace=true
|
||||
end_of_line=lf
|
||||
insert_final_newline=true
|
||||
|
||||
[*.{c++,cc,cpp,cxx,h,h++,hh,hpp,hxx,inl,ipp,tlh,tli}]
|
||||
[*.{c++,cc,cpp,cxx,h,h++,hh,hpp,hxx,inl,ipp,tlh,tli,tcc,tpp}]
|
||||
|
||||
vc_generate_documentation_comments=doxygen_slash_star
|
||||
|
||||
|
||||
@@ -14,4 +14,4 @@ EXTERNAL_IP=localhost
|
||||
MARIADB_USER=darkflame
|
||||
MARIADB_PASSWORD=SECRET_VALUE_CHANGE_ME
|
||||
MARIADB_ROOT_PASSWORD=SECRET_VALUE_CHANGE_ME
|
||||
MARIADB_DATABASE=darkflame
|
||||
MARIADB_DATABASE=darkflame
|
||||
@@ -97,56 +97,15 @@ make_directory(${CMAKE_BINARY_DIR}/logs)
|
||||
|
||||
# Copy resource files on first build
|
||||
set(RESOURCE_FILES "sharedconfig.ini" "authconfig.ini" "chatconfig.ini" "worldconfig.ini" "masterconfig.ini" "blacklist.dcf")
|
||||
message(STATUS "Checking resource file integrity")
|
||||
foreach (resource_file ${RESOURCE_FILES})
|
||||
set(file_size 0)
|
||||
if (EXISTS ${PROJECT_BINARY_DIR}/${resource_file})
|
||||
file(SIZE ${PROJECT_BINARY_DIR}/${resource_file} file_size)
|
||||
endif()
|
||||
if (${file_size} EQUAL 0)
|
||||
foreach(resource_file ${RESOURCE_FILES})
|
||||
if (NOT EXISTS ${PROJECT_BINARY_DIR}/${resource_file})
|
||||
configure_file(
|
||||
${CMAKE_SOURCE_DIR}/resources/${resource_file} ${PROJECT_BINARY_DIR}/${resource_file}
|
||||
COPYONLY
|
||||
)
|
||||
message(STATUS "Moved " ${resource_file} " to project binary directory")
|
||||
elseif (resource_file MATCHES ".ini")
|
||||
message(STATUS "Checking " ${resource_file} " for missing config options")
|
||||
file(READ ${PROJECT_BINARY_DIR}/${resource_file} current_file_contents)
|
||||
string(REPLACE "\\\n" "" current_file_contents ${current_file_contents})
|
||||
string(REPLACE "\n" ";" current_file_contents ${current_file_contents})
|
||||
set(parsed_current_file_contents "")
|
||||
# Remove comment lines so they do not interfere with the variable parsing
|
||||
foreach (line ${current_file_contents})
|
||||
string(FIND ${line} "#" is_comment)
|
||||
if (NOT ${is_comment} EQUAL 0)
|
||||
string(APPEND parsed_current_file_contents ${line})
|
||||
endif()
|
||||
endforeach()
|
||||
file(READ ${CMAKE_SOURCE_DIR}/resources/${resource_file} depot_file_contents)
|
||||
string(REPLACE "\\\n" "" depot_file_contents ${depot_file_contents})
|
||||
string(REPLACE "\n" ";" depot_file_contents ${depot_file_contents})
|
||||
set(line_to_add "")
|
||||
foreach (line ${depot_file_contents})
|
||||
string(FIND ${line} "#" is_comment)
|
||||
if (NOT ${is_comment} EQUAL 0)
|
||||
string(REPLACE "=" ";" line_split ${line})
|
||||
list(GET line_split 0 variable_name)
|
||||
if (NOT ${parsed_current_file_contents} MATCHES ${variable_name})
|
||||
message(STATUS "Adding missing config option " ${variable_name} " to " ${resource_file})
|
||||
set(line_to_add ${line_to_add} ${line})
|
||||
foreach (line_to_append ${line_to_add})
|
||||
file(APPEND ${PROJECT_BINARY_DIR}/${resource_file} "\n" ${line_to_append})
|
||||
endforeach()
|
||||
file(APPEND ${PROJECT_BINARY_DIR}/${resource_file} "\n")
|
||||
endif()
|
||||
set(line_to_add "")
|
||||
else()
|
||||
set(line_to_add ${line_to_add} ${line})
|
||||
endif()
|
||||
endforeach()
|
||||
message("Moved ${resource_file} to project binary directory")
|
||||
endif()
|
||||
endforeach()
|
||||
message(STATUS "Resource file integrity check complete")
|
||||
|
||||
# Copy navmesh data on first build and extract it
|
||||
if (NOT EXISTS ${PROJECT_BINARY_DIR}/navmeshes/)
|
||||
@@ -346,7 +305,7 @@ file(
|
||||
file(
|
||||
GLOB HEADERS_DGAME
|
||||
LIST_DIRECTORIES false
|
||||
${PROJECT_SOURCE_DIR}/dGame/Entity.h
|
||||
${PROJECT_SOURCE_DIR}/dGame/dEntity/Entity.h
|
||||
${PROJECT_SOURCE_DIR}/dGame/dGameMessages/GameMessages.h
|
||||
${PROJECT_SOURCE_DIR}/dGame/EntityManager.h
|
||||
${PROJECT_SOURCE_DIR}/dScripts/CppScripts.h
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
PROJECT_VERSION_MAJOR=1
|
||||
PROJECT_VERSION_MINOR=0
|
||||
PROJECT_VERSION_PATCH=1
|
||||
PROJECT_VERSION_PATCH=4
|
||||
# LICENSE
|
||||
LICENSE=AGPL-3.0
|
||||
# The network version.
|
||||
# 171023 - Darkflame Universe client
|
||||
# 171022 - Unmodded client
|
||||
NET_VERSION=171022
|
||||
# Debugging
|
||||
# Set __dynamic to 1 to enable the -rdynamic flag for the linker, yielding some symbols in crashlogs.
|
||||
__dynamic=1
|
||||
@@ -13,7 +17,7 @@ __dynamic=1
|
||||
# Set __compile_backtrace__ to 1 to compile the backtrace library instead of using system libraries.
|
||||
# __compile_backtrace__=1
|
||||
# Set to the number of jobs (make -j equivalent) to compile the mariadbconn files with.
|
||||
__maria_db_connector_compile_jobs__=1
|
||||
__maria_db_connector_compile_jobs__=
|
||||
# When set to 1 and uncommented, compiling and linking testing folders and libraries will be done.
|
||||
__enable_testing__=1
|
||||
# The path to OpenSSL. Change this if your OpenSSL install path is different than the default.
|
||||
|
||||
@@ -179,7 +179,7 @@ If you would like to build the server faster, append `-j<number>` where number i
|
||||
### Notes
|
||||
Depending on your operating system, you may need to adjust some pre-processor defines in [CMakeVariables.txt](./CMakeVariables.txt) before building:
|
||||
* If you are on MacOS, ensure OPENSSL_ROOT_DIR is pointing to the openssl root directory.
|
||||
* If you are using a Darkflame Universe client, ensure `client_net_version` in `build/sharedconfig.ini` is changed to 171023.
|
||||
* If you are using a Darkflame Universe client, ensure NET_VERSION is changed to 171023.
|
||||
|
||||
## Configuring your server
|
||||
This server has a few steps that need to be taken to configure the server for your use case.
|
||||
|
||||
@@ -29,7 +29,6 @@ namespace Game {
|
||||
dServer* server = nullptr;
|
||||
dConfig* config = nullptr;
|
||||
bool shouldShutdown = false;
|
||||
std::mt19937 randomEngine;
|
||||
}
|
||||
|
||||
dLogger* SetupLogger();
|
||||
@@ -84,8 +83,6 @@ int main(int argc, char** argv) {
|
||||
delete res;
|
||||
delete stmt;
|
||||
|
||||
Game::randomEngine = std::mt19937(time(0));
|
||||
|
||||
//It's safe to pass 'localhost' here, as the IP is only used as the external IP.
|
||||
uint32_t maxClients = 50;
|
||||
uint32_t ourPort = 1001; //LU client is hardcoded to use this for auth port, so I'm making it the default.
|
||||
|
||||
@@ -33,7 +33,6 @@ namespace Game {
|
||||
dChatFilter* chatFilter = nullptr;
|
||||
AssetManager* assetManager = nullptr;
|
||||
bool shouldShutdown = false;
|
||||
std::mt19937 randomEngine;
|
||||
}
|
||||
|
||||
|
||||
@@ -115,8 +114,6 @@ int main(int argc, char** argv) {
|
||||
Game::server = new dServer(Game::config->GetValue("external_ip"), ourPort, 0, maxClients, false, true, Game::logger, masterIP, masterPort, ServerType::Chat, Game::config, &Game::shouldShutdown);
|
||||
|
||||
Game::chatFilter = new dChatFilter(Game::assetManager->GetResPath().string() + "/chatplus_en_us", bool(std::stoi(Game::config->GetValue("dont_generate_dcf"))));
|
||||
|
||||
Game::randomEngine = std::mt19937(time(0));
|
||||
|
||||
//Run it until server gets a kill message from Master:
|
||||
auto t = std::chrono::high_resolution_clock::now();
|
||||
|
||||
@@ -10,8 +10,6 @@ class dConfig;
|
||||
class RakPeerInterface;
|
||||
class AssetManager;
|
||||
struct SystemAddress;
|
||||
class EntityManager;
|
||||
class dZoneManager;
|
||||
|
||||
namespace Game {
|
||||
extern dLogger* logger;
|
||||
@@ -24,6 +22,4 @@ namespace Game {
|
||||
extern AssetManager* assetManager;
|
||||
extern SystemAddress chatSysAddr;
|
||||
extern bool shouldShutdown;
|
||||
extern EntityManager* entityManager;
|
||||
extern dZoneManager* zoneManager;
|
||||
}
|
||||
|
||||
@@ -111,6 +111,29 @@ namespace GeneralUtils {
|
||||
*/
|
||||
bool CheckBit(int64_t value, uint32_t index);
|
||||
|
||||
// MARK: Random Number Generation
|
||||
|
||||
//! Generates a random number
|
||||
/*!
|
||||
\param min The minimum the generate from
|
||||
\param max The maximum to generate to
|
||||
*/
|
||||
template <typename T>
|
||||
inline T GenerateRandomNumber(std::size_t min, std::size_t max) {
|
||||
// Make sure it is a numeric type
|
||||
static_assert(std::is_arithmetic<T>::value, "Not an arithmetic type");
|
||||
|
||||
if constexpr (std::is_integral_v<T>) { // constexpr only necessary on first statement
|
||||
std::uniform_int_distribution<T> distribution(min, max);
|
||||
return distribution(Game::randomEngine);
|
||||
} else if (std::is_floating_point_v<T>) {
|
||||
std::uniform_real_distribution<T> distribution(min, max);
|
||||
return distribution(Game::randomEngine);
|
||||
}
|
||||
|
||||
return T();
|
||||
}
|
||||
|
||||
bool ReplaceInString(std::string& str, const std::string& from, const std::string& to);
|
||||
|
||||
std::u16string ReadWString(RakNet::BitStream* inStream);
|
||||
@@ -200,42 +223,4 @@ namespace GeneralUtils {
|
||||
std::hash<T> h;
|
||||
s ^= h(v) + 0x9e3779b9 + (s << 6) + (s >> 2);
|
||||
}
|
||||
|
||||
// MARK: Random Number Generation
|
||||
|
||||
//! Generates a random number
|
||||
/*!
|
||||
\param min The minimum the generate from
|
||||
\param max The maximum to generate to
|
||||
*/
|
||||
template <typename T>
|
||||
inline T GenerateRandomNumber(std::size_t min, std::size_t max) {
|
||||
// Make sure it is a numeric type
|
||||
static_assert(std::is_arithmetic<T>::value, "Not an arithmetic type");
|
||||
|
||||
if constexpr (std::is_integral_v<T>) { // constexpr only necessary on first statement
|
||||
std::uniform_int_distribution<T> distribution(min, max);
|
||||
return distribution(Game::randomEngine);
|
||||
} else if (std::is_floating_point_v<T>) {
|
||||
std::uniform_real_distribution<T> distribution(min, max);
|
||||
return distribution(Game::randomEngine);
|
||||
}
|
||||
|
||||
return T();
|
||||
}
|
||||
|
||||
// on Windows we need to undef these or else they conflict with our numeric limits calls
|
||||
// DEVELOPERS DEVELOPERS DEVELOPERS DEVELOPERS DEVELOPERS DEVELOPERS DEVELOPERS DEVELOPERS
|
||||
#ifdef _WIN32
|
||||
#undef min
|
||||
#undef max
|
||||
#endif
|
||||
|
||||
template <typename T>
|
||||
inline T GenerateRandomNumber() {
|
||||
// Make sure it is a numeric type
|
||||
static_assert(std::is_arithmetic<T>::value, "Not an arithmetic type");
|
||||
|
||||
return GenerateRandomNumber<T>(std::numeric_limits<T>::min(), std::numeric_limits<T>::max());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ public:
|
||||
|
||||
virtual void WriteToPacket(RakNet::BitStream* packet) = 0;
|
||||
|
||||
virtual const std::u16string& GetKey() = 0;
|
||||
virtual const std::u16string& GetKey() const = 0;
|
||||
|
||||
virtual eLDFType GetValueType() = 0;
|
||||
|
||||
@@ -117,7 +117,7 @@ public:
|
||||
/*!
|
||||
\return The key
|
||||
*/
|
||||
const std::u16string& GetKey(void) override { return this->key; }
|
||||
const std::u16string& GetKey(void) const override { return this->key; }
|
||||
|
||||
//! Gets the LDF Type
|
||||
/*!
|
||||
|
||||
@@ -129,13 +129,11 @@ NiPoint3 NiPoint3::operator+(const NiPoint3& point) const {
|
||||
}
|
||||
|
||||
//! Operator for addition of vectors
|
||||
NiPoint3& NiPoint3::operator+=(const NiPoint3& point) {
|
||||
this->x += point.x;
|
||||
this->y += point.y;
|
||||
this->z += point.z;
|
||||
return *this;
|
||||
NiPoint3 NiPoint3::operator+=(const NiPoint3& point) const {
|
||||
return NiPoint3(this->x + point.x, this->y + point.y, this->z + point.z);
|
||||
}
|
||||
|
||||
|
||||
//! Operator for subtraction of vectors
|
||||
NiPoint3 NiPoint3::operator-(const NiPoint3& point) const {
|
||||
return NiPoint3(this->x - point.x, this->y - point.y, this->z - point.z);
|
||||
|
||||
@@ -136,7 +136,7 @@ public:
|
||||
NiPoint3 operator+(const NiPoint3& point) const;
|
||||
|
||||
//! Operator for addition of vectors
|
||||
NiPoint3& operator+=(const NiPoint3& point);
|
||||
NiPoint3 operator+=(const NiPoint3& point) const;
|
||||
|
||||
//! Operator for subtraction of vectors
|
||||
NiPoint3 operator-(const NiPoint3& point) const;
|
||||
|
||||
@@ -56,6 +56,13 @@ const uint32_t LWOCLONEID_INVALID = -1; //!< Invalid LWOCLONEID
|
||||
const uint16_t LWOINSTANCEID_INVALID = -1; //!< Invalid LWOINSTANCEID
|
||||
const uint16_t LWOMAPID_INVALID = -1; //!< Invalid LWOMAPID
|
||||
const uint64_t LWOZONEID_INVALID = 0; //!< Invalid LWOZONEID
|
||||
const LOT LOT_ZONE_CONTROL = 2365;
|
||||
const LOT LOT_3D_AMBIENT_SOUND = 6368;
|
||||
const LOT LOT_MODEL_IN_WORLD = 14;
|
||||
const LOT LOT_THINKING_CAP = 6086;
|
||||
const LOT LOT_ROCKET = 6416;
|
||||
const LOT LOT_LEGACY_RESPAWN_POINT = 4945;
|
||||
|
||||
|
||||
const float PI = 3.14159f;
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
14
dCommon/dEnums/ePhysicsBehaviorType.h
Normal file
14
dCommon/dEnums/ePhysicsBehaviorType.h
Normal file
@@ -0,0 +1,14 @@
|
||||
#ifndef __EPHYSICSBEHAVIORTYPE__H__
|
||||
#define __EPHYSICSBEHAVIORTYPE__H__
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
enum class ePhysicsBehaviorType : int32_t {
|
||||
INVALID = -1,
|
||||
GROUND,
|
||||
FLYING,
|
||||
STANDARD,
|
||||
DYNAMIC
|
||||
};
|
||||
|
||||
#endif //!__EPHYSICSBEHAVIORTYPE__H__
|
||||
@@ -11,14 +11,14 @@ enum class eReplicaComponentType : uint32_t {
|
||||
CHARACTER,
|
||||
SCRIPT,
|
||||
BOUNCER,
|
||||
BUFF, // buff is really 98, this is DESTROYABLE
|
||||
DESTROYABLE,
|
||||
GHOST,
|
||||
SKILL,
|
||||
SPAWNER,
|
||||
SPAWN,
|
||||
ITEM,
|
||||
REBUILD,
|
||||
REBUILD_START,
|
||||
REBUILD_ACTIVATOR,
|
||||
MODULAR_BUILD,
|
||||
BUILD_CONTROLLER,
|
||||
BUILD_ACTIVATOR,
|
||||
ICON_ONLY,
|
||||
VENDOR,
|
||||
INVENTORY,
|
||||
@@ -33,8 +33,8 @@ enum class eReplicaComponentType : uint32_t {
|
||||
PET,
|
||||
PLATFORM_BOUNDARY,
|
||||
MODULE,
|
||||
ARCADE,
|
||||
VEHICLE_PHYSICS, // Havok demo based
|
||||
JETPACKPAD,
|
||||
HAVOK_VEHICLE_PHYSICS,
|
||||
MOVEMENT_AI,
|
||||
EXHIBIT,
|
||||
OVERHEAD_ICON,
|
||||
@@ -46,23 +46,23 @@ enum class eReplicaComponentType : uint32_t {
|
||||
SCRIPTED_ACTIVITY,
|
||||
PHANTOM_PHYSICS,
|
||||
SPRINGPAD,
|
||||
MODEL,
|
||||
MODEL_BEHAVIOR,
|
||||
PROPERTY_ENTRANCE,
|
||||
FX,
|
||||
PROPERTY_MANAGEMENT,
|
||||
VEHICLE_PHYSICS_NEW, // internal physics based on havok
|
||||
VEHICLE_PHYSICS,
|
||||
PHYSICS_SYSTEM,
|
||||
QUICK_BUILD,
|
||||
SWITCH,
|
||||
ZONE_CONTROL, // Minigame
|
||||
CHANGLING,
|
||||
MINIGAME_CONTROL,
|
||||
CHANGLING_BUILD,
|
||||
CHOICE_BUILD,
|
||||
PACKAGE,
|
||||
SOUND_REPEATER,
|
||||
SOUND_AMBIENT_2D,
|
||||
SOUND_AMBIENT_3D,
|
||||
PRECONDITION,
|
||||
PLAYER_FLAG,
|
||||
FLAG,
|
||||
CUSTOM_BUILD_ASSEMBLY,
|
||||
BASE_COMBAT_AI,
|
||||
MODULE_ASSEMBLY,
|
||||
@@ -71,8 +71,8 @@ enum class eReplicaComponentType : uint32_t {
|
||||
GENERIC_ACTIVATOR,
|
||||
PROPERTY_VENDOR,
|
||||
HF_LIGHT_DIRECTION_GADGET,
|
||||
ROCKET_LAUNCH,
|
||||
ROCKET_LANDING,
|
||||
ROCKET_LAUNCHPAD_CONTROL,
|
||||
ROCKET_ANIMATION_CONTROL,
|
||||
TRIGGER,
|
||||
DROPPED_LOOT,
|
||||
RACING_CONTROL,
|
||||
@@ -84,7 +84,7 @@ enum class eReplicaComponentType : uint32_t {
|
||||
SOUND_TRIGGER,
|
||||
PROXIMITY_MONITOR,
|
||||
RACING_SOUND_TRIGGER,
|
||||
CHAT,
|
||||
CHAT_BUBBLE,
|
||||
FRIENDS_LIST,
|
||||
GUILD,
|
||||
LOCAL_SYSTEM,
|
||||
@@ -101,12 +101,12 @@ enum class eReplicaComponentType : uint32_t {
|
||||
TRADE,
|
||||
USER_CONTROL,
|
||||
IGNORE_LIST,
|
||||
ROCKET_LAUNCH_LUP,
|
||||
BUFF_REAL, // the real buff component, should just be name BUFF
|
||||
MULTI_ZONE_ENTRANCE,
|
||||
BUFF,
|
||||
INTERACTION_MANAGER,
|
||||
DONATION_VENDOR,
|
||||
COMBAT_MEDIATOR,
|
||||
COMMENDATION_VENDOR,
|
||||
ACHIEVEMENT_VENDOR,
|
||||
GATE_RUSH_CONTROL,
|
||||
RAIL_ACTIVATOR,
|
||||
ROLLER,
|
||||
@@ -114,14 +114,14 @@ enum class eReplicaComponentType : uint32_t {
|
||||
CRAFTING,
|
||||
POSSESSABLE,
|
||||
LEVEL_PROGRESSION,
|
||||
POSSESSOR,
|
||||
POSSESSION,
|
||||
MOUNT_CONTROL,
|
||||
UNKNOWN_112,
|
||||
PROPERTY_PLAQUE,
|
||||
BUILD_BORDER,
|
||||
UNKNOWN_115,
|
||||
CULLING_PLANE,
|
||||
DESTROYABLE = 1000 // Actually 7
|
||||
NUMBER_OF_COMPONENTS
|
||||
};
|
||||
|
||||
#endif //!__EREPLICACOMPONENTTYPE__H__
|
||||
|
||||
13
dCommon/dEnums/eUgcModerationStatus.h
Normal file
13
dCommon/dEnums/eUgcModerationStatus.h
Normal file
@@ -0,0 +1,13 @@
|
||||
|
||||
#ifndef __EUGCMODERATIONSTATUS__H__
|
||||
#define __EUGCMODERATIONSTATUS__H__
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
enum class eUgcModerationStatus : uint32_t {
|
||||
NoStatus,
|
||||
Approved,
|
||||
Rejected,
|
||||
};
|
||||
|
||||
#endif //!__EUGCMODERATIONSTATUS__H__
|
||||
@@ -1,30 +1,26 @@
|
||||
#include "CDBehaviorParameterTable.h"
|
||||
#include "GeneralUtils.h"
|
||||
|
||||
uint64_t GetHash(const uint32_t behaviorID, const uint32_t parameterID) {
|
||||
uint64_t hash = behaviorID;
|
||||
hash <<= 31U;
|
||||
hash |= parameterID;
|
||||
|
||||
return hash;
|
||||
}
|
||||
|
||||
CDBehaviorParameterTable::CDBehaviorParameterTable() {
|
||||
CDBehaviorParameterTable::CDBehaviorParameterTable(void) {
|
||||
auto tableData = CDClientDatabase::ExecuteQuery("SELECT * FROM BehaviorParameter");
|
||||
uint32_t uniqueParameterId = 0;
|
||||
uint64_t hash = 0;
|
||||
while (!tableData.eof()) {
|
||||
uint32_t behaviorID = tableData.getIntField("behaviorID", -1);
|
||||
CDBehaviorParameter entry;
|
||||
entry.behaviorID = tableData.getIntField("behaviorID", -1);
|
||||
auto candidateStringToAdd = std::string(tableData.getStringField("parameterID", ""));
|
||||
auto parameter = m_ParametersList.find(candidateStringToAdd);
|
||||
uint32_t parameterId;
|
||||
if (parameter != m_ParametersList.end()) {
|
||||
parameterId = parameter->second;
|
||||
entry.parameterID = parameter;
|
||||
} else {
|
||||
parameterId = m_ParametersList.insert(std::make_pair(candidateStringToAdd, m_ParametersList.size())).first->second;
|
||||
entry.parameterID = m_ParametersList.insert(std::make_pair(candidateStringToAdd, uniqueParameterId)).first;
|
||||
uniqueParameterId++;
|
||||
}
|
||||
uint64_t hash = GetHash(behaviorID, parameterId);
|
||||
float value = tableData.getFloatField("value", -1.0f);
|
||||
hash = entry.behaviorID;
|
||||
hash = (hash << 31U) | entry.parameterID->second;
|
||||
entry.value = tableData.getFloatField("value", -1.0f);
|
||||
|
||||
m_Entries.insert(std::make_pair(hash, value));
|
||||
m_Entries.insert(std::make_pair(hash, entry));
|
||||
|
||||
tableData.nextRow();
|
||||
}
|
||||
@@ -34,22 +30,27 @@ CDBehaviorParameterTable::CDBehaviorParameterTable() {
|
||||
float CDBehaviorParameterTable::GetValue(const uint32_t behaviorID, const std::string& name, const float defaultValue) {
|
||||
auto parameterID = this->m_ParametersList.find(name);
|
||||
if (parameterID == this->m_ParametersList.end()) return defaultValue;
|
||||
auto hash = GetHash(behaviorID, parameterID->second);
|
||||
|
||||
uint64_t hash = behaviorID;
|
||||
|
||||
hash = (hash << 31U) | parameterID->second;
|
||||
|
||||
// Search for specific parameter
|
||||
auto it = m_Entries.find(hash);
|
||||
return it != m_Entries.end() ? it->second : defaultValue;
|
||||
const auto& it = m_Entries.find(hash);
|
||||
return it != m_Entries.end() ? it->second.value : defaultValue;
|
||||
}
|
||||
|
||||
std::map<std::string, float> CDBehaviorParameterTable::GetParametersByBehaviorID(uint32_t behaviorID) {
|
||||
uint64_t hashBase = behaviorID;
|
||||
std::map<std::string, float> returnInfo;
|
||||
for (auto& [parameterString, parameterId] : m_ParametersList) {
|
||||
uint64_t hash = GetHash(hashBase, parameterId);
|
||||
uint64_t hash;
|
||||
for (auto& parameterCandidate : m_ParametersList) {
|
||||
hash = (hashBase << 31U) | parameterCandidate.second;
|
||||
auto infoCandidate = m_Entries.find(hash);
|
||||
if (infoCandidate != m_Entries.end()) {
|
||||
returnInfo.insert(std::make_pair(parameterString, infoCandidate->second));
|
||||
returnInfo.insert(std::make_pair(infoCandidate->second.parameterID->first, infoCandidate->second.value));
|
||||
}
|
||||
}
|
||||
return returnInfo;
|
||||
}
|
||||
|
||||
|
||||
@@ -5,15 +5,18 @@
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
|
||||
struct CDBehaviorParameter {
|
||||
unsigned int behaviorID; //!< The Behavior ID
|
||||
std::unordered_map<std::string, uint32_t>::iterator parameterID; //!< The Parameter ID
|
||||
float value; //!< The value of the behavior template
|
||||
};
|
||||
|
||||
class CDBehaviorParameterTable : public CDTable<CDBehaviorParameterTable> {
|
||||
private:
|
||||
typedef uint64_t BehaviorParameterHash;
|
||||
typedef float BehaviorParameterValue;
|
||||
std::unordered_map<BehaviorParameterHash, BehaviorParameterValue> m_Entries;
|
||||
std::unordered_map<uint64_t, CDBehaviorParameter> m_Entries;
|
||||
std::unordered_map<std::string, uint32_t> m_ParametersList;
|
||||
public:
|
||||
CDBehaviorParameterTable();
|
||||
|
||||
float GetValue(const uint32_t behaviorID, const std::string& name, const float defaultValue = 0);
|
||||
|
||||
std::map<std::string, float> GetParametersByBehaviorID(uint32_t behaviorID);
|
||||
|
||||
@@ -90,4 +90,11 @@ int32_t CDComponentsRegistryTable::GetByIDAndType(uint32_t id, eReplicaComponent
|
||||
return defaultValue;
|
||||
#endif
|
||||
}
|
||||
|
||||
std::vector<std::pair<eReplicaComponentType, uint32_t>> CDComponentsRegistryTable::GetTemplateComponents(LOT templateId) {
|
||||
std::vector<std::pair<eReplicaComponentType, uint32_t>> components;
|
||||
for (int8_t i = 0; static_cast<eReplicaComponentType>(i) < eReplicaComponentType::NUMBER_OF_COMPONENTS; i++) {
|
||||
auto compId = GetByIDAndType(templateId, static_cast<eReplicaComponentType>(i), -1);
|
||||
if (compId != -1) components.push_back(std::make_pair(static_cast<eReplicaComponentType>(i), compId));
|
||||
}
|
||||
return components;
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
// Custom Classes
|
||||
#include "CDTable.h"
|
||||
#include "dCommonVars.h"
|
||||
|
||||
enum class eReplicaComponentType : uint32_t;
|
||||
struct CDComponentsRegistry {
|
||||
@@ -18,4 +19,5 @@ private:
|
||||
public:
|
||||
CDComponentsRegistryTable();
|
||||
int32_t GetByIDAndType(uint32_t id, eReplicaComponentType componentType, int32_t defaultValue = 0);
|
||||
std::vector<std::pair<eReplicaComponentType, uint32_t>> GetTemplateComponents(LOT templateId);
|
||||
};
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
#include "CDItemComponentTable.h"
|
||||
#include "GeneralUtils.h"
|
||||
|
||||
#include "eItemType.h"
|
||||
|
||||
CDItemComponent CDItemComponentTable::Default = {};
|
||||
|
||||
//! Constructor
|
||||
@@ -74,8 +76,8 @@ CDItemComponentTable::CDItemComponentTable(void) {
|
||||
#endif
|
||||
}
|
||||
|
||||
const CDItemComponent& CDItemComponentTable::GetItemComponentByID(unsigned int skillID) {
|
||||
const auto& it = this->entries.find(skillID);
|
||||
const CDItemComponent& CDItemComponentTable::GetItemComponentByID(unsigned int id) {
|
||||
const auto& it = this->entries.find(id);
|
||||
if (it != this->entries.end()) {
|
||||
return it->second;
|
||||
}
|
||||
@@ -83,11 +85,11 @@ const CDItemComponent& CDItemComponentTable::GetItemComponentByID(unsigned int s
|
||||
#ifndef CDCLIENT_CACHE_ALL
|
||||
std::stringstream query;
|
||||
|
||||
query << "SELECT * FROM ItemComponent WHERE id = " << std::to_string(skillID);
|
||||
query << "SELECT * FROM ItemComponent WHERE id = " << std::to_string(id);
|
||||
|
||||
auto tableData = CDClientDatabase::ExecuteQuery(query.str());
|
||||
if (tableData.eof()) {
|
||||
entries.insert(std::make_pair(skillID, Default));
|
||||
entries.insert(std::make_pair(id, Default));
|
||||
return Default;
|
||||
}
|
||||
|
||||
@@ -98,7 +100,7 @@ const CDItemComponent& CDItemComponentTable::GetItemComponentByID(unsigned int s
|
||||
entry.baseValue = tableData.getIntField("baseValue", -1);
|
||||
entry.isKitPiece = tableData.getIntField("isKitPiece", -1) == 1 ? true : false;
|
||||
entry.rarity = tableData.getIntField("rarity", 0);
|
||||
entry.itemType = tableData.getIntField("itemType", -1);
|
||||
entry.itemType = static_cast<eItemType>(tableData.getIntField("itemType", -1));
|
||||
entry.itemInfo = tableData.getInt64Field("itemInfo", -1);
|
||||
entry.inLootTable = tableData.getIntField("inLootTable", -1) == 1 ? true : false;
|
||||
entry.inVendor = tableData.getIntField("inVendor", -1) == 1 ? true : false;
|
||||
@@ -140,7 +142,7 @@ const CDItemComponent& CDItemComponentTable::GetItemComponentByID(unsigned int s
|
||||
tableData.nextRow();
|
||||
}
|
||||
|
||||
const auto& it2 = this->entries.find(skillID);
|
||||
const auto& it2 = this->entries.find(id);
|
||||
if (it2 != this->entries.end()) {
|
||||
return it2->second;
|
||||
}
|
||||
|
||||
@@ -4,13 +4,15 @@
|
||||
#include "CDTable.h"
|
||||
#include "dCommonVars.h"
|
||||
|
||||
enum class eItemType : int32_t;
|
||||
|
||||
struct CDItemComponent {
|
||||
unsigned int id; //!< The Component ID
|
||||
std::string equipLocation; //!< The equip location
|
||||
unsigned int baseValue; //!< The monetary base value of the item
|
||||
bool isKitPiece; //!< Whether or not the item belongs to a kit
|
||||
unsigned int rarity; //!< The rarity of the item
|
||||
unsigned int itemType; //!< The item type
|
||||
eItemType itemType; //!< The item type
|
||||
int64_t itemInfo; //!< The item info
|
||||
bool inLootTable; //!< Whether or not the item is in a loot table
|
||||
bool inVendor; //!< Whether or not the item is in a vendor inventory
|
||||
@@ -58,7 +60,7 @@ public:
|
||||
static std::map<LOT, uint32_t> ParseCraftingCurrencies(const CDItemComponent& itemComponent);
|
||||
|
||||
// Gets an entry by ID
|
||||
const CDItemComponent& GetItemComponentByID(unsigned int skillID);
|
||||
const CDItemComponent& GetItemComponentByID(unsigned int id);
|
||||
|
||||
static CDItemComponent Default;
|
||||
};
|
||||
|
||||
@@ -37,10 +37,7 @@ CDPhysicsComponentTable::~CDPhysicsComponentTable() {
|
||||
}
|
||||
|
||||
CDPhysicsComponent* CDPhysicsComponentTable::GetByID(unsigned int componentID) {
|
||||
for (auto e : m_entries) {
|
||||
if (e.first == componentID) return e.second;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
auto itr = m_entries.find(componentID);
|
||||
return itr != m_entries.end() ? itr->second : nullptr;
|
||||
}
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ CDVendorComponentTable::CDVendorComponentTable(void) {
|
||||
while (!tableData.eof()) {
|
||||
CDVendorComponent entry;
|
||||
entry.id = tableData.getIntField("id", -1);
|
||||
entry.buyScalar = tableData.getFloatField("buyScalar", 0.0f);
|
||||
entry.buyScalar = tableData.getFloatField("buyScalar", -1.0f);
|
||||
entry.sellScalar = tableData.getFloatField("sellScalar", -1.0f);
|
||||
entry.refreshTimeSeconds = tableData.getFloatField("refreshTimeSeconds", -1.0f);
|
||||
entry.LootMatrixIndex = tableData.getIntField("LootMatrixIndex", -1);
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
set(DGAME_SOURCES "Character.cpp"
|
||||
"Entity.cpp"
|
||||
"EntityManager.cpp"
|
||||
"LeaderboardManager.cpp"
|
||||
"Player.cpp"
|
||||
|
||||
@@ -241,7 +241,7 @@ void Character::DoQuickXMLDataParse() {
|
||||
//To try and fix the AG landing into:
|
||||
if (m_ZoneID == 1000 && Game::server->GetZoneID() == 1100) {
|
||||
//sneakily insert our position:
|
||||
auto pos = Game::zoneManager->GetZone()->GetSpawnPos();
|
||||
auto pos = dZoneManager::Instance()->GetZone()->GetSpawnPos();
|
||||
character->SetAttribute("lzx", pos.x);
|
||||
character->SetAttribute("lzy", pos.y);
|
||||
character->SetAttribute("lzz", pos.z);
|
||||
@@ -290,13 +290,13 @@ void Character::DoQuickXMLDataParse() {
|
||||
|
||||
void Character::UnlockEmote(int emoteID) {
|
||||
m_UnlockedEmotes.push_back(emoteID);
|
||||
GameMessages::SendSetEmoteLockState(Game::entityManager->GetEntity(m_ObjectID), false, emoteID);
|
||||
GameMessages::SendSetEmoteLockState(EntityManager::Instance()->GetEntity(m_ObjectID), false, emoteID);
|
||||
}
|
||||
|
||||
void Character::SetBuildMode(bool buildMode) {
|
||||
m_BuildMode = buildMode;
|
||||
|
||||
auto* controller = Game::zoneManager->GetZoneControlObject();
|
||||
auto* controller = dZoneManager::Instance()->GetZoneControlObject();
|
||||
|
||||
controller->OnFireEventServerSide(m_OurEntity, buildMode ? "OnBuildModeEnter" : "OnBuildModeLeave");
|
||||
}
|
||||
@@ -312,7 +312,7 @@ void Character::SaveXMLToDatabase() {
|
||||
character->SetAttribute("gm", static_cast<uint32_t>(m_GMLevel));
|
||||
character->SetAttribute("cc", m_Coins);
|
||||
|
||||
auto zoneInfo = Game::zoneManager->GetZone()->GetZoneID();
|
||||
auto zoneInfo = dZoneManager::Instance()->GetZone()->GetZoneID();
|
||||
// lzid garbage, binary concat of zoneID, zoneInstance and zoneClone
|
||||
if (zoneInfo.GetMapID() != 0 && zoneInfo.GetCloneID() == 0) {
|
||||
uint64_t lzidConcat = zoneInfo.GetCloneID();
|
||||
@@ -424,7 +424,7 @@ void Character::SetPlayerFlag(const uint32_t flagId, const bool value) {
|
||||
|
||||
if (value) {
|
||||
// Update the mission component:
|
||||
auto* player = Game::entityManager->GetEntity(m_ObjectID);
|
||||
auto* player = EntityManager::Instance()->GetEntity(m_ObjectID);
|
||||
|
||||
if (player != nullptr) {
|
||||
auto* missionComponent = player->GetComponent<MissionComponent>();
|
||||
@@ -602,7 +602,7 @@ void Character::SetCoins(int64_t newCoins, eLootSourceType lootSource) {
|
||||
|
||||
m_Coins = newCoins;
|
||||
|
||||
GameMessages::SendSetCurrency(Game::entityManager->GetEntity(m_ObjectID), m_Coins, 0, 0, 0, 0, true, lootSource);
|
||||
GameMessages::SendSetCurrency(EntityManager::Instance()->GetEntity(m_ObjectID), m_Coins, 0, 0, 0, 0, true, lootSource);
|
||||
}
|
||||
|
||||
bool Character::HasBeenToWorld(LWOMAPID mapID) const {
|
||||
|
||||
2074
dGame/Entity.cpp
2074
dGame/Entity.cpp
File diff suppressed because it is too large
Load Diff
504
dGame/Entity.h
504
dGame/Entity.h
@@ -1,504 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <map>
|
||||
#include <functional>
|
||||
#include <typeinfo>
|
||||
#include <type_traits>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include "NiPoint3.h"
|
||||
#include "NiQuaternion.h"
|
||||
#include "LDFFormat.h"
|
||||
#include "eKillType.h"
|
||||
|
||||
namespace Loot {
|
||||
class Info;
|
||||
};
|
||||
|
||||
namespace tinyxml2 {
|
||||
class XMLDocument;
|
||||
};
|
||||
|
||||
class Player;
|
||||
class EntityInfo;
|
||||
class User;
|
||||
class Spawner;
|
||||
class ScriptComponent;
|
||||
class dpEntity;
|
||||
class EntityTimer;
|
||||
class Component;
|
||||
class Item;
|
||||
class Character;
|
||||
class EntityCallbackTimer;
|
||||
enum class eTriggerEventType;
|
||||
enum class eGameMasterLevel : uint8_t;
|
||||
enum class eReplicaComponentType : uint32_t;
|
||||
enum class eReplicaPacketType : uint8_t;
|
||||
enum class eCinematicEvent : uint32_t;
|
||||
|
||||
namespace CppScripts {
|
||||
class Script;
|
||||
};
|
||||
|
||||
/**
|
||||
* An entity in the world. Has multiple components.
|
||||
*/
|
||||
class Entity {
|
||||
public:
|
||||
explicit Entity(const LWOOBJID& objectID, EntityInfo info, Entity* parentEntity = nullptr);
|
||||
virtual ~Entity();
|
||||
|
||||
virtual void Initialize();
|
||||
|
||||
bool operator==(const Entity& other) const;
|
||||
bool operator!=(const Entity& other) const;
|
||||
|
||||
/**
|
||||
* Getters
|
||||
*/
|
||||
|
||||
const LWOOBJID& GetObjectID() const { return m_ObjectID; }
|
||||
|
||||
const LOT GetLOT() const { return m_TemplateID; }
|
||||
|
||||
Character* GetCharacter() const { return m_Character; }
|
||||
|
||||
eGameMasterLevel GetGMLevel() const { return m_GMLevel; }
|
||||
|
||||
uint8_t GetCollectibleID() const { return uint8_t(m_CollectibleID); }
|
||||
|
||||
Entity* GetParentEntity() const { return m_ParentEntity; }
|
||||
|
||||
std::vector<std::string>& GetGroups() { return m_Groups; };
|
||||
|
||||
Spawner* GetSpawner() const { return m_Spawner; }
|
||||
|
||||
LWOOBJID GetSpawnerID() const { return m_SpawnerID; }
|
||||
|
||||
const std::vector<LDFBaseData*>& GetSettings() const { return m_Settings; }
|
||||
|
||||
const std::vector<LDFBaseData*>& GetNetworkSettings() const { return m_NetworkSettings; }
|
||||
|
||||
bool GetIsDead() const;
|
||||
|
||||
bool GetPlayerReadyForUpdates() const { return m_PlayerIsReadyForUpdates; }
|
||||
|
||||
bool GetIsGhostingCandidate() const;
|
||||
void SetIsGhostingCandidate(bool value) { m_IsGhostingCandidate = value; };
|
||||
|
||||
int8_t GetObservers() const;
|
||||
|
||||
uint16_t GetNetworkId() const;
|
||||
|
||||
Entity* GetOwner() const;
|
||||
|
||||
const NiPoint3& GetDefaultPosition() const;
|
||||
|
||||
const NiQuaternion& GetDefaultRotation() const;
|
||||
|
||||
float GetDefaultScale() const;
|
||||
|
||||
const NiPoint3& GetPosition() const;
|
||||
|
||||
const NiQuaternion& GetRotation() const;
|
||||
|
||||
virtual User* GetParentUser() const;
|
||||
|
||||
virtual SystemAddress GetSystemAddress() const { return UNASSIGNED_SYSTEM_ADDRESS; };
|
||||
|
||||
/**
|
||||
* Setters
|
||||
*/
|
||||
|
||||
void SetCharacter(Character* value) { m_Character = value; }
|
||||
|
||||
void SetGMLevel(eGameMasterLevel value);
|
||||
|
||||
void SetOwnerOverride(LWOOBJID value);
|
||||
|
||||
void SetPlayerReadyForUpdates() { m_PlayerIsReadyForUpdates = true; }
|
||||
|
||||
void SetObservers(int8_t value);
|
||||
|
||||
void SetNetworkId(uint16_t id);
|
||||
|
||||
void SetPosition(NiPoint3 position);
|
||||
|
||||
void SetRotation(NiQuaternion rotation);
|
||||
|
||||
virtual void SetRespawnPos(NiPoint3 position) {}
|
||||
|
||||
virtual void SetRespawnRot(NiQuaternion rotation) {}
|
||||
|
||||
virtual void SetSystemAddress(const SystemAddress& value) {};
|
||||
|
||||
/**
|
||||
* Component management
|
||||
*/
|
||||
|
||||
Component* GetComponent(eReplicaComponentType componentID) const;
|
||||
|
||||
template<typename T>
|
||||
T* GetComponent() const;
|
||||
|
||||
template<typename T>
|
||||
bool TryGetComponent(eReplicaComponentType componentId, T*& component) const;
|
||||
|
||||
bool HasComponent(eReplicaComponentType componentId) const;
|
||||
|
||||
void AddComponent(eReplicaComponentType componentId, Component* component);
|
||||
|
||||
std::vector<ScriptComponent*> GetScriptComponents();
|
||||
|
||||
void Subscribe(LWOOBJID scriptObjId, CppScripts::Script* scriptToAdd, const std::string& notificationName);
|
||||
void Unsubscribe(LWOOBJID scriptObjId, const std::string& notificationName);
|
||||
|
||||
void SetProximityRadius(float proxRadius, std::string name);
|
||||
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);
|
||||
void CancelCallbackTimers();
|
||||
void CancelAllTimers();
|
||||
void CancelTimer(const std::string& name);
|
||||
|
||||
void AddToGroup(const std::string& group);
|
||||
bool IsPlayer() const;
|
||||
|
||||
std::unordered_map<eReplicaComponentType, Component*>& GetComponents() { return m_Components; } // TODO: Remove
|
||||
|
||||
void WriteBaseReplicaData(RakNet::BitStream* outBitStream, eReplicaPacketType packetType);
|
||||
void WriteComponents(RakNet::BitStream* outBitStream, eReplicaPacketType packetType);
|
||||
void ResetFlags();
|
||||
void UpdateXMLDoc(tinyxml2::XMLDocument* doc);
|
||||
void Update(float deltaTime);
|
||||
|
||||
// Events
|
||||
void OnCollisionProximity(LWOOBJID otherEntity, const std::string& proxName, const std::string& status);
|
||||
void OnCollisionPhantom(LWOOBJID otherEntity);
|
||||
void OnCollisionLeavePhantom(LWOOBJID otherEntity);
|
||||
|
||||
void OnFireEventServerSide(Entity* sender, std::string args, int32_t param1 = -1, int32_t param2 = -1, int32_t param3 = -1);
|
||||
void OnActivityStateChangeRequest(const LWOOBJID senderID, const int32_t value1, const int32_t value2,
|
||||
const std::u16string& stringValue);
|
||||
void OnCinematicUpdate(Entity* self, Entity* sender, eCinematicEvent event, const std::u16string& pathName,
|
||||
float_t pathTime, float_t totalTime, int32_t waypoint);
|
||||
|
||||
void NotifyObject(Entity* sender, const std::string& name, int32_t param1 = 0, int32_t param2 = 0);
|
||||
void OnEmoteReceived(int32_t emote, Entity* target);
|
||||
|
||||
void OnUse(Entity* originator);
|
||||
|
||||
void OnHitOrHealResult(Entity* attacker, int32_t damage);
|
||||
void OnHit(Entity* attacker);
|
||||
|
||||
void OnZonePropertyEditBegin();
|
||||
void OnZonePropertyEditEnd();
|
||||
void OnZonePropertyModelEquipped();
|
||||
void OnZonePropertyModelPlaced(Entity* player);
|
||||
void OnZonePropertyModelPickedUp(Entity* player);
|
||||
void OnZonePropertyModelRemoved(Entity* player);
|
||||
void OnZonePropertyModelRemovedWhileEquipped(Entity* player);
|
||||
void OnZonePropertyModelRotated(Entity* player);
|
||||
|
||||
void OnMessageBoxResponse(Entity* sender, int32_t button, const std::u16string& identifier, const std::u16string& userData);
|
||||
void OnChoiceBoxResponse(Entity* sender, int32_t button, const std::u16string& buttonIdentifier, const std::u16string& identifier);
|
||||
void RequestActivityExit(Entity* sender, LWOOBJID player, bool canceled);
|
||||
|
||||
void Smash(const LWOOBJID source = LWOOBJID_EMPTY, const eKillType killType = eKillType::VIOLENT, const std::u16string& deathType = u"");
|
||||
void Kill(Entity* murderer = nullptr);
|
||||
void AddRebuildCompleteCallback(const std::function<void(Entity* user)>& callback) const;
|
||||
void AddCollisionPhantomCallback(const std::function<void(Entity* target)>& callback);
|
||||
void AddDieCallback(const std::function<void()>& callback);
|
||||
void Resurrect();
|
||||
|
||||
void AddLootItem(const Loot::Info& info);
|
||||
void PickupItem(const LWOOBJID& objectID);
|
||||
|
||||
bool CanPickupCoins(uint64_t count);
|
||||
void RegisterCoinDrop(uint64_t count);
|
||||
|
||||
void ScheduleKillAfterUpdate(Entity* murderer = nullptr);
|
||||
void TriggerEvent(eTriggerEventType event, Entity* optionalTarget = nullptr);
|
||||
void ScheduleDestructionAfterUpdate() { m_ShouldDestroyAfterUpdate = true; }
|
||||
|
||||
virtual NiPoint3 GetRespawnPosition() const { return NiPoint3::ZERO; }
|
||||
virtual NiQuaternion GetRespawnRotation() const { return NiQuaternion::IDENTITY; }
|
||||
|
||||
void Sleep();
|
||||
void Wake();
|
||||
bool IsSleeping() const;
|
||||
|
||||
/*
|
||||
* Utility
|
||||
*/
|
||||
/**
|
||||
* Retroactively corrects the model vault size due to incorrect initialization in a previous patch.
|
||||
*
|
||||
*/
|
||||
void RetroactiveVaultSize();
|
||||
bool GetBoolean(const std::u16string& name) const;
|
||||
int32_t GetI32(const std::u16string& name) const;
|
||||
int64_t GetI64(const std::u16string& name) const;
|
||||
|
||||
void SetBoolean(const std::u16string& name, bool value);
|
||||
void SetI32(const std::u16string& name, int32_t value);
|
||||
void SetI64(const std::u16string& name, int64_t value);
|
||||
|
||||
bool HasVar(const std::u16string& name) const;
|
||||
|
||||
template<typename T>
|
||||
const T& GetVar(const std::u16string& name) const;
|
||||
|
||||
template<typename T>
|
||||
void SetVar(const std::u16string& name, T value);
|
||||
|
||||
void SendNetworkVar(const std::string& data, const SystemAddress& sysAddr);
|
||||
|
||||
template<typename T>
|
||||
void SetNetworkVar(const std::u16string& name, T value, const SystemAddress& sysAddr = UNASSIGNED_SYSTEM_ADDRESS);
|
||||
|
||||
template<typename T>
|
||||
void SetNetworkVar(const std::u16string& name, std::vector<T> value, const SystemAddress& sysAddr = UNASSIGNED_SYSTEM_ADDRESS);
|
||||
|
||||
template<typename T>
|
||||
T GetNetworkVar(const std::u16string& name);
|
||||
|
||||
/**
|
||||
* Get the LDF value and cast it as T.
|
||||
*/
|
||||
template<typename T>
|
||||
T GetVarAs(const std::u16string& name) const;
|
||||
|
||||
/**
|
||||
* Get the LDF data.
|
||||
*/
|
||||
LDFBaseData* GetVarData(const std::u16string& name) const;
|
||||
|
||||
/**
|
||||
* Get the LDF value and convert it to a string.
|
||||
*/
|
||||
std::string GetVarAsString(const std::u16string& name) const;
|
||||
|
||||
/*
|
||||
* Collision
|
||||
*/
|
||||
std::vector<LWOOBJID>& GetTargetsInPhantom();
|
||||
|
||||
Entity* GetScheduledKiller() { return m_ScheduleKiller; }
|
||||
|
||||
protected:
|
||||
LWOOBJID m_ObjectID;
|
||||
|
||||
LOT m_TemplateID;
|
||||
|
||||
std::vector<LDFBaseData*> m_Settings;
|
||||
std::vector<LDFBaseData*> m_NetworkSettings;
|
||||
|
||||
NiPoint3 m_DefaultPosition;
|
||||
NiQuaternion m_DefaultRotation;
|
||||
float m_Scale;
|
||||
|
||||
Spawner* m_Spawner;
|
||||
LWOOBJID m_SpawnerID;
|
||||
|
||||
bool m_HasSpawnerNodeID;
|
||||
uint32_t m_SpawnerNodeID;
|
||||
|
||||
Character* m_Character;
|
||||
|
||||
Entity* m_ParentEntity; //For spawners and the like
|
||||
std::vector<Entity*> m_ChildEntities;
|
||||
eGameMasterLevel m_GMLevel;
|
||||
uint16_t m_CollectibleID;
|
||||
std::vector<std::string> m_Groups;
|
||||
uint16_t m_NetworkID;
|
||||
std::vector<std::function<void()>> m_DieCallbacks;
|
||||
std::vector<std::function<void(Entity* target)>> m_PhantomCollisionCallbacks;
|
||||
|
||||
std::unordered_map<eReplicaComponentType, Component*> m_Components;
|
||||
std::vector<EntityTimer*> m_Timers;
|
||||
std::vector<EntityTimer*> m_PendingTimers;
|
||||
std::vector<EntityCallbackTimer*> m_CallbackTimers;
|
||||
|
||||
bool m_ShouldDestroyAfterUpdate = false;
|
||||
|
||||
LWOOBJID m_OwnerOverride;
|
||||
|
||||
Entity* m_ScheduleKiller;
|
||||
|
||||
bool m_PlayerIsReadyForUpdates = false;
|
||||
|
||||
bool m_IsGhostingCandidate = false;
|
||||
|
||||
int8_t m_Observers = 0;
|
||||
|
||||
bool m_IsParentChildDirty = true;
|
||||
|
||||
/*
|
||||
* Collision
|
||||
*/
|
||||
std::vector<LWOOBJID> m_TargetsInPhantom;
|
||||
};
|
||||
|
||||
/**
|
||||
* Template definitions.
|
||||
*/
|
||||
|
||||
template<typename T>
|
||||
bool Entity::TryGetComponent(const eReplicaComponentType componentId, T*& component) const {
|
||||
const auto& index = m_Components.find(componentId);
|
||||
|
||||
if (index == m_Components.end()) {
|
||||
component = nullptr;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
component = dynamic_cast<T*>(index->second);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T* Entity::GetComponent() const {
|
||||
return dynamic_cast<T*>(GetComponent(T::ComponentType));
|
||||
}
|
||||
|
||||
|
||||
template<typename T>
|
||||
const T& Entity::GetVar(const std::u16string& name) const {
|
||||
auto* data = GetVarData(name);
|
||||
|
||||
if (data == nullptr) {
|
||||
return LDFData<T>::Default;
|
||||
}
|
||||
|
||||
auto* typed = dynamic_cast<LDFData<T>*>(data);
|
||||
|
||||
if (typed == nullptr) {
|
||||
return LDFData<T>::Default;
|
||||
}
|
||||
|
||||
return typed->GetValue();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T Entity::GetVarAs(const std::u16string& name) const {
|
||||
const auto data = GetVarAsString(name);
|
||||
|
||||
T value;
|
||||
|
||||
if (!GeneralUtils::TryParse(data, value)) {
|
||||
return LDFData<T>::Default;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void Entity::SetVar(const std::u16string& name, T value) {
|
||||
auto* data = GetVarData(name);
|
||||
|
||||
if (data == nullptr) {
|
||||
auto* data = new LDFData<T>(name, value);
|
||||
|
||||
m_Settings.push_back(data);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
auto* typed = dynamic_cast<LDFData<T>*>(data);
|
||||
|
||||
if (typed == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
typed->SetValue(value);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void Entity::SetNetworkVar(const std::u16string& name, T value, const SystemAddress& sysAddr) {
|
||||
LDFData<T>* newData = nullptr;
|
||||
|
||||
for (auto* data : m_NetworkSettings) {
|
||||
if (data->GetKey() != name)
|
||||
continue;
|
||||
|
||||
newData = dynamic_cast<LDFData<T>*>(data);
|
||||
if (newData != nullptr) {
|
||||
newData->SetValue(value);
|
||||
} else { // If we're changing types
|
||||
m_NetworkSettings.erase(
|
||||
std::remove(m_NetworkSettings.begin(), m_NetworkSettings.end(), data), m_NetworkSettings.end()
|
||||
);
|
||||
delete data;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (newData == nullptr) {
|
||||
newData = new LDFData<T>(name, value);
|
||||
}
|
||||
|
||||
m_NetworkSettings.push_back(newData);
|
||||
SendNetworkVar(newData->GetString(true), sysAddr);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void Entity::SetNetworkVar(const std::u16string& name, std::vector<T> values, const SystemAddress& sysAddr) {
|
||||
std::stringstream updates;
|
||||
auto index = 1;
|
||||
|
||||
for (const auto& value : values) {
|
||||
LDFData<T>* newData = nullptr;
|
||||
const auto& indexedName = name + u"." + GeneralUtils::to_u16string(index);
|
||||
|
||||
for (auto* data : m_NetworkSettings) {
|
||||
if (data->GetKey() != indexedName)
|
||||
continue;
|
||||
|
||||
newData = dynamic_cast<LDFData<T>*>(data);
|
||||
newData->SetValue(value);
|
||||
break;
|
||||
}
|
||||
|
||||
if (newData == nullptr) {
|
||||
newData = new LDFData<T>(indexedName, value);
|
||||
}
|
||||
|
||||
m_NetworkSettings.push_back(newData);
|
||||
|
||||
if (index == values.size()) {
|
||||
updates << newData->GetString(true);
|
||||
} else {
|
||||
updates << newData->GetString(true) << "\n";
|
||||
}
|
||||
|
||||
index++;
|
||||
}
|
||||
|
||||
SendNetworkVar(updates.str(), sysAddr);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T Entity::GetNetworkVar(const std::u16string& name) {
|
||||
for (auto* data : m_NetworkSettings) {
|
||||
if (data == nullptr || data->GetKey() != name)
|
||||
continue;
|
||||
|
||||
auto* typed = dynamic_cast<LDFData<T>*>(data);
|
||||
if (typed == nullptr)
|
||||
continue;
|
||||
|
||||
return typed->GetValue();
|
||||
}
|
||||
|
||||
return LDFData<T>::Default;
|
||||
}
|
||||
@@ -24,6 +24,9 @@
|
||||
#include "eGameMasterLevel.h"
|
||||
#include "eReplicaComponentType.h"
|
||||
#include "eReplicaPacketType.h"
|
||||
#include "CollectibleComponent.h"
|
||||
|
||||
EntityManager* EntityManager::m_Address = nullptr;
|
||||
|
||||
// Configure which zones have ghosting disabled, mostly small worlds.
|
||||
std::vector<LWOMAPID> EntityManager::m_GhostingExcludedZones = {
|
||||
@@ -60,7 +63,7 @@ void EntityManager::Initialize() {
|
||||
m_GhostingEnabled = std::find(
|
||||
m_GhostingExcludedZones.begin(),
|
||||
m_GhostingExcludedZones.end(),
|
||||
Game::zoneManager->GetZoneID().GetMapID()
|
||||
dZoneManager::Instance()->GetZoneID().GetMapID()
|
||||
) == m_GhostingExcludedZones.end();
|
||||
|
||||
// grab hardcore mode settings and load them with sane defaults
|
||||
@@ -75,7 +78,10 @@ void EntityManager::Initialize() {
|
||||
|
||||
// If cloneID is not zero, then hardcore mode is disabled
|
||||
// aka minigames and props
|
||||
if (Game::zoneManager->GetZoneID().GetCloneID() != 0) m_HardcoreMode = false;
|
||||
if (dZoneManager::Instance()->GetZoneID().GetCloneID() != 0) m_HardcoreMode = false;
|
||||
}
|
||||
|
||||
EntityManager::~EntityManager() {
|
||||
}
|
||||
|
||||
Entity* EntityManager::CreateEntity(EntityInfo info, User* user, Entity* parentEntity, const bool controller, const LWOOBJID explicitId) {
|
||||
@@ -513,16 +519,18 @@ void EntityManager::UpdateGhosting(Player* player) {
|
||||
entity->SetObservers(entity->GetObservers() - 1);
|
||||
} else if (!observed && ghostingDistanceMin > distance) {
|
||||
// Check collectables, don't construct if it has been collected
|
||||
uint32_t collectionId = entity->GetCollectibleID();
|
||||
auto* collectibleComponent = entity->GetComponent<CollectibleComponent>();
|
||||
if (collectibleComponent) {
|
||||
uint32_t collectionId = collectibleComponent->GetCollectibleId();
|
||||
|
||||
if (collectionId != 0) {
|
||||
collectionId = static_cast<uint32_t>(collectionId) + static_cast<uint32_t>(Game::server->GetZoneID() << 8);
|
||||
if (collectionId != 0) {
|
||||
collectionId = static_cast<uint32_t>(collectionId) + static_cast<uint32_t>(Game::server->GetZoneID() << 8);
|
||||
|
||||
if (missionComponent->HasCollectible(collectionId)) {
|
||||
continue;
|
||||
if (missionComponent->HasCollectible(collectionId)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
player->ObserveEntity(id);
|
||||
|
||||
ConstructEntity(entity, player->GetSystemAddress());
|
||||
@@ -594,7 +602,7 @@ void EntityManager::ScheduleForKill(Entity* entity) {
|
||||
if (!entity)
|
||||
return;
|
||||
|
||||
SwitchComponent* switchComp = entity->GetComponent<SwitchComponent>();
|
||||
auto* switchComp = entity->GetComponent<SwitchComponent>();
|
||||
if (switchComp) {
|
||||
entity->TriggerEvent(eTriggerEventType::DEACTIVATED, entity);
|
||||
}
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
#ifndef ENTITYMANAGER_H
|
||||
#define ENTITYMANAGER_H
|
||||
|
||||
#include "dCommonVars.h"
|
||||
#include <map>
|
||||
#include <stack>
|
||||
#include <vector>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "dCommonVars.h"
|
||||
|
||||
class Entity;
|
||||
class EntityInfo;
|
||||
class Player;
|
||||
@@ -18,8 +17,19 @@ struct SystemAddress;
|
||||
|
||||
class EntityManager {
|
||||
public:
|
||||
static EntityManager* Instance() {
|
||||
if (!m_Address) {
|
||||
m_Address = new EntityManager();
|
||||
m_Address->Initialize();
|
||||
}
|
||||
|
||||
return m_Address;
|
||||
}
|
||||
|
||||
void Initialize();
|
||||
|
||||
~EntityManager();
|
||||
|
||||
void UpdateEntities(float deltaTime);
|
||||
Entity* CreateEntity(EntityInfo info, User* user = nullptr, Entity* parentEntity = nullptr, bool controller = false, LWOOBJID explicitId = LWOOBJID_EMPTY);
|
||||
void DestroyEntity(const LWOOBJID& objectID);
|
||||
@@ -79,6 +89,7 @@ private:
|
||||
void KillEntities();
|
||||
void DeleteEntities();
|
||||
|
||||
static EntityManager* m_Address; //For singleton method
|
||||
static std::vector<LWOMAPID> m_GhostingExcludedZones;
|
||||
static std::vector<LOT> m_GhostingExcludedLOTs;
|
||||
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
#include "LeaderboardManager.h"
|
||||
|
||||
#include <sstream>
|
||||
#include <utility>
|
||||
|
||||
#include "Database.h"
|
||||
#include "EntityManager.h"
|
||||
#include "Character.h"
|
||||
@@ -13,400 +10,461 @@
|
||||
#include "CDClientManager.h"
|
||||
#include "GeneralUtils.h"
|
||||
#include "Entity.h"
|
||||
#include "LDFFormat.h"
|
||||
#include "DluAssert.h"
|
||||
|
||||
#include "CDActivitiesTable.h"
|
||||
#include "Metrics.hpp"
|
||||
|
||||
namespace LeaderboardManager {
|
||||
std::map<GameID, Leaderboard::Type> leaderboardCache;
|
||||
}
|
||||
|
||||
Leaderboard::Leaderboard(const GameID gameID, const Leaderboard::InfoType infoType, const bool weekly, LWOOBJID relatedPlayer, const Leaderboard::Type leaderboardType) {
|
||||
Leaderboard::Leaderboard(uint32_t gameID, uint32_t infoType, bool weekly, std::vector<LeaderboardEntry> entries,
|
||||
LWOOBJID relatedPlayer, LeaderboardType leaderboardType) {
|
||||
this->relatedPlayer = relatedPlayer;
|
||||
this->gameID = gameID;
|
||||
this->weekly = weekly;
|
||||
this->infoType = infoType;
|
||||
this->entries = std::move(entries);
|
||||
this->leaderboardType = leaderboardType;
|
||||
this->relatedPlayer = relatedPlayer;
|
||||
}
|
||||
|
||||
Leaderboard::~Leaderboard() {
|
||||
Clear();
|
||||
}
|
||||
std::u16string Leaderboard::ToString() const {
|
||||
std::string leaderboard;
|
||||
|
||||
void Leaderboard::Clear() {
|
||||
for (auto& entry : entries) for (auto ldfData : entry) delete ldfData;
|
||||
}
|
||||
leaderboard += "ADO.Result=7:1\n";
|
||||
leaderboard += "Result.Count=1:1\n";
|
||||
leaderboard += "Result[0].Index=0:RowNumber\n";
|
||||
leaderboard += "Result[0].RowCount=1:" + std::to_string(entries.size()) + "\n";
|
||||
|
||||
inline void WriteLeaderboardRow(std::ostringstream& leaderboard, const uint32_t& index, LDFBaseData* data) {
|
||||
leaderboard << "\nResult[0].Row[" << index << "]." << data->GetString();
|
||||
}
|
||||
auto index = 0;
|
||||
for (const auto& entry : entries) {
|
||||
leaderboard += "Result[0].Row[" + std::to_string(index) + "].LastPlayed=8:" + std::to_string(entry.lastPlayed) + "\n";
|
||||
leaderboard += "Result[0].Row[" + std::to_string(index) + "].CharacterID=8:" + std::to_string(entry.playerID) + "\n";
|
||||
leaderboard += "Result[0].Row[" + std::to_string(index) + "].NumPlayed=1:1\n";
|
||||
leaderboard += "Result[0].Row[" + std::to_string(index) + "].RowNumber=8:" + std::to_string(entry.placement) + "\n";
|
||||
leaderboard += "Result[0].Row[" + std::to_string(index) + "].Time=1:" + std::to_string(entry.time) + "\n";
|
||||
|
||||
void Leaderboard::Serialize(RakNet::BitStream* bitStream) const {
|
||||
bitStream->Write(gameID);
|
||||
bitStream->Write(infoType);
|
||||
|
||||
std::ostringstream leaderboard;
|
||||
|
||||
leaderboard << "ADO.Result=7:1"; // Unused in 1.10.64, but is in captures
|
||||
leaderboard << "\nResult.Count=1:1"; // number of results, always 1
|
||||
if (!this->entries.empty()) leaderboard << "\nResult[0].Index=0:RowNumber"; // "Primary key". Live doesn't include this if there are no entries.
|
||||
leaderboard << "\nResult[0].RowCount=1:" << entries.size();
|
||||
|
||||
int32_t rowNumber = 0;
|
||||
for (auto& entry : entries) {
|
||||
for (auto* data : entry) {
|
||||
WriteLeaderboardRow(leaderboard, rowNumber, data);
|
||||
// Only these minigames have a points system
|
||||
if (leaderboardType == Survival || leaderboardType == ShootingGallery) {
|
||||
leaderboard += "Result[0].Row[" + std::to_string(index) + "].Points=1:" + std::to_string(entry.score) + "\n";
|
||||
} else if (leaderboardType == SurvivalNS) {
|
||||
leaderboard += "Result[0].Row[" + std::to_string(index) + "].Wave=1:" + std::to_string(entry.score) + "\n";
|
||||
}
|
||||
rowNumber++;
|
||||
|
||||
leaderboard += "Result[0].Row[" + std::to_string(index) + "].name=0:" + entry.playerName + "\n";
|
||||
index++;
|
||||
}
|
||||
|
||||
// Serialize the thing to a BitStream
|
||||
uint32_t leaderboardSize = leaderboard.tellp();
|
||||
bitStream->Write<uint32_t>(leaderboardSize);
|
||||
// Doing this all in 1 call so there is no possbility of a dangling pointer.
|
||||
bitStream->WriteAlignedBytes(reinterpret_cast<const unsigned char*>(GeneralUtils::ASCIIToUTF16(leaderboard.str()).c_str()), leaderboardSize * sizeof(char16_t));
|
||||
if (leaderboardSize > 0) bitStream->Write<uint16_t>(0);
|
||||
bitStream->Write0();
|
||||
bitStream->Write0();
|
||||
return GeneralUtils::UTF8ToUTF16(leaderboard);
|
||||
}
|
||||
|
||||
void Leaderboard::QueryToLdf(std::unique_ptr<sql::ResultSet>& rows) {
|
||||
Clear();
|
||||
if (rows->rowsCount() == 0) return;
|
||||
|
||||
this->entries.reserve(rows->rowsCount());
|
||||
while (rows->next()) {
|
||||
constexpr int32_t MAX_NUM_DATA_PER_ROW = 9;
|
||||
this->entries.push_back(std::vector<LDFBaseData*>());
|
||||
auto& entry = this->entries.back();
|
||||
entry.reserve(MAX_NUM_DATA_PER_ROW);
|
||||
entry.push_back(new LDFData<uint64_t>(u"CharacterID", rows->getInt("character_id")));
|
||||
entry.push_back(new LDFData<uint64_t>(u"LastPlayed", rows->getUInt64("lastPlayed")));
|
||||
entry.push_back(new LDFData<int32_t>(u"NumPlayed", rows->getInt("timesPlayed")));
|
||||
entry.push_back(new LDFData<std::u16string>(u"name", GeneralUtils::ASCIIToUTF16(rows->getString("name").c_str())));
|
||||
entry.push_back(new LDFData<uint64_t>(u"RowNumber", rows->getInt("ranking")));
|
||||
switch (leaderboardType) {
|
||||
case Type::ShootingGallery:
|
||||
entry.push_back(new LDFData<int32_t>(u"Score", rows->getInt("primaryScore")));
|
||||
// Score:1
|
||||
entry.push_back(new LDFData<int32_t>(u"Streak", rows->getInt("secondaryScore")));
|
||||
// Streak:1
|
||||
entry.push_back(new LDFData<float>(u"HitPercentage", (rows->getInt("tertiaryScore") / 100.0f)));
|
||||
// HitPercentage:3 between 0 and 1
|
||||
break;
|
||||
case Type::Racing:
|
||||
entry.push_back(new LDFData<float>(u"BestTime", rows->getDouble("primaryScore")));
|
||||
// BestLapTime:3
|
||||
entry.push_back(new LDFData<float>(u"BestLapTime", rows->getDouble("secondaryScore")));
|
||||
// BestTime:3
|
||||
entry.push_back(new LDFData<int32_t>(u"License", 1));
|
||||
// License:1 - 1 if player has completed mission 637 and 0 otherwise
|
||||
entry.push_back(new LDFData<int32_t>(u"NumWins", rows->getInt("numWins")));
|
||||
// NumWins:1
|
||||
break;
|
||||
case Type::UnusedLeaderboard4:
|
||||
entry.push_back(new LDFData<int32_t>(u"Points", rows->getInt("primaryScore")));
|
||||
// Points:1
|
||||
break;
|
||||
case Type::MonumentRace:
|
||||
entry.push_back(new LDFData<int32_t>(u"Time", rows->getInt("primaryScore")));
|
||||
// Time:1(?)
|
||||
break;
|
||||
case Type::FootRace:
|
||||
entry.push_back(new LDFData<int32_t>(u"Time", rows->getInt("primaryScore")));
|
||||
// Time:1
|
||||
break;
|
||||
case Type::Survival:
|
||||
entry.push_back(new LDFData<int32_t>(u"Points", rows->getInt("primaryScore")));
|
||||
// Points:1
|
||||
entry.push_back(new LDFData<int32_t>(u"Time", rows->getInt("secondaryScore")));
|
||||
// Time:1
|
||||
break;
|
||||
case Type::SurvivalNS:
|
||||
entry.push_back(new LDFData<int32_t>(u"Wave", rows->getInt("primaryScore")));
|
||||
// Wave:1
|
||||
entry.push_back(new LDFData<int32_t>(u"Time", rows->getInt("secondaryScore")));
|
||||
// Time:1
|
||||
break;
|
||||
case Type::Donations:
|
||||
entry.push_back(new LDFData<int32_t>(u"Score", rows->getInt("primaryScore")));
|
||||
// Score:1
|
||||
break;
|
||||
case Type::None:
|
||||
// This type is included here simply to resolve a compiler warning on mac about unused enum types
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
std::vector<LeaderboardEntry> Leaderboard::GetEntries() {
|
||||
return entries;
|
||||
}
|
||||
|
||||
const std::string_view Leaderboard::GetOrdering(Leaderboard::Type leaderboardType) {
|
||||
// Use a switch case and return desc for all 3 columns if higher is better and asc if lower is better
|
||||
switch (leaderboardType) {
|
||||
case Type::Racing:
|
||||
case Type::MonumentRace:
|
||||
return "primaryScore ASC, secondaryScore ASC, tertiaryScore ASC";
|
||||
case Type::Survival:
|
||||
return Game::config->GetValue("classic_survival_scoring") == "1" ?
|
||||
"secondaryScore DESC, primaryScore DESC, tertiaryScore DESC" :
|
||||
"primaryScore DESC, secondaryScore DESC, tertiaryScore DESC";
|
||||
case Type::SurvivalNS:
|
||||
return "primaryScore DESC, secondaryScore ASC, tertiaryScore DESC";
|
||||
case Type::ShootingGallery:
|
||||
case Type::FootRace:
|
||||
case Type::UnusedLeaderboard4:
|
||||
case Type::Donations:
|
||||
case Type::None:
|
||||
default:
|
||||
return "primaryScore DESC, secondaryScore DESC, tertiaryScore DESC";
|
||||
}
|
||||
uint32_t Leaderboard::GetGameID() const {
|
||||
return gameID;
|
||||
}
|
||||
|
||||
void Leaderboard::SetupLeaderboard(bool weekly, uint32_t resultStart, uint32_t resultEnd) {
|
||||
resultStart++;
|
||||
resultEnd++;
|
||||
// We need everything except 1 column so i'm selecting * from leaderboard
|
||||
const std::string queryBase =
|
||||
R"QUERY(
|
||||
WITH leaderboardsRanked AS (
|
||||
SELECT leaderboard.*, charinfo.name,
|
||||
RANK() OVER
|
||||
(
|
||||
ORDER BY %s, UNIX_TIMESTAMP(last_played) ASC, id DESC
|
||||
) AS ranking
|
||||
FROM leaderboard JOIN charinfo on charinfo.id = leaderboard.character_id
|
||||
WHERE game_id = ? %s
|
||||
),
|
||||
myStanding AS (
|
||||
SELECT
|
||||
ranking as myRank
|
||||
FROM leaderboardsRanked
|
||||
WHERE id = ?
|
||||
),
|
||||
lowestRanking AS (
|
||||
SELECT MAX(ranking) AS lowestRank
|
||||
FROM leaderboardsRanked
|
||||
)
|
||||
SELECT leaderboardsRanked.*, character_id, UNIX_TIMESTAMP(last_played) as lastPlayed, leaderboardsRanked.name, leaderboardsRanked.ranking FROM leaderboardsRanked, myStanding, lowestRanking
|
||||
WHERE leaderboardsRanked.ranking
|
||||
BETWEEN
|
||||
LEAST(GREATEST(CAST(myRank AS SIGNED) - 5, %i), lowestRanking.lowestRank - 9)
|
||||
AND
|
||||
LEAST(GREATEST(myRank + 5, %i), lowestRanking.lowestRank)
|
||||
ORDER BY ranking ASC;
|
||||
)QUERY";
|
||||
|
||||
std::string friendsFilter =
|
||||
R"QUERY(
|
||||
AND (
|
||||
character_id IN (
|
||||
SELECT fr.requested_player FROM (
|
||||
SELECT CASE
|
||||
WHEN player_id = ? THEN friend_id
|
||||
WHEN friend_id = ? THEN player_id
|
||||
END AS requested_player
|
||||
FROM friends
|
||||
) AS fr
|
||||
JOIN charinfo AS ci
|
||||
ON ci.id = fr.requested_player
|
||||
WHERE fr.requested_player IS NOT NULL
|
||||
)
|
||||
OR character_id = ?
|
||||
)
|
||||
)QUERY";
|
||||
|
||||
std::string weeklyFilter = " AND UNIX_TIMESTAMP(last_played) BETWEEN UNIX_TIMESTAMP(date_sub(now(),INTERVAL 1 WEEK)) AND UNIX_TIMESTAMP(now()) ";
|
||||
|
||||
std::string filter;
|
||||
// Setup our filter based on the query type
|
||||
if (this->infoType == InfoType::Friends) filter += friendsFilter;
|
||||
if (this->weekly) filter += weeklyFilter;
|
||||
const auto orderBase = GetOrdering(this->leaderboardType);
|
||||
|
||||
// For top query, we want to just rank all scores, but for all others we need the scores around a specific player
|
||||
std::string baseLookup;
|
||||
if (this->infoType == InfoType::Top) {
|
||||
baseLookup = "SELECT id, last_played FROM leaderboard WHERE game_id = ? " + (this->weekly ? weeklyFilter : std::string("")) + " ORDER BY ";
|
||||
baseLookup += orderBase.data();
|
||||
} else {
|
||||
baseLookup = "SELECT id, last_played FROM leaderboard WHERE game_id = ? " + (this->weekly ? weeklyFilter : std::string("")) + " AND character_id = ";
|
||||
baseLookup += std::to_string(static_cast<uint32_t>(this->relatedPlayer));
|
||||
}
|
||||
baseLookup += " LIMIT 1";
|
||||
Game::logger->LogDebug("LeaderboardManager", "query is %s", baseLookup.c_str());
|
||||
std::unique_ptr<sql::PreparedStatement> baseQuery(Database::CreatePreppedStmt(baseLookup));
|
||||
baseQuery->setInt(1, this->gameID);
|
||||
std::unique_ptr<sql::ResultSet> baseResult(baseQuery->executeQuery());
|
||||
|
||||
if (!baseResult->next()) return; // In this case, there are no entries in the leaderboard for this game.
|
||||
|
||||
uint32_t relatedPlayerLeaderboardId = baseResult->getInt("id");
|
||||
|
||||
// Create and execute the actual save here. Using a heap allocated buffer to avoid stack overflow
|
||||
constexpr uint16_t STRING_LENGTH = 4096;
|
||||
std::unique_ptr<char[]> lookupBuffer = std::make_unique<char[]>(STRING_LENGTH);
|
||||
int32_t res = snprintf(lookupBuffer.get(), STRING_LENGTH, queryBase.c_str(), orderBase.data(), filter.c_str(), resultStart, resultEnd);
|
||||
DluAssert(res != -1);
|
||||
std::unique_ptr<sql::PreparedStatement> query(Database::CreatePreppedStmt(lookupBuffer.get()));
|
||||
Game::logger->LogDebug("LeaderboardManager", "Query is %s vars are %i %i %i", lookupBuffer.get(), this->gameID, this->relatedPlayer, relatedPlayerLeaderboardId);
|
||||
query->setInt(1, this->gameID);
|
||||
if (this->infoType == InfoType::Friends) {
|
||||
query->setInt(2, this->relatedPlayer);
|
||||
query->setInt(3, this->relatedPlayer);
|
||||
query->setInt(4, this->relatedPlayer);
|
||||
query->setInt(5, relatedPlayerLeaderboardId);
|
||||
} else {
|
||||
query->setInt(2, relatedPlayerLeaderboardId);
|
||||
}
|
||||
std::unique_ptr<sql::ResultSet> result(query->executeQuery());
|
||||
QueryToLdf(result);
|
||||
uint32_t Leaderboard::GetInfoType() const {
|
||||
return infoType;
|
||||
}
|
||||
|
||||
void Leaderboard::Send(const LWOOBJID targetID) const {
|
||||
auto* player = Game::entityManager->GetEntity(relatedPlayer);
|
||||
void Leaderboard::Send(LWOOBJID targetID) const {
|
||||
auto* player = EntityManager::Instance()->GetEntity(relatedPlayer);
|
||||
if (player != nullptr) {
|
||||
GameMessages::SendActivitySummaryLeaderboardData(targetID, this, player->GetSystemAddress());
|
||||
}
|
||||
}
|
||||
|
||||
std::string FormatInsert(const Leaderboard::Type& type, const Score& score, const bool useUpdate) {
|
||||
std::string insertStatement;
|
||||
if (useUpdate) {
|
||||
insertStatement =
|
||||
R"QUERY(
|
||||
UPDATE leaderboard
|
||||
SET primaryScore = %f, secondaryScore = %f, tertiaryScore = %f,
|
||||
timesPlayed = timesPlayed + 1 WHERE character_id = ? AND game_id = ?;
|
||||
)QUERY";
|
||||
} else {
|
||||
insertStatement =
|
||||
R"QUERY(
|
||||
INSERT leaderboard SET
|
||||
primaryScore = %f, secondaryScore = %f, tertiaryScore = %f,
|
||||
character_id = ?, game_id = ?;
|
||||
)QUERY";
|
||||
}
|
||||
void LeaderboardManager::SaveScore(LWOOBJID playerID, uint32_t gameID, uint32_t score, uint32_t time) {
|
||||
const auto* player = EntityManager::Instance()->GetEntity(playerID);
|
||||
if (player == nullptr)
|
||||
return;
|
||||
|
||||
constexpr uint16_t STRING_LENGTH = 400;
|
||||
// Then fill in our score
|
||||
char finishedQuery[STRING_LENGTH];
|
||||
int32_t res = snprintf(finishedQuery, STRING_LENGTH, insertStatement.c_str(), score.GetPrimaryScore(), score.GetSecondaryScore(), score.GetTertiaryScore());
|
||||
DluAssert(res != -1);
|
||||
return finishedQuery;
|
||||
}
|
||||
auto* character = player->GetCharacter();
|
||||
if (character == nullptr)
|
||||
return;
|
||||
|
||||
void LeaderboardManager::SaveScore(const LWOOBJID& playerID, const GameID activityId, const float primaryScore, const float secondaryScore, const float tertiaryScore) {
|
||||
const Leaderboard::Type leaderboardType = GetLeaderboardType(activityId);
|
||||
auto* select = Database::CreatePreppedStmt("SELECT time, score FROM leaderboard WHERE character_id = ? AND game_id = ?;");
|
||||
|
||||
std::unique_ptr<sql::PreparedStatement> query(Database::CreatePreppedStmt("SELECT * FROM leaderboard WHERE character_id = ? AND game_id = ?;"));
|
||||
query->setInt(1, playerID);
|
||||
query->setInt(2, activityId);
|
||||
std::unique_ptr<sql::ResultSet> myScoreResult(query->executeQuery());
|
||||
select->setUInt64(1, character->GetID());
|
||||
select->setInt(2, gameID);
|
||||
|
||||
auto any = false;
|
||||
auto* result = select->executeQuery();
|
||||
auto leaderboardType = GetLeaderboardType(gameID);
|
||||
|
||||
// Check if the new score is a high score
|
||||
while (result->next()) {
|
||||
any = true;
|
||||
|
||||
const auto storedTime = result->getInt(1);
|
||||
const auto storedScore = result->getInt(2);
|
||||
auto highscore = true;
|
||||
bool classicSurvivalScoring = Game::config->GetValue("classic_survival_scoring") == "1";
|
||||
|
||||
std::string saveQuery("UPDATE leaderboard SET timesPlayed = timesPlayed + 1 WHERE character_id = ? AND game_id = ?;");
|
||||
Score newScore(primaryScore, secondaryScore, tertiaryScore);
|
||||
if (myScoreResult->next()) {
|
||||
Score oldScore;
|
||||
bool lowerScoreBetter = false;
|
||||
switch (leaderboardType) {
|
||||
// Higher score better
|
||||
case Leaderboard::Type::ShootingGallery: {
|
||||
oldScore.SetPrimaryScore(myScoreResult->getInt("primaryScore"));
|
||||
oldScore.SetSecondaryScore(myScoreResult->getInt("secondaryScore"));
|
||||
oldScore.SetTertiaryScore(myScoreResult->getInt("tertiaryScore"));
|
||||
case ShootingGallery:
|
||||
if (score <= storedScore)
|
||||
highscore = false;
|
||||
break;
|
||||
}
|
||||
case Leaderboard::Type::FootRace: {
|
||||
oldScore.SetPrimaryScore(myScoreResult->getInt("primaryScore"));
|
||||
case Racing:
|
||||
if (time >= storedTime)
|
||||
highscore = false;
|
||||
break;
|
||||
}
|
||||
case Leaderboard::Type::Survival: {
|
||||
oldScore.SetPrimaryScore(myScoreResult->getInt("primaryScore"));
|
||||
oldScore.SetSecondaryScore(myScoreResult->getInt("secondaryScore"));
|
||||
case MonumentRace:
|
||||
if (time >= storedTime)
|
||||
highscore = false;
|
||||
break;
|
||||
}
|
||||
case Leaderboard::Type::SurvivalNS: {
|
||||
oldScore.SetPrimaryScore(myScoreResult->getInt("primaryScore"));
|
||||
oldScore.SetSecondaryScore(myScoreResult->getInt("secondaryScore"));
|
||||
case FootRace:
|
||||
if (time <= storedTime)
|
||||
highscore = false;
|
||||
break;
|
||||
}
|
||||
case Leaderboard::Type::UnusedLeaderboard4:
|
||||
case Leaderboard::Type::Donations: {
|
||||
oldScore.SetPrimaryScore(myScoreResult->getInt("primaryScore"));
|
||||
newScore.SetPrimaryScore(oldScore.GetPrimaryScore() + newScore.GetPrimaryScore());
|
||||
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 Leaderboard::Type::Racing: {
|
||||
oldScore.SetPrimaryScore(myScoreResult->getInt("primaryScore"));
|
||||
oldScore.SetSecondaryScore(myScoreResult->getInt("secondaryScore"));
|
||||
|
||||
// For wins we dont care about the score, just the time, so zero out the tertiary.
|
||||
// Wins are updated later.
|
||||
oldScore.SetTertiaryScore(0);
|
||||
newScore.SetTertiaryScore(0);
|
||||
lowerScoreBetter = true;
|
||||
case SurvivalNS:
|
||||
if (!(score > storedScore || (time < storedTime && score >= storedScore)))
|
||||
highscore = false;
|
||||
break;
|
||||
}
|
||||
case Leaderboard::Type::MonumentRace: {
|
||||
oldScore.SetPrimaryScore(myScoreResult->getInt("primaryScore"));
|
||||
lowerScoreBetter = true;
|
||||
// Do score checking here
|
||||
break;
|
||||
}
|
||||
case Leaderboard::Type::None:
|
||||
default:
|
||||
Game::logger->Log("LeaderboardManager", "Unknown leaderboard type %i for game %i. Cannot save score!", leaderboardType, activityId);
|
||||
highscore = false;
|
||||
}
|
||||
|
||||
if (!highscore) {
|
||||
delete select;
|
||||
delete result;
|
||||
return;
|
||||
}
|
||||
bool newHighScore = lowerScoreBetter ? newScore < oldScore : newScore > oldScore;
|
||||
// Nimbus station has a weird leaderboard where we need a custom scoring system
|
||||
if (leaderboardType == Leaderboard::Type::SurvivalNS) {
|
||||
newHighScore = newScore.GetPrimaryScore() > oldScore.GetPrimaryScore() ||
|
||||
(newScore.GetPrimaryScore() == oldScore.GetPrimaryScore() && newScore.GetSecondaryScore() < oldScore.GetSecondaryScore());
|
||||
} else if (leaderboardType == Leaderboard::Type::Survival && Game::config->GetValue("classic_survival_scoring") == "1") {
|
||||
Score oldScoreFlipped(oldScore.GetSecondaryScore(), oldScore.GetPrimaryScore());
|
||||
Score newScoreFlipped(newScore.GetSecondaryScore(), newScore.GetPrimaryScore());
|
||||
newHighScore = newScoreFlipped > oldScoreFlipped;
|
||||
}
|
||||
if (newHighScore) {
|
||||
saveQuery = FormatInsert(leaderboardType, newScore, true);
|
||||
}
|
||||
}
|
||||
|
||||
delete select;
|
||||
delete result;
|
||||
|
||||
if (any) {
|
||||
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());
|
||||
statement->setInt(4, gameID);
|
||||
statement->execute();
|
||||
|
||||
delete statement;
|
||||
} else {
|
||||
saveQuery = FormatInsert(leaderboardType, newScore, false);
|
||||
}
|
||||
Game::logger->Log("LeaderboardManager", "save query %s %i %i", saveQuery.c_str(), playerID, activityId);
|
||||
std::unique_ptr<sql::PreparedStatement> saveStatement(Database::CreatePreppedStmt(saveQuery));
|
||||
saveStatement->setInt(1, playerID);
|
||||
saveStatement->setInt(2, activityId);
|
||||
saveStatement->execute();
|
||||
// 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);
|
||||
statement->setInt(3, time);
|
||||
statement->setInt(4, score);
|
||||
statement->execute();
|
||||
|
||||
// track wins separately
|
||||
if (leaderboardType == Leaderboard::Type::Racing && tertiaryScore != 0.0f) {
|
||||
std::unique_ptr<sql::PreparedStatement> winUpdate(Database::CreatePreppedStmt("UPDATE leaderboard SET numWins = numWins + 1 WHERE character_id = ? AND game_id = ?;"));
|
||||
winUpdate->setInt(1, playerID);
|
||||
winUpdate->setInt(2, activityId);
|
||||
winUpdate->execute();
|
||||
delete statement;
|
||||
}
|
||||
}
|
||||
|
||||
void LeaderboardManager::SendLeaderboard(const GameID gameID, const Leaderboard::InfoType infoType, const bool weekly, const LWOOBJID playerID, const LWOOBJID targetID, const uint32_t resultStart, const uint32_t resultEnd) {
|
||||
Leaderboard leaderboard(gameID, infoType, weekly, playerID, GetLeaderboardType(gameID));
|
||||
leaderboard.SetupLeaderboard(weekly, resultStart, resultEnd);
|
||||
leaderboard.Send(targetID);
|
||||
Leaderboard* LeaderboardManager::GetLeaderboard(uint32_t gameID, InfoType infoType, bool weekly, LWOOBJID playerID) {
|
||||
auto leaderboardType = GetLeaderboardType(gameID);
|
||||
|
||||
std::string query;
|
||||
bool classicSurvivalScoring = Game::config->GetValue("classic_survival_scoring") == "1";
|
||||
switch (infoType) {
|
||||
case InfoType::Standings:
|
||||
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:
|
||||
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:
|
||||
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);
|
||||
statement->setUInt(1, gameID);
|
||||
|
||||
// Only the standings and friends leaderboards require the character ID to be set
|
||||
if (infoType == Standings || infoType == Friends) {
|
||||
auto characterID = 0;
|
||||
|
||||
const auto* player = EntityManager::Instance()->GetEntity(playerID);
|
||||
if (player != nullptr) {
|
||||
auto* character = player->GetCharacter();
|
||||
if (character != nullptr)
|
||||
characterID = character->GetID();
|
||||
}
|
||||
|
||||
statement->setUInt64(2, characterID);
|
||||
}
|
||||
|
||||
auto* res = statement->executeQuery();
|
||||
|
||||
std::vector<LeaderboardEntry> entries{};
|
||||
|
||||
uint32_t index = 0;
|
||||
while (res->next()) {
|
||||
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++;
|
||||
}
|
||||
|
||||
delete res;
|
||||
delete statement;
|
||||
|
||||
return new Leaderboard(gameID, infoType, weekly, entries, playerID, leaderboardType);
|
||||
}
|
||||
|
||||
Leaderboard::Type LeaderboardManager::GetLeaderboardType(const GameID gameID) {
|
||||
auto lookup = leaderboardCache.find(gameID);
|
||||
if (lookup != leaderboardCache.end()) return lookup->second;
|
||||
void LeaderboardManager::SendLeaderboard(uint32_t gameID, InfoType infoType, bool weekly, LWOOBJID targetID,
|
||||
LWOOBJID playerID) {
|
||||
const auto* leaderboard = LeaderboardManager::GetLeaderboard(gameID, infoType, weekly, playerID);
|
||||
leaderboard->Send(targetID);
|
||||
delete leaderboard;
|
||||
}
|
||||
|
||||
LeaderboardType LeaderboardManager::GetLeaderboardType(uint32_t gameID) {
|
||||
auto* activitiesTable = CDClientManager::Instance().GetTable<CDActivitiesTable>();
|
||||
std::vector<CDActivities> activities = activitiesTable->Query([gameID](const CDActivities& entry) {
|
||||
return entry.ActivityID == gameID;
|
||||
std::vector<CDActivities> activities = activitiesTable->Query([=](const CDActivities& entry) {
|
||||
return (entry.ActivityID == gameID);
|
||||
});
|
||||
auto type = !activities.empty() ? static_cast<Leaderboard::Type>(activities.at(0).leaderboardType) : Leaderboard::Type::None;
|
||||
leaderboardCache.insert_or_assign(gameID, type);
|
||||
return type;
|
||||
|
||||
for (const auto& activity : activities) {
|
||||
return static_cast<LeaderboardType>(activity.leaderboardType);
|
||||
}
|
||||
|
||||
return LeaderboardType::None;
|
||||
}
|
||||
|
||||
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 "
|
||||
" 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::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 "
|
||||
" 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::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 "
|
||||
" 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::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 "
|
||||
" 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::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 "
|
||||
" 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::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 "
|
||||
" 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::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;";
|
||||
|
||||
@@ -1,134 +1,80 @@
|
||||
#ifndef __LEADERBOARDMANAGER__H__
|
||||
#define __LEADERBOARDMANAGER__H__
|
||||
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <string_view>
|
||||
#pragma once
|
||||
#include <vector>
|
||||
|
||||
#include "Singleton.h"
|
||||
#include <climits>
|
||||
#include "dCommonVars.h"
|
||||
#include "LDFFormat.h"
|
||||
|
||||
namespace sql {
|
||||
class ResultSet;
|
||||
struct LeaderboardEntry {
|
||||
uint64_t playerID;
|
||||
std::string playerName;
|
||||
uint32_t time;
|
||||
uint32_t score;
|
||||
uint32_t placement;
|
||||
time_t lastPlayed;
|
||||
};
|
||||
|
||||
namespace RakNet {
|
||||
class BitStream;
|
||||
enum InfoType : uint32_t {
|
||||
Top, // Top 11 all time players
|
||||
Standings, // Ranking of the current player
|
||||
Friends // Ranking between friends
|
||||
};
|
||||
|
||||
class Score {
|
||||
public:
|
||||
Score() {
|
||||
primaryScore = 0;
|
||||
secondaryScore = 0;
|
||||
tertiaryScore = 0;
|
||||
}
|
||||
Score(const float primaryScore, const float secondaryScore = 0, const float tertiaryScore = 0) {
|
||||
this->primaryScore = primaryScore;
|
||||
this->secondaryScore = secondaryScore;
|
||||
this->tertiaryScore = tertiaryScore;
|
||||
}
|
||||
bool operator<(const Score& rhs) const {
|
||||
return primaryScore < rhs.primaryScore || (primaryScore == rhs.primaryScore && secondaryScore < rhs.secondaryScore) || (primaryScore == rhs.primaryScore && secondaryScore == rhs.secondaryScore && tertiaryScore < rhs.tertiaryScore);
|
||||
}
|
||||
bool operator>(const Score& rhs) const {
|
||||
return primaryScore > rhs.primaryScore || (primaryScore == rhs.primaryScore && secondaryScore > rhs.secondaryScore) || (primaryScore == rhs.primaryScore && secondaryScore == rhs.secondaryScore && tertiaryScore > rhs.tertiaryScore);
|
||||
}
|
||||
void SetPrimaryScore(const float score) { primaryScore = score; }
|
||||
float GetPrimaryScore() const { return primaryScore; }
|
||||
|
||||
void SetSecondaryScore(const float score) { secondaryScore = score; }
|
||||
float GetSecondaryScore() const { return secondaryScore; }
|
||||
|
||||
void SetTertiaryScore(const float score) { tertiaryScore = score; }
|
||||
float GetTertiaryScore() const { return tertiaryScore; }
|
||||
private:
|
||||
float primaryScore;
|
||||
float secondaryScore;
|
||||
float tertiaryScore;
|
||||
enum LeaderboardType : uint32_t {
|
||||
ShootingGallery,
|
||||
Racing,
|
||||
MonumentRace,
|
||||
FootRace,
|
||||
Survival = 5,
|
||||
SurvivalNS = 6,
|
||||
None = UINT_MAX
|
||||
};
|
||||
|
||||
using GameID = uint32_t;
|
||||
|
||||
class Leaderboard {
|
||||
public:
|
||||
|
||||
// Enums for leaderboards
|
||||
enum InfoType : uint32_t {
|
||||
Top, // Top 11 all time players
|
||||
MyStanding, // Ranking of the current player
|
||||
Friends // Ranking between friends
|
||||
};
|
||||
|
||||
enum Type : uint32_t {
|
||||
ShootingGallery,
|
||||
Racing,
|
||||
MonumentRace,
|
||||
FootRace,
|
||||
UnusedLeaderboard4, // There is no 4 defined anywhere in the cdclient, but it takes a Score.
|
||||
Survival,
|
||||
SurvivalNS,
|
||||
Donations,
|
||||
None
|
||||
};
|
||||
Leaderboard() = delete;
|
||||
Leaderboard(const GameID gameID, const Leaderboard::InfoType infoType, const bool weekly, LWOOBJID relatedPlayer, const Leaderboard::Type = None);
|
||||
|
||||
~Leaderboard();
|
||||
|
||||
/**
|
||||
* @brief Resets the leaderboard state and frees its allocated memory
|
||||
*
|
||||
*/
|
||||
void Clear();
|
||||
|
||||
/**
|
||||
* Serialize the Leaderboard to a BitStream
|
||||
*
|
||||
* Expensive! Leaderboards are very string intensive so be wary of performatnce calling this method.
|
||||
*/
|
||||
void Serialize(RakNet::BitStream* bitStream) const;
|
||||
|
||||
/**
|
||||
* Builds the leaderboard from the database based on the associated gameID
|
||||
*
|
||||
* @param resultStart The index to start the leaderboard at. Zero indexed.
|
||||
* @param resultEnd The index to end the leaderboard at. Zero indexed.
|
||||
*/
|
||||
void SetupLeaderboard(bool weekly, uint32_t resultStart = 0, uint32_t resultEnd = 10);
|
||||
|
||||
/**
|
||||
* Sends the leaderboard to the client specified by targetID.
|
||||
*/
|
||||
void Send(const LWOOBJID targetID) const;
|
||||
|
||||
// Helper function to get the columns, ordering and insert format for a leaderboard
|
||||
static const std::string_view GetOrdering(Type leaderboardType);
|
||||
Leaderboard(uint32_t gameID, uint32_t infoType, bool weekly, std::vector<LeaderboardEntry> entries,
|
||||
LWOOBJID relatedPlayer = LWOOBJID_EMPTY, LeaderboardType = None);
|
||||
std::vector<LeaderboardEntry> GetEntries();
|
||||
[[nodiscard]] std::u16string ToString() const;
|
||||
[[nodiscard]] uint32_t GetGameID() const;
|
||||
[[nodiscard]] uint32_t GetInfoType() const;
|
||||
void Send(LWOOBJID targetID) const;
|
||||
private:
|
||||
// Takes the resulting query from a leaderboard lookup and converts it to the LDF we need
|
||||
// to send it to a client.
|
||||
void QueryToLdf(std::unique_ptr<sql::ResultSet>& rows);
|
||||
|
||||
using LeaderboardEntry = std::vector<LDFBaseData*>;
|
||||
using LeaderboardEntries = std::vector<LeaderboardEntry>;
|
||||
|
||||
LeaderboardEntries entries;
|
||||
std::vector<LeaderboardEntry> entries{};
|
||||
LWOOBJID relatedPlayer;
|
||||
GameID gameID;
|
||||
InfoType infoType;
|
||||
Leaderboard::Type leaderboardType;
|
||||
uint32_t gameID;
|
||||
uint32_t infoType;
|
||||
LeaderboardType leaderboardType;
|
||||
bool weekly;
|
||||
};
|
||||
|
||||
namespace LeaderboardManager {
|
||||
void SendLeaderboard(const GameID gameID, const Leaderboard::InfoType infoType, const bool weekly, const LWOOBJID playerID, const LWOOBJID targetID, const uint32_t resultStart = 0, const uint32_t resultEnd = 10);
|
||||
class LeaderboardManager {
|
||||
public:
|
||||
static LeaderboardManager* Instance() {
|
||||
if (address == nullptr)
|
||||
address = new LeaderboardManager;
|
||||
return address;
|
||||
}
|
||||
static void SendLeaderboard(uint32_t gameID, InfoType infoType, bool weekly, LWOOBJID targetID,
|
||||
LWOOBJID playerID = LWOOBJID_EMPTY);
|
||||
static Leaderboard* GetLeaderboard(uint32_t gameID, InfoType infoType, bool weekly, LWOOBJID playerID = LWOOBJID_EMPTY);
|
||||
static void SaveScore(LWOOBJID playerID, uint32_t gameID, uint32_t score, uint32_t time);
|
||||
static LeaderboardType GetLeaderboardType(uint32_t gameID);
|
||||
private:
|
||||
static LeaderboardManager* address;
|
||||
|
||||
void SaveScore(const LWOOBJID& playerID, const GameID activityId, const float primaryScore, const float secondaryScore = 0, const float tertiaryScore = 0);
|
||||
// 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;
|
||||
|
||||
Leaderboard::Type GetLeaderboardType(const GameID gameID);
|
||||
extern std::map<GameID, Leaderboard::Type> leaderboardCache;
|
||||
// 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;
|
||||
};
|
||||
|
||||
#endif //!__LEADERBOARDMANAGER__H__
|
||||
|
||||
@@ -51,21 +51,17 @@ User* Player::GetParentUser() const {
|
||||
return m_ParentUser;
|
||||
}
|
||||
|
||||
SystemAddress Player::GetSystemAddress() const {
|
||||
return m_SystemAddress;
|
||||
}
|
||||
|
||||
void Player::SetSystemAddress(const SystemAddress& value) {
|
||||
m_SystemAddress = value;
|
||||
}
|
||||
|
||||
void Player::SetRespawnPos(const NiPoint3 position) {
|
||||
void Player::SetRespawnPosition(const NiPoint3& position) {
|
||||
m_respawnPos = position;
|
||||
|
||||
m_Character->SetRespawnPoint(Game::zoneManager->GetZone()->GetWorldID(), position);
|
||||
m_Character->SetRespawnPoint(dZoneManager::Instance()->GetZone()->GetWorldID(), position);
|
||||
}
|
||||
|
||||
void Player::SetRespawnRot(const NiQuaternion rotation) {
|
||||
void Player::SetRespawnRotation(const NiQuaternion& rotation) {
|
||||
m_respawnRot = rotation;
|
||||
}
|
||||
|
||||
@@ -85,7 +81,7 @@ void Player::SendToZone(LWOMAPID zoneId, LWOCLONEID cloneId) {
|
||||
const auto objid = GetObjectID();
|
||||
|
||||
ZoneInstanceManager::Instance()->RequestZoneTransfer(Game::server, zoneId, cloneId, false, [objid](bool mythranShift, uint32_t zoneID, uint32_t zoneInstance, uint32_t zoneClone, std::string serverIP, uint16_t serverPort) {
|
||||
auto* entity = Game::entityManager->GetEntity(objid);
|
||||
auto* entity = EntityManager::Instance()->GetEntity(objid);
|
||||
|
||||
if (entity == nullptr) {
|
||||
return;
|
||||
@@ -108,7 +104,7 @@ void Player::SendToZone(LWOMAPID zoneId, LWOCLONEID cloneId) {
|
||||
|
||||
WorldPackets::SendTransferToWorld(sysAddr, serverIP, serverPort, mythranShift);
|
||||
|
||||
Game::entityManager->DestructEntity(entity);
|
||||
EntityManager::Instance()->DestructEntity(entity);
|
||||
return;
|
||||
});
|
||||
}
|
||||
@@ -135,13 +131,13 @@ void Player::RemoveLimboConstruction(LWOOBJID objectId) {
|
||||
|
||||
void Player::ConstructLimboEntities() {
|
||||
for (const auto objectId : m_LimboConstructions) {
|
||||
auto* entity = Game::entityManager->GetEntity(objectId);
|
||||
auto* entity = EntityManager::Instance()->GetEntity(objectId);
|
||||
|
||||
if (entity == nullptr) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Game::entityManager->ConstructEntity(entity, m_SystemAddress);
|
||||
EntityManager::Instance()->ConstructEntity(entity, m_SystemAddress);
|
||||
}
|
||||
|
||||
m_LimboConstructions.clear();
|
||||
@@ -224,7 +220,7 @@ Player* Player::GetPlayer(const SystemAddress& sysAddr) {
|
||||
}
|
||||
|
||||
Player* Player::GetPlayer(const std::string& name) {
|
||||
const auto characters = Game::entityManager->GetEntitiesByComponent(eReplicaComponentType::CHARACTER);
|
||||
const auto characters = EntityManager::Instance()->GetEntitiesByComponent(eReplicaComponentType::CHARACTER);
|
||||
|
||||
for (auto* character : characters) {
|
||||
if (!character->IsPlayer()) continue;
|
||||
@@ -251,7 +247,7 @@ const std::vector<Player*>& Player::GetAllPlayers() {
|
||||
return m_Players;
|
||||
}
|
||||
|
||||
uint64_t Player::GetDroppedCoins() {
|
||||
uint64_t Player::GetDroppedCoins() const {
|
||||
return m_DroppedCoins;
|
||||
}
|
||||
|
||||
@@ -269,7 +265,7 @@ Player::~Player() {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto* entity = Game::entityManager->GetGhostCandidate(id);
|
||||
auto* entity = EntityManager::Instance()->GetGhostCandidate(id);
|
||||
|
||||
if (entity != nullptr) {
|
||||
entity->SetObservers(entity->GetObservers() - 1);
|
||||
@@ -285,17 +281,13 @@ Player::~Player() {
|
||||
}
|
||||
|
||||
if (IsPlayer()) {
|
||||
Entity* zoneControl = Game::entityManager->GetZoneControlEntity();
|
||||
for (CppScripts::Script* script : CppScripts::GetEntityScripts(zoneControl)) {
|
||||
script->OnPlayerExit(zoneControl, this);
|
||||
}
|
||||
Entity* zoneControl = EntityManager::Instance()->GetZoneControlEntity();
|
||||
zoneControl->GetScript()->OnPlayerExit(zoneControl, this);
|
||||
|
||||
std::vector<Entity*> scriptedActs = Game::entityManager->GetEntitiesByComponent(eReplicaComponentType::SCRIPTED_ACTIVITY);
|
||||
std::vector<Entity*> scriptedActs = EntityManager::Instance()->GetEntitiesByComponent(eReplicaComponentType::SCRIPTED_ACTIVITY);
|
||||
for (Entity* scriptEntity : scriptedActs) {
|
||||
if (scriptEntity->GetObjectID() != zoneControl->GetObjectID()) { // Don't want to trigger twice on instance worlds
|
||||
for (CppScripts::Script* script : CppScripts::GetEntityScripts(scriptEntity)) {
|
||||
script->OnPlayerExit(scriptEntity, this);
|
||||
}
|
||||
scriptEntity->GetScript()->OnPlayerExit(scriptEntity, this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ public:
|
||||
|
||||
User* GetParentUser() const override;
|
||||
|
||||
SystemAddress GetSystemAddress() const override;
|
||||
const SystemAddress GetSystemAddress() const override { return m_SystemAddress; }
|
||||
|
||||
NiPoint3 GetRespawnPosition() const override;
|
||||
|
||||
@@ -36,7 +36,7 @@ public:
|
||||
|
||||
std::map<LWOOBJID, Loot::Info>& GetDroppedLoot();
|
||||
|
||||
uint64_t GetDroppedCoins();
|
||||
uint64_t GetDroppedCoins() const;
|
||||
|
||||
/**
|
||||
* Setters
|
||||
@@ -44,9 +44,9 @@ public:
|
||||
|
||||
void SetSystemAddress(const SystemAddress& value) override;
|
||||
|
||||
void SetRespawnPos(NiPoint3 position) override;
|
||||
void SetRespawnPosition(const NiPoint3& position) override;
|
||||
|
||||
void SetRespawnRot(NiQuaternion rotation) override;
|
||||
void SetRespawnRotation(const NiQuaternion& rotation) override;
|
||||
|
||||
void SetGhostReferencePoint(const NiPoint3& value);
|
||||
|
||||
|
||||
@@ -40,11 +40,11 @@ LWOOBJID Trade::GetParticipantB() const {
|
||||
}
|
||||
|
||||
Entity* Trade::GetParticipantAEntity() const {
|
||||
return Game::entityManager->GetEntity(m_ParticipantA);
|
||||
return EntityManager::Instance()->GetEntity(m_ParticipantA);
|
||||
}
|
||||
|
||||
Entity* Trade::GetParticipantBEntity() const {
|
||||
return Game::entityManager->GetEntity(m_ParticipantB);
|
||||
return EntityManager::Instance()->GetEntity(m_ParticipantB);
|
||||
}
|
||||
|
||||
void Trade::SetCoins(LWOOBJID participant, uint64_t coins) {
|
||||
@@ -219,7 +219,7 @@ void Trade::SendUpdateToOther(LWOOBJID participant) {
|
||||
if (inventoryComponent == nullptr) return;
|
||||
|
||||
for (const auto tradeItem : itemIds) {
|
||||
auto* item = inventoryComponent->FindItemById(tradeItem.itemId);
|
||||
auto item = inventoryComponent->FindItemById(tradeItem.itemId);
|
||||
|
||||
if (item == nullptr) return;
|
||||
|
||||
|
||||
@@ -220,7 +220,7 @@ void UserManager::RequestCharacterList(const SystemAddress& sysAddr) {
|
||||
skillComponent->Reset();
|
||||
}
|
||||
|
||||
Game::entityManager->DestroyEntity(chars[i]->GetEntity());
|
||||
EntityManager::Instance()->DestroyEntity(chars[i]->GetEntity());
|
||||
|
||||
chars[i]->SaveXMLToDatabase();
|
||||
|
||||
|
||||
@@ -39,7 +39,7 @@ void AirMovementBehavior::Sync(BehaviorContext* context, RakNet::BitStream* bitS
|
||||
|
||||
auto* behavior = CreateBehavior(behaviorId);
|
||||
|
||||
if (Game::entityManager->GetEntity(target) != nullptr) {
|
||||
if (EntityManager::Instance()->GetEntity(target) != nullptr) {
|
||||
branch.target = target;
|
||||
}
|
||||
|
||||
|
||||
@@ -4,9 +4,8 @@
|
||||
#include "BehaviorBranchContext.h"
|
||||
#include "BuffComponent.h"
|
||||
|
||||
|
||||
void ApplyBuffBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) {
|
||||
auto* entity = Game::entityManager->GetEntity(branch.target == LWOOBJID_EMPTY ? context->originator : branch.target);
|
||||
auto* entity = EntityManager::Instance()->GetEntity(branch.target == LWOOBJID_EMPTY ? context->originator : branch.target);
|
||||
|
||||
if (entity == nullptr) return;
|
||||
|
||||
@@ -19,7 +18,7 @@ void ApplyBuffBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bitS
|
||||
}
|
||||
|
||||
void ApplyBuffBehavior::UnCast(BehaviorContext* context, BehaviorBranchContext branch) {
|
||||
auto* entity = Game::entityManager->GetEntity(branch.target);
|
||||
auto* entity = EntityManager::Instance()->GetEntity(branch.target);
|
||||
|
||||
if (entity == nullptr) return;
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
#include "dLogger.h"
|
||||
#include "BehaviorBranchContext.h"
|
||||
#include "BehaviorContext.h"
|
||||
#include "RebuildComponent.h"
|
||||
#include "QuickBuildComponent.h"
|
||||
#include "DestroyableComponent.h"
|
||||
#include "Game.h"
|
||||
#include "dLogger.h"
|
||||
@@ -47,7 +47,7 @@ void AreaOfEffectBehavior::Handle(BehaviorContext* context, RakNet::BitStream* b
|
||||
}
|
||||
|
||||
void AreaOfEffectBehavior::Calculate(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) {
|
||||
auto* self = Game::entityManager->GetEntity(context->caster);
|
||||
auto* self = EntityManager::Instance()->GetEntity(context->caster);
|
||||
if (self == nullptr) {
|
||||
Game::logger->Log("AreaOfEffectBehavior", "Invalid self for (%llu)!", context->originator);
|
||||
|
||||
@@ -58,7 +58,7 @@ void AreaOfEffectBehavior::Calculate(BehaviorContext* context, RakNet::BitStream
|
||||
|
||||
std::vector<Entity*> targets;
|
||||
|
||||
auto* presetTarget = Game::entityManager->GetEntity(branch.target);
|
||||
auto* presetTarget = EntityManager::Instance()->GetEntity(branch.target);
|
||||
|
||||
if (presetTarget != nullptr) {
|
||||
if (this->m_radius * this->m_radius >= Vector3::DistanceSquared(reference, presetTarget->GetPosition())) {
|
||||
@@ -75,7 +75,7 @@ void AreaOfEffectBehavior::Calculate(BehaviorContext* context, RakNet::BitStream
|
||||
|
||||
// Gets all of the valid targets, passing in if should target enemies and friends
|
||||
for (auto validTarget : context->GetValidTargets(m_ignoreFaction, includeFaction, m_TargetSelf == 1, m_targetEnemy == 1, m_targetFriend == 1)) {
|
||||
auto* entity = Game::entityManager->GetEntity(validTarget);
|
||||
auto* entity = EntityManager::Instance()->GetEntity(validTarget);
|
||||
|
||||
if (entity == nullptr) {
|
||||
Game::logger->Log("AreaOfEffectBehavior", "Invalid target (%llu) for (%llu)!", validTarget, context->originator);
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
|
||||
void BasicAttackBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) {
|
||||
if (context->unmanaged) {
|
||||
auto* entity = Game::entityManager->GetEntity(branch.target);
|
||||
auto* entity = EntityManager::Instance()->GetEntity(branch.target);
|
||||
|
||||
auto* destroyableComponent = entity->GetComponent<DestroyableComponent>();
|
||||
if (destroyableComponent != nullptr) {
|
||||
@@ -38,7 +38,7 @@ void BasicAttackBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bi
|
||||
}
|
||||
|
||||
void BasicAttackBehavior::DoHandleBehavior(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) {
|
||||
auto* targetEntity = Game::entityManager->GetEntity(branch.target);
|
||||
auto* targetEntity = EntityManager::Instance()->GetEntity(branch.target);
|
||||
if (!targetEntity) {
|
||||
Game::logger->Log("BasicAttackBehavior", "Target targetEntity %llu not found.", branch.target);
|
||||
return;
|
||||
@@ -61,7 +61,7 @@ void BasicAttackBehavior::DoHandleBehavior(BehaviorContext* context, RakNet::Bit
|
||||
|
||||
if (isBlocked) {
|
||||
destroyableComponent->SetAttacksToBlock(std::min(destroyableComponent->GetAttacksToBlock() - 1, 0U));
|
||||
Game::entityManager->SerializeEntity(targetEntity);
|
||||
EntityManager::Instance()->SerializeEntity(targetEntity);
|
||||
this->m_OnFailBlocked->Handle(context, bitStream, branch);
|
||||
return;
|
||||
}
|
||||
@@ -155,14 +155,14 @@ void BasicAttackBehavior::Calculate(BehaviorContext* context, RakNet::BitStream*
|
||||
}
|
||||
|
||||
void BasicAttackBehavior::DoBehaviorCalculation(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) {
|
||||
auto* targetEntity = Game::entityManager->GetEntity(branch.target);
|
||||
auto* targetEntity = EntityManager::Instance()->GetEntity(branch.target);
|
||||
if (!targetEntity) {
|
||||
Game::logger->Log("BasicAttackBehavior", "Target entity %llu is null!", branch.target);
|
||||
return;
|
||||
}
|
||||
|
||||
auto* destroyableComponent = targetEntity->GetComponent<DestroyableComponent>();
|
||||
if (!destroyableComponent || !destroyableComponent->GetParent()) {
|
||||
if (!destroyableComponent || !destroyableComponent->GetParentEntity()) {
|
||||
Game::logger->Log("BasicAttackBehavior", "No destroyable component on %llu", branch.target);
|
||||
return;
|
||||
}
|
||||
@@ -173,7 +173,7 @@ void BasicAttackBehavior::DoBehaviorCalculation(BehaviorContext* context, RakNet
|
||||
|
||||
if (isBlocking) {
|
||||
destroyableComponent->SetAttacksToBlock(destroyableComponent->GetAttacksToBlock() - 1);
|
||||
Game::entityManager->SerializeEntity(targetEntity);
|
||||
EntityManager::Instance()->SerializeEntity(targetEntity);
|
||||
this->m_OnFailBlocked->Calculate(context, bitStream, branch);
|
||||
return;
|
||||
}
|
||||
@@ -213,7 +213,7 @@ void BasicAttackBehavior::DoBehaviorCalculation(BehaviorContext* context, RakNet
|
||||
|
||||
bitStream->Write(armorDamageDealt);
|
||||
bitStream->Write(healthDamageDealt);
|
||||
bitStream->Write(targetEntity->GetIsDead());
|
||||
bitStream->Write(targetEntity->IsDead());
|
||||
}
|
||||
|
||||
bitStream->Write(successState);
|
||||
|
||||
@@ -314,7 +314,7 @@ BehaviorTemplates Behavior::GetBehaviorTemplate(const uint32_t behaviorId) {
|
||||
|
||||
// For use with enemies, to display the correct damage animations on the players
|
||||
void Behavior::PlayFx(std::u16string type, const LWOOBJID target, const LWOOBJID secondary) {
|
||||
auto* targetEntity = Game::entityManager->GetEntity(target);
|
||||
auto* targetEntity = EntityManager::Instance()->GetEntity(target);
|
||||
|
||||
if (targetEntity == nullptr) {
|
||||
return;
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
#include "DestroyableComponent.h"
|
||||
#include "EchoSyncSkill.h"
|
||||
#include "PhantomPhysicsComponent.h"
|
||||
#include "RebuildComponent.h"
|
||||
#include "QuickBuildComponent.h"
|
||||
#include "eReplicaComponentType.h"
|
||||
#include "eConnectionType.h"
|
||||
|
||||
@@ -27,7 +27,7 @@ BehaviorEndEntry::BehaviorEndEntry() {
|
||||
}
|
||||
|
||||
uint32_t BehaviorContext::GetUniqueSkillId() const {
|
||||
auto* entity = Game::entityManager->GetEntity(this->originator);
|
||||
auto* entity = EntityManager::Instance()->GetEntity(this->originator);
|
||||
|
||||
if (entity == nullptr) {
|
||||
Game::logger->Log("BehaviorContext", "Invalid entity for (%llu)!", this->originator);
|
||||
@@ -94,11 +94,11 @@ void BehaviorContext::ScheduleUpdate(const LWOOBJID id) {
|
||||
|
||||
void BehaviorContext::ExecuteUpdates() {
|
||||
for (const auto& id : this->scheduledUpdates) {
|
||||
auto* entity = Game::entityManager->GetEntity(id);
|
||||
auto* entity = EntityManager::Instance()->GetEntity(id);
|
||||
|
||||
if (entity == nullptr) continue;
|
||||
|
||||
Game::entityManager->SerializeEntity(entity);
|
||||
EntityManager::Instance()->SerializeEntity(entity);
|
||||
}
|
||||
|
||||
this->scheduledUpdates.clear();
|
||||
@@ -308,7 +308,7 @@ void BehaviorContext::Reset() {
|
||||
}
|
||||
|
||||
std::vector<LWOOBJID> BehaviorContext::GetValidTargets(int32_t ignoreFaction, int32_t includeFaction, bool targetSelf, bool targetEnemy, bool targetFriend) const {
|
||||
auto* entity = Game::entityManager->GetEntity(this->caster);
|
||||
auto* entity = EntityManager::Instance()->GetEntity(this->caster);
|
||||
|
||||
std::vector<LWOOBJID> targets;
|
||||
|
||||
@@ -320,7 +320,7 @@ std::vector<LWOOBJID> BehaviorContext::GetValidTargets(int32_t ignoreFaction, in
|
||||
|
||||
if (!ignoreFaction && !includeFaction) {
|
||||
for (auto entry : entity->GetTargetsInPhantom()) {
|
||||
auto* instance = Game::entityManager->GetEntity(entry);
|
||||
auto* instance = EntityManager::Instance()->GetEntity(entry);
|
||||
|
||||
if (instance == nullptr) {
|
||||
continue;
|
||||
@@ -331,12 +331,12 @@ std::vector<LWOOBJID> BehaviorContext::GetValidTargets(int32_t ignoreFaction, in
|
||||
}
|
||||
|
||||
if (ignoreFaction || includeFaction || (!entity->HasComponent(eReplicaComponentType::PHANTOM_PHYSICS) && targets.empty())) {
|
||||
DestroyableComponent* destroyableComponent;
|
||||
if (!entity->TryGetComponent(eReplicaComponentType::DESTROYABLE, destroyableComponent)) {
|
||||
auto* destroyableComponent = entity->GetComponent<DestroyableComponent>();
|
||||
if (!destroyableComponent) {
|
||||
return targets;
|
||||
}
|
||||
|
||||
auto entities = Game::entityManager->GetEntitiesByComponent(eReplicaComponentType::CONTROLLABLE_PHYSICS);
|
||||
auto entities = EntityManager::Instance()->GetEntitiesByComponent(eReplicaComponentType::CONTROLLABLE_PHYSICS);
|
||||
for (auto* candidate : entities) {
|
||||
const auto id = candidate->GetObjectID();
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
void BlockBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) {
|
||||
const auto target = context->originator;
|
||||
|
||||
auto* entity = Game::entityManager->GetEntity(target);
|
||||
auto* entity = EntityManager::Instance()->GetEntity(target);
|
||||
|
||||
if (entity == nullptr) {
|
||||
Game::logger->Log("DamageAbsorptionBehavior", "Failed to find target (%llu)!", branch.target);
|
||||
@@ -40,7 +40,7 @@ void BlockBehavior::Calculate(BehaviorContext* context, RakNet::BitStream* bitSt
|
||||
void BlockBehavior::UnCast(BehaviorContext* context, BehaviorBranchContext branch) {
|
||||
const auto target = context->originator;
|
||||
|
||||
auto* entity = Game::entityManager->GetEntity(target);
|
||||
auto* entity = EntityManager::Instance()->GetEntity(target);
|
||||
|
||||
if (entity == nullptr) {
|
||||
Game::logger->Log("DamageAbsorptionBehavior", "Failed to find target (%llu)!", branch.target);
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
void BuffBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) {
|
||||
const auto target = branch.target != LWOOBJID_EMPTY ? branch.target : context->originator;
|
||||
|
||||
auto* entity = Game::entityManager->GetEntity(target);
|
||||
auto* entity = EntityManager::Instance()->GetEntity(target);
|
||||
|
||||
if (entity == nullptr) {
|
||||
Game::logger->Log("BuffBehavior", "Invalid target (%llu)!", target);
|
||||
@@ -30,7 +30,7 @@ void BuffBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bitStream
|
||||
component->SetMaxArmor(component->GetMaxArmor() + this->m_armor);
|
||||
component->SetMaxImagination(component->GetMaxImagination() + this->m_imagination);
|
||||
|
||||
Game::entityManager->SerializeEntity(entity);
|
||||
EntityManager::Instance()->SerializeEntity(entity);
|
||||
|
||||
if (!context->unmanaged) {
|
||||
if (branch.duration > 0) {
|
||||
@@ -44,7 +44,7 @@ void BuffBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bitStream
|
||||
void BuffBehavior::UnCast(BehaviorContext* context, BehaviorBranchContext branch) {
|
||||
const auto target = branch.target != LWOOBJID_EMPTY ? branch.target : context->originator;
|
||||
|
||||
auto* entity = Game::entityManager->GetEntity(target);
|
||||
auto* entity = EntityManager::Instance()->GetEntity(target);
|
||||
|
||||
if (entity == nullptr) {
|
||||
Game::logger->Log("BuffBehavior", "Invalid target (%llu)!", target);
|
||||
@@ -64,7 +64,7 @@ void BuffBehavior::UnCast(BehaviorContext* context, BehaviorBranchContext branch
|
||||
component->SetMaxArmor(component->GetMaxArmor() - this->m_armor);
|
||||
component->SetMaxImagination(component->GetMaxImagination() - this->m_imagination);
|
||||
|
||||
Game::entityManager->SerializeEntity(entity);
|
||||
EntityManager::Instance()->SerializeEntity(entity);
|
||||
}
|
||||
|
||||
void BuffBehavior::Timer(BehaviorContext* context, const BehaviorBranchContext branch, LWOOBJID second) {
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
void CarBoostBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) {
|
||||
GameMessages::SendVehicleAddPassiveBoostAction(branch.target, UNASSIGNED_SYSTEM_ADDRESS);
|
||||
|
||||
auto* entity = Game::entityManager->GetEntity(context->originator);
|
||||
auto* entity = EntityManager::Instance()->GetEntity(context->originator);
|
||||
|
||||
if (entity == nullptr) {
|
||||
return;
|
||||
@@ -22,7 +22,7 @@ void CarBoostBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bitSt
|
||||
auto* possessableComponent = entity->GetComponent<PossessableComponent>();
|
||||
if (possessableComponent != nullptr) {
|
||||
|
||||
auto* possessor = Game::entityManager->GetEntity(possessableComponent->GetPossessor());
|
||||
auto* possessor = EntityManager::Instance()->GetEntity(possessableComponent->GetPossessor());
|
||||
if (possessor != nullptr) {
|
||||
|
||||
auto* characterComponent = possessor->GetComponent<CharacterComponent>();
|
||||
|
||||
@@ -5,14 +5,14 @@
|
||||
|
||||
void ChangeOrientationBehavior::Calculate(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) {
|
||||
Entity* sourceEntity;
|
||||
if (this->m_orientCaster) sourceEntity = Game::entityManager->GetEntity(context->originator);
|
||||
else sourceEntity = Game::entityManager->GetEntity(branch.target);
|
||||
if (this->m_orientCaster) sourceEntity = EntityManager::Instance()->GetEntity(context->originator);
|
||||
else sourceEntity = EntityManager::Instance()->GetEntity(branch.target);
|
||||
if (!sourceEntity) return;
|
||||
|
||||
if (this->m_toTarget) {
|
||||
Entity* destinationEntity;
|
||||
if (this->m_orientCaster) destinationEntity = Game::entityManager->GetEntity(branch.target);
|
||||
else destinationEntity = Game::entityManager->GetEntity(context->originator);
|
||||
if (this->m_orientCaster) destinationEntity = EntityManager::Instance()->GetEntity(branch.target);
|
||||
else destinationEntity = EntityManager::Instance()->GetEntity(context->originator);
|
||||
if (!destinationEntity) return;
|
||||
|
||||
sourceEntity->SetRotation(
|
||||
@@ -23,7 +23,7 @@ void ChangeOrientationBehavior::Calculate(BehaviorContext* context, RakNet::BitS
|
||||
if (this->m_relative) baseAngle += sourceEntity->GetRotation().GetForwardVector();
|
||||
sourceEntity->SetRotation(NiQuaternion::FromEulerAngles(baseAngle));
|
||||
} else return;
|
||||
Game::entityManager->SerializeEntity(sourceEntity);
|
||||
EntityManager::Instance()->SerializeEntity(sourceEntity);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
#include "DestroyableComponent.h"
|
||||
|
||||
void DamageAbsorptionBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bitStream, const BehaviorBranchContext branch) {
|
||||
auto* target = Game::entityManager->GetEntity(branch.target);
|
||||
auto* target = EntityManager::Instance()->GetEntity(branch.target);
|
||||
|
||||
if (target == nullptr) {
|
||||
Game::logger->Log("DamageAbsorptionBehavior", "Failed to find target (%llu)!", branch.target);
|
||||
@@ -34,7 +34,7 @@ void DamageAbsorptionBehavior::Calculate(BehaviorContext* context, RakNet::BitSt
|
||||
}
|
||||
|
||||
void DamageAbsorptionBehavior::Timer(BehaviorContext* context, BehaviorBranchContext branch, const LWOOBJID second) {
|
||||
auto* target = Game::entityManager->GetEntity(second);
|
||||
auto* target = EntityManager::Instance()->GetEntity(second);
|
||||
|
||||
if (target == nullptr) {
|
||||
Game::logger->Log("DamageAbsorptionBehavior", "Failed to find target (%llu)!", second);
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
#include "DestroyableComponent.h"
|
||||
|
||||
void DamageReductionBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bitStream, const BehaviorBranchContext branch) {
|
||||
auto* target = Game::entityManager->GetEntity(branch.target);
|
||||
auto* target = EntityManager::Instance()->GetEntity(branch.target);
|
||||
|
||||
if (target == nullptr) {
|
||||
Game::logger->Log("DamageReductionBehavior", "Failed to find target (%llu)!", branch.target);
|
||||
@@ -32,7 +32,7 @@ void DamageReductionBehavior::Calculate(BehaviorContext* context, RakNet::BitStr
|
||||
}
|
||||
|
||||
void DamageReductionBehavior::Timer(BehaviorContext* context, BehaviorBranchContext branch, const LWOOBJID second) {
|
||||
auto* target = Game::entityManager->GetEntity(second);
|
||||
auto* target = EntityManager::Instance()->GetEntity(second);
|
||||
|
||||
if (target == nullptr) {
|
||||
Game::logger->Log("DamageReductionBehavior", "Failed to find target (%llu)!", second);
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
#include "BehaviorContext.h"
|
||||
|
||||
void DarkInspirationBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bitStream, const BehaviorBranchContext branch) {
|
||||
auto* target = Game::entityManager->GetEntity(branch.target);
|
||||
auto* target = EntityManager::Instance()->GetEntity(branch.target);
|
||||
|
||||
if (target == nullptr) {
|
||||
Game::logger->LogDebug("DarkInspirationBehavior", "Failed to find target (%llu)!", branch.target);
|
||||
@@ -26,7 +26,7 @@ void DarkInspirationBehavior::Handle(BehaviorContext* context, RakNet::BitStream
|
||||
}
|
||||
|
||||
void DarkInspirationBehavior::Calculate(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) {
|
||||
auto* target = Game::entityManager->GetEntity(branch.target);
|
||||
auto* target = EntityManager::Instance()->GetEntity(branch.target);
|
||||
|
||||
if (target == nullptr) {
|
||||
Game::logger->LogDebug("DarkInspirationBehavior", "Failed to find target (%llu)!", branch.target);
|
||||
|
||||
@@ -8,13 +8,13 @@
|
||||
void FallSpeedBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) {
|
||||
// make sure required parameter has non-default value
|
||||
if (m_PercentSlowed == 0.0f) return;
|
||||
auto* target = Game::entityManager->GetEntity(branch.target);
|
||||
auto* target = EntityManager::Instance()->GetEntity(branch.target);
|
||||
if (!target) return;
|
||||
|
||||
auto* controllablePhysicsComponent = target->GetComponent<ControllablePhysicsComponent>();
|
||||
if (!controllablePhysicsComponent) return;
|
||||
controllablePhysicsComponent->SetGravityScale(m_PercentSlowed);
|
||||
Game::entityManager->SerializeEntity(target);
|
||||
EntityManager::Instance()->SerializeEntity(target);
|
||||
|
||||
if (branch.duration > 0.0f) {
|
||||
context->RegisterTimerBehavior(this, branch);
|
||||
@@ -36,13 +36,13 @@ void FallSpeedBehavior::UnCast(BehaviorContext* context, BehaviorBranchContext b
|
||||
}
|
||||
|
||||
void FallSpeedBehavior::End(BehaviorContext* context, BehaviorBranchContext branch, LWOOBJID second) {
|
||||
auto* target = Game::entityManager->GetEntity(branch.target);
|
||||
auto* target = EntityManager::Instance()->GetEntity(branch.target);
|
||||
if (!target) return;
|
||||
|
||||
auto* controllablePhysicsComponent = target->GetComponent<ControllablePhysicsComponent>();
|
||||
if (!controllablePhysicsComponent) return;
|
||||
controllablePhysicsComponent->SetGravityScale(1);
|
||||
Game::entityManager->SerializeEntity(target);
|
||||
EntityManager::Instance()->SerializeEntity(target);
|
||||
}
|
||||
|
||||
void FallSpeedBehavior::Load(){
|
||||
|
||||
@@ -42,7 +42,7 @@ void ForceMovementBehavior::Calculate(BehaviorContext* context, RakNet::BitStrea
|
||||
return;
|
||||
}
|
||||
|
||||
auto* casterEntity = Game::entityManager->GetEntity(context->caster);
|
||||
auto* casterEntity = EntityManager::Instance()->GetEntity(context->caster);
|
||||
if (casterEntity != nullptr) {
|
||||
auto* controllablePhysicsComponent = casterEntity->GetComponent<ControllablePhysicsComponent>();
|
||||
if (controllablePhysicsComponent != nullptr) {
|
||||
@@ -51,7 +51,7 @@ void ForceMovementBehavior::Calculate(BehaviorContext* context, RakNet::BitStrea
|
||||
controllablePhysicsComponent->SetVelocity(controllablePhysicsComponent->GetRotation().GetForwardVector() * 25);
|
||||
}
|
||||
|
||||
Game::entityManager->SerializeEntity(casterEntity);
|
||||
EntityManager::Instance()->SerializeEntity(casterEntity);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,7 +72,7 @@ void ForceMovementBehavior::Load() {
|
||||
}
|
||||
|
||||
void ForceMovementBehavior::SyncCalculation(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) {
|
||||
auto* casterEntity = Game::entityManager->GetEntity(context->caster);
|
||||
auto* casterEntity = EntityManager::Instance()->GetEntity(context->caster);
|
||||
if (casterEntity != nullptr) {
|
||||
auto* controllablePhysicsComponent = casterEntity->GetComponent<ControllablePhysicsComponent>();
|
||||
if (controllablePhysicsComponent != nullptr) {
|
||||
@@ -80,7 +80,7 @@ void ForceMovementBehavior::SyncCalculation(BehaviorContext* context, RakNet::Bi
|
||||
controllablePhysicsComponent->SetPosition(controllablePhysicsComponent->GetPosition() + controllablePhysicsComponent->GetVelocity() * m_Duration);
|
||||
controllablePhysicsComponent->SetVelocity({});
|
||||
|
||||
Game::entityManager->SerializeEntity(casterEntity);
|
||||
EntityManager::Instance()->SerializeEntity(casterEntity);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
|
||||
void HealBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bit_stream, const BehaviorBranchContext branch) {
|
||||
auto* entity = Game::entityManager->GetEntity(branch.target);
|
||||
auto* entity = EntityManager::Instance()->GetEntity(branch.target);
|
||||
|
||||
if (entity == nullptr) {
|
||||
Game::logger->Log("HealBehavior", "Failed to find entity for (%llu)!", branch.target);
|
||||
@@ -16,7 +16,7 @@ void HealBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bit_strea
|
||||
return;
|
||||
}
|
||||
|
||||
auto* destroyable = static_cast<DestroyableComponent*>(entity->GetComponent(eReplicaComponentType::DESTROYABLE));
|
||||
auto* destroyable = entity->GetComponent<DestroyableComponent>();
|
||||
|
||||
if (destroyable == nullptr) {
|
||||
Game::logger->Log("HealBehavior", "Failed to find destroyable component for %(llu)!", branch.target);
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
|
||||
|
||||
void ImaginationBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bit_stream, const BehaviorBranchContext branch) {
|
||||
auto* entity = Game::entityManager->GetEntity(branch.target);
|
||||
auto* entity = EntityManager::Instance()->GetEntity(branch.target);
|
||||
|
||||
if (entity == nullptr) {
|
||||
return;
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
#include "eStateChangeType.h"
|
||||
|
||||
void ImmunityBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bitStream, const BehaviorBranchContext branch) {
|
||||
auto* target = Game::entityManager->GetEntity(branch.target);
|
||||
auto* target = EntityManager::Instance()->GetEntity(branch.target);
|
||||
|
||||
if (!target) {
|
||||
Game::logger->Log("DamageAbsorptionBehavior", "Failed to find target (%llu)!", branch.target);
|
||||
@@ -56,7 +56,7 @@ void ImmunityBehavior::Calculate(BehaviorContext* context, RakNet::BitStream* bi
|
||||
}
|
||||
|
||||
void ImmunityBehavior::Timer(BehaviorContext* context, BehaviorBranchContext branch, const LWOOBJID second) {
|
||||
auto* target = Game::entityManager->GetEntity(second);
|
||||
auto* target = EntityManager::Instance()->GetEntity(second);
|
||||
|
||||
if (!target) {
|
||||
Game::logger->Log("DamageAbsorptionBehavior", "Failed to find target (%llu)!", second);
|
||||
|
||||
@@ -42,7 +42,7 @@ void InterruptBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bitS
|
||||
|
||||
if (branch.target == context->originator) return;
|
||||
|
||||
auto* target = Game::entityManager->GetEntity(branch.target);
|
||||
auto* target = EntityManager::Instance()->GetEntity(branch.target);
|
||||
|
||||
if (target == nullptr) return;
|
||||
|
||||
@@ -67,7 +67,7 @@ void InterruptBehavior::Calculate(BehaviorContext* context, RakNet::BitStream* b
|
||||
|
||||
if (branch.target == context->originator) return;
|
||||
|
||||
auto* target = Game::entityManager->GetEntity(branch.target);
|
||||
auto* target = EntityManager::Instance()->GetEntity(branch.target);
|
||||
|
||||
if (target == nullptr) return;
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
#include "Character.h"
|
||||
|
||||
void JetPackBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bit_stream, const BehaviorBranchContext branch) {
|
||||
auto* entity = Game::entityManager->GetEntity(branch.target);
|
||||
auto* entity = EntityManager::Instance()->GetEntity(branch.target);
|
||||
|
||||
GameMessages::SendSetJetPackMode(entity, true, this->m_BypassChecks, this->m_EnableHover, this->m_effectId, this->m_Airspeed, this->m_MaxAirspeed, this->m_VerticalVelocity, this->m_WarningEffectID);
|
||||
|
||||
@@ -20,7 +20,7 @@ void JetPackBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bit_st
|
||||
}
|
||||
|
||||
void JetPackBehavior::UnCast(BehaviorContext* context, BehaviorBranchContext branch) {
|
||||
auto* entity = Game::entityManager->GetEntity(branch.target);
|
||||
auto* entity = EntityManager::Instance()->GetEntity(branch.target);
|
||||
|
||||
GameMessages::SendSetJetPackMode(entity, false);
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ void KnockbackBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bitS
|
||||
void KnockbackBehavior::Calculate(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) {
|
||||
bool blocked = false;
|
||||
|
||||
auto* target = Game::entityManager->GetEntity(branch.target);
|
||||
auto* target = EntityManager::Instance()->GetEntity(branch.target);
|
||||
|
||||
if (target != nullptr) {
|
||||
auto* destroyableComponent = target->GetComponent<DestroyableComponent>();
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
#include "LootBuffBehavior.h"
|
||||
|
||||
void LootBuffBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) {
|
||||
auto target = Game::entityManager->GetEntity(context->caster);
|
||||
auto target = EntityManager::Instance()->GetEntity(context->caster);
|
||||
if (!target) return;
|
||||
|
||||
auto controllablePhysicsComponent = target->GetComponent<ControllablePhysicsComponent>();
|
||||
auto* controllablePhysicsComponent = target->GetComponent<ControllablePhysicsComponent>();
|
||||
if (!controllablePhysicsComponent) return;
|
||||
|
||||
controllablePhysicsComponent->AddPickupRadiusScale(m_Scale);
|
||||
Game::entityManager->SerializeEntity(target);
|
||||
EntityManager::Instance()->SerializeEntity(target);
|
||||
|
||||
if (branch.duration > 0) context->RegisterTimerBehavior(this, branch);
|
||||
|
||||
@@ -19,14 +19,14 @@ void LootBuffBehavior::Calculate(BehaviorContext* context, RakNet::BitStream* bi
|
||||
}
|
||||
|
||||
void LootBuffBehavior::UnCast(BehaviorContext* context, BehaviorBranchContext branch) {
|
||||
auto target = Game::entityManager->GetEntity(context->caster);
|
||||
auto target = EntityManager::Instance()->GetEntity(context->caster);
|
||||
if (!target) return;
|
||||
|
||||
auto controllablePhysicsComponent = target->GetComponent<ControllablePhysicsComponent>();
|
||||
auto* controllablePhysicsComponent = target->GetComponent<ControllablePhysicsComponent>();
|
||||
if (!controllablePhysicsComponent) return;
|
||||
|
||||
controllablePhysicsComponent->RemovePickupRadiusScale(m_Scale);
|
||||
Game::entityManager->SerializeEntity(target);
|
||||
EntityManager::Instance()->SerializeEntity(target);
|
||||
}
|
||||
|
||||
void LootBuffBehavior::Timer(BehaviorContext* context, BehaviorBranchContext branch, LWOOBJID second) {
|
||||
|
||||
@@ -14,13 +14,13 @@
|
||||
void OverTimeBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) {
|
||||
const auto originator = context->originator;
|
||||
|
||||
auto* entity = Game::entityManager->GetEntity(originator);
|
||||
auto* entity = EntityManager::Instance()->GetEntity(originator);
|
||||
|
||||
if (entity == nullptr) return;
|
||||
|
||||
for (size_t i = 0; i < m_NumIntervals; i++) {
|
||||
entity->AddCallbackTimer((i + 1) * m_Delay, [originator, branch, this]() {
|
||||
auto* entity = Game::entityManager->GetEntity(originator);
|
||||
auto* entity = EntityManager::Instance()->GetEntity(originator);
|
||||
|
||||
if (entity == nullptr) return;
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ void ProjectileAttackBehavior::Handle(BehaviorContext* context, RakNet::BitStrea
|
||||
return;
|
||||
};
|
||||
|
||||
auto* entity = Game::entityManager->GetEntity(context->originator);
|
||||
auto* entity = EntityManager::Instance()->GetEntity(context->originator);
|
||||
|
||||
if (entity == nullptr) {
|
||||
Game::logger->Log("ProjectileAttackBehavior", "Failed to find originator (%llu)!", context->originator);
|
||||
@@ -40,7 +40,7 @@ void ProjectileAttackBehavior::Handle(BehaviorContext* context, RakNet::BitStrea
|
||||
};
|
||||
}
|
||||
|
||||
auto* targetEntity = Game::entityManager->GetEntity(target);
|
||||
auto* targetEntity = EntityManager::Instance()->GetEntity(target);
|
||||
|
||||
for (auto i = 0u; i < this->m_projectileCount; ++i) {
|
||||
LWOOBJID projectileId{};
|
||||
@@ -61,7 +61,7 @@ void ProjectileAttackBehavior::Handle(BehaviorContext* context, RakNet::BitStrea
|
||||
void ProjectileAttackBehavior::Calculate(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) {
|
||||
bitStream->Write(branch.target);
|
||||
|
||||
auto* entity = Game::entityManager->GetEntity(context->originator);
|
||||
auto* entity = EntityManager::Instance()->GetEntity(context->originator);
|
||||
|
||||
if (entity == nullptr) {
|
||||
Game::logger->Log("ProjectileAttackBehavior", "Failed to find originator (%llu)!", context->originator);
|
||||
@@ -78,7 +78,7 @@ void ProjectileAttackBehavior::Calculate(BehaviorContext* context, RakNet::BitSt
|
||||
|
||||
}
|
||||
|
||||
auto* other = Game::entityManager->GetEntity(branch.target);
|
||||
auto* other = EntityManager::Instance()->GetEntity(branch.target);
|
||||
|
||||
if (other == nullptr) {
|
||||
Game::logger->Log("ProjectileAttackBehavior", "Invalid projectile target (%llu)!", branch.target);
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
#include "dZoneManager.h"
|
||||
|
||||
void PropertyTeleportBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) {
|
||||
auto* caster = Game::entityManager->GetEntity(context->caster);
|
||||
auto* caster = EntityManager::Instance()->GetEntity(context->caster);
|
||||
if (!caster) return;
|
||||
|
||||
auto* character = caster->GetCharacter();
|
||||
@@ -23,16 +23,16 @@ void PropertyTeleportBehavior::Handle(BehaviorContext* context, RakNet::BitStrea
|
||||
LWOMAPID targetMapId = m_MapId;
|
||||
LWOCLONEID targetCloneId = character->GetPropertyCloneID();
|
||||
|
||||
if (Game::zoneManager->GetZoneID().GetCloneID() == character->GetPropertyCloneID()) {
|
||||
if (dZoneManager::Instance()->GetZoneID().GetCloneID() == character->GetPropertyCloneID()) {
|
||||
targetMapId = character->GetLastNonInstanceZoneID();
|
||||
targetCloneId = 0;
|
||||
} else {
|
||||
character->SetLastNonInstanceZoneID(Game::zoneManager->GetZoneID().GetMapID());
|
||||
character->SetLastNonInstanceZoneID(dZoneManager::Instance()->GetZoneID().GetMapID());
|
||||
}
|
||||
|
||||
ZoneInstanceManager::Instance()->RequestZoneTransfer(Game::server, targetMapId, targetCloneId, false, [objId](bool mythranShift, uint32_t zoneID, uint32_t zoneInstance, uint32_t zoneClone, std::string serverIP, uint16_t serverPort) {
|
||||
|
||||
auto* entity = Game::entityManager->GetEntity(objId);
|
||||
auto* entity = EntityManager::Instance()->GetEntity(objId);
|
||||
if (!entity) return;
|
||||
|
||||
const auto sysAddr = entity->GetSystemAddress();
|
||||
|
||||
@@ -6,9 +6,9 @@
|
||||
#include "MovementAIComponent.h"
|
||||
|
||||
void PullToPointBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) {
|
||||
auto* entity = Game::entityManager->GetEntity(context->originator);
|
||||
auto* entity = EntityManager::Instance()->GetEntity(context->originator);
|
||||
|
||||
auto* target = Game::entityManager->GetEntity(branch.target);
|
||||
auto* target = EntityManager::Instance()->GetEntity(branch.target);
|
||||
|
||||
if (entity == nullptr || target == nullptr) {
|
||||
return;
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
#include "BuffComponent.h"
|
||||
|
||||
void RemoveBuffBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) {
|
||||
auto* entity = Game::entityManager->GetEntity(context->caster);
|
||||
auto* entity = EntityManager::Instance()->GetEntity(context->caster);
|
||||
if (!entity) return;
|
||||
|
||||
auto* buffComponent = entity->GetComponent<BuffComponent>();
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
#include "eReplicaComponentType.h"
|
||||
|
||||
void RepairBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bit_stream, const BehaviorBranchContext branch) {
|
||||
auto* entity = Game::entityManager->GetEntity(branch.target);
|
||||
auto* entity = EntityManager::Instance()->GetEntity(branch.target);
|
||||
|
||||
if (entity == nullptr) {
|
||||
Game::logger->Log("RepairBehavior", "Failed to find entity for (%llu)!", branch.target);
|
||||
@@ -16,7 +16,7 @@ void RepairBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bit_str
|
||||
return;
|
||||
}
|
||||
|
||||
auto* destroyable = static_cast<DestroyableComponent*>(entity->GetComponent(eReplicaComponentType::DESTROYABLE));
|
||||
auto* destroyable = entity->GetComponent<DestroyableComponent>();
|
||||
|
||||
if (destroyable == nullptr) {
|
||||
Game::logger->Log("RepairBehavior", "Failed to find destroyable component for %(llu)!", branch.target);
|
||||
|
||||
@@ -5,24 +5,20 @@
|
||||
#include "CppScripts.h"
|
||||
|
||||
void SkillEventBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) {
|
||||
auto* target = Game::entityManager->GetEntity(branch.target);
|
||||
auto* caster = Game::entityManager->GetEntity(context->originator);
|
||||
auto* target = EntityManager::Instance()->GetEntity(branch.target);
|
||||
auto* caster = EntityManager::Instance()->GetEntity(context->originator);
|
||||
|
||||
if (caster != nullptr && target != nullptr && this->m_effectHandle != nullptr && !this->m_effectHandle->empty()) {
|
||||
for (CppScripts::Script* script : CppScripts::GetEntityScripts(target)) {
|
||||
script->OnSkillEventFired(target, caster, *this->m_effectHandle);
|
||||
}
|
||||
target->GetScript()->OnSkillEventFired(target, caster, *this->m_effectHandle);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
SkillEventBehavior::Calculate(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) {
|
||||
auto* target = Game::entityManager->GetEntity(branch.target);
|
||||
auto* caster = Game::entityManager->GetEntity(context->originator);
|
||||
auto* target = EntityManager::Instance()->GetEntity(branch.target);
|
||||
auto* caster = EntityManager::Instance()->GetEntity(context->originator);
|
||||
|
||||
if (caster != nullptr && target != nullptr && this->m_effectHandle != nullptr && !this->m_effectHandle->empty()) {
|
||||
for (CppScripts::Script* script : CppScripts::GetEntityScripts(target)) {
|
||||
script->OnSkillEventFired(target, caster, *this->m_effectHandle);
|
||||
}
|
||||
target->GetScript()->OnSkillEventFired(target, caster, *this->m_effectHandle);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,13 +6,13 @@
|
||||
#include "Game.h"
|
||||
#include "dLogger.h"
|
||||
#include "DestroyableComponent.h"
|
||||
#include "RebuildComponent.h"
|
||||
#include "QuickBuildComponent.h"
|
||||
#include "Entity.h"
|
||||
#include "EntityInfo.h"
|
||||
#include "eReplicaComponentType.h"
|
||||
|
||||
void SpawnBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) {
|
||||
auto* origin = Game::entityManager->GetEntity(context->originator);
|
||||
auto* origin = EntityManager::Instance()->GetEntity(context->originator);
|
||||
|
||||
if (origin == nullptr) {
|
||||
Game::logger->Log("SpawnBehavior", "Failed to find self entity (%llu)!", context->originator);
|
||||
@@ -21,7 +21,7 @@ void SpawnBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bitStrea
|
||||
}
|
||||
|
||||
if (branch.isProjectile) {
|
||||
auto* target = Game::entityManager->GetEntity(branch.target);
|
||||
auto* target = EntityManager::Instance()->GetEntity(branch.target);
|
||||
|
||||
if (target != nullptr) {
|
||||
origin = target;
|
||||
@@ -38,10 +38,10 @@ void SpawnBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bitStrea
|
||||
info.spawnerNodeID = 0;
|
||||
info.pos = info.pos + (info.rot.GetForwardVector() * m_Distance);
|
||||
|
||||
auto* entity = Game::entityManager->CreateEntity(
|
||||
auto* entity = EntityManager::Instance()->CreateEntity(
|
||||
info,
|
||||
nullptr,
|
||||
Game::entityManager->GetEntity(context->originator)
|
||||
EntityManager::Instance()->GetEntity(context->originator)
|
||||
);
|
||||
|
||||
if (entity == nullptr) {
|
||||
@@ -53,13 +53,13 @@ void SpawnBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bitStrea
|
||||
entity->SetOwnerOverride(context->originator);
|
||||
|
||||
// Unset the flag to reposition the player, this makes it harder to glitch out of the map
|
||||
auto* rebuildComponent = entity->GetComponent<RebuildComponent>();
|
||||
auto* quickBuildComponent = entity->GetComponent<QuickBuildComponent>();
|
||||
|
||||
if (rebuildComponent != nullptr) {
|
||||
rebuildComponent->SetRepositionPlayer(false);
|
||||
if (quickBuildComponent != nullptr) {
|
||||
quickBuildComponent->SetRepositionPlayer(false);
|
||||
}
|
||||
|
||||
Game::entityManager->ConstructEntity(entity);
|
||||
EntityManager::Instance()->ConstructEntity(entity);
|
||||
|
||||
if (branch.duration > 0) {
|
||||
context->RegisterTimerBehavior(this, branch, entity->GetObjectID());
|
||||
@@ -79,7 +79,7 @@ void SpawnBehavior::Calculate(BehaviorContext* context, RakNet::BitStream* bitSt
|
||||
}
|
||||
|
||||
void SpawnBehavior::Timer(BehaviorContext* context, const BehaviorBranchContext branch, const LWOOBJID second) {
|
||||
auto* entity = Game::entityManager->GetEntity(second);
|
||||
auto* entity = EntityManager::Instance()->GetEntity(second);
|
||||
|
||||
if (entity == nullptr) {
|
||||
Game::logger->Log("SpawnBehavior", "Failed to find spawned entity (%llu)!", second);
|
||||
@@ -87,9 +87,9 @@ void SpawnBehavior::Timer(BehaviorContext* context, const BehaviorBranchContext
|
||||
return;
|
||||
}
|
||||
|
||||
auto* destroyable = static_cast<DestroyableComponent*>(entity->GetComponent(eReplicaComponentType::DESTROYABLE));
|
||||
auto* destroyable = entity->GetComponent<DestroyableComponent>();
|
||||
|
||||
if (destroyable == nullptr) {
|
||||
if (!destroyable) {
|
||||
entity->Smash(context->originator);
|
||||
|
||||
return;
|
||||
|
||||
@@ -9,14 +9,14 @@
|
||||
void SpeedBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) {
|
||||
if (m_AffectsCaster) branch.target = context->caster;
|
||||
|
||||
auto* target = Game::entityManager->GetEntity(branch.target);
|
||||
auto* target = EntityManager::Instance()->GetEntity(branch.target);
|
||||
if (!target) return;
|
||||
|
||||
auto* controllablePhysicsComponent = target->GetComponent<ControllablePhysicsComponent>();
|
||||
if (!controllablePhysicsComponent) return;
|
||||
|
||||
controllablePhysicsComponent->AddSpeedboost(m_RunSpeed);
|
||||
Game::entityManager->SerializeEntity(target);
|
||||
EntityManager::Instance()->SerializeEntity(target);
|
||||
|
||||
if (branch.duration > 0.0f) {
|
||||
context->RegisterTimerBehavior(this, branch);
|
||||
@@ -38,14 +38,14 @@ void SpeedBehavior::Timer(BehaviorContext* context, BehaviorBranchContext branch
|
||||
}
|
||||
|
||||
void SpeedBehavior::End(BehaviorContext* context, BehaviorBranchContext branch, LWOOBJID second) {
|
||||
auto* target = Game::entityManager->GetEntity(branch.target);
|
||||
auto* target = EntityManager::Instance()->GetEntity(branch.target);
|
||||
if (!target) return;
|
||||
|
||||
auto* controllablePhysicsComponent = target->GetComponent<ControllablePhysicsComponent>();
|
||||
if (!controllablePhysicsComponent) return;
|
||||
|
||||
controllablePhysicsComponent->RemoveSpeedboost(m_RunSpeed);
|
||||
Game::entityManager->SerializeEntity(target);
|
||||
EntityManager::Instance()->SerializeEntity(target);
|
||||
}
|
||||
|
||||
void SpeedBehavior::Load() {
|
||||
|
||||
@@ -21,7 +21,7 @@ void StunBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bitStream
|
||||
return;
|
||||
};
|
||||
|
||||
auto* target = Game::entityManager->GetEntity(branch.target);
|
||||
auto* target = EntityManager::Instance()->GetEntity(branch.target);
|
||||
|
||||
if (target == nullptr) {
|
||||
Game::logger->Log("StunBehavior", "Failed to find target (%llu)!", branch.target);
|
||||
@@ -33,7 +33,7 @@ void StunBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bitStream
|
||||
* If our target is an enemy we can go ahead and stun it.
|
||||
*/
|
||||
|
||||
auto* combatAiComponent = static_cast<BaseCombatAIComponent*>(target->GetComponent(eReplicaComponentType::BASE_COMBAT_AI));
|
||||
auto* combatAiComponent = target->GetComponent<BaseCombatAIComponent>();
|
||||
|
||||
if (combatAiComponent == nullptr) {
|
||||
return;
|
||||
@@ -44,7 +44,7 @@ void StunBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bitStream
|
||||
|
||||
void StunBehavior::Calculate(BehaviorContext* context, RakNet::BitStream* bitStream, const BehaviorBranchContext branch) {
|
||||
if (this->m_stunCaster || branch.target == context->originator) {
|
||||
auto* self = Game::entityManager->GetEntity(context->originator);
|
||||
auto* self = EntityManager::Instance()->GetEntity(context->originator);
|
||||
|
||||
if (self == nullptr) {
|
||||
Game::logger->Log("StunBehavior", "Invalid self entity (%llu)!", context->originator);
|
||||
@@ -56,7 +56,7 @@ void StunBehavior::Calculate(BehaviorContext* context, RakNet::BitStream* bitStr
|
||||
* See if we can stun ourselves
|
||||
*/
|
||||
|
||||
auto* combatAiComponent = static_cast<BaseCombatAIComponent*>(self->GetComponent(eReplicaComponentType::BASE_COMBAT_AI));
|
||||
auto* combatAiComponent = self->GetComponent<BaseCombatAIComponent>();
|
||||
|
||||
if (combatAiComponent == nullptr) {
|
||||
return;
|
||||
@@ -69,7 +69,7 @@ void StunBehavior::Calculate(BehaviorContext* context, RakNet::BitStream* bitStr
|
||||
|
||||
bool blocked = false;
|
||||
|
||||
auto* target = Game::entityManager->GetEntity(branch.target);
|
||||
auto* target = EntityManager::Instance()->GetEntity(branch.target);
|
||||
|
||||
if (target != nullptr) {
|
||||
auto* destroyableComponent = target->GetComponent<DestroyableComponent>();
|
||||
@@ -91,7 +91,7 @@ void StunBehavior::Calculate(BehaviorContext* context, RakNet::BitStream* bitStr
|
||||
* If our target is an enemy we can go ahead and stun it.
|
||||
*/
|
||||
|
||||
auto* combatAiComponent = static_cast<BaseCombatAIComponent*>(target->GetComponent(eReplicaComponentType::BASE_COMBAT_AI));
|
||||
auto* combatAiComponent = target->GetComponent<BaseCombatAIComponent>();
|
||||
|
||||
if (combatAiComponent == nullptr) {
|
||||
return;
|
||||
|
||||
@@ -16,7 +16,7 @@ void SwitchBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bitStre
|
||||
};
|
||||
}
|
||||
|
||||
auto* entity = Game::entityManager->GetEntity(context->originator);
|
||||
auto* entity = EntityManager::Instance()->GetEntity(context->originator);
|
||||
|
||||
if (entity == nullptr) {
|
||||
return;
|
||||
@@ -41,7 +41,7 @@ void SwitchBehavior::Calculate(BehaviorContext* context, RakNet::BitStream* bitS
|
||||
auto state = true;
|
||||
|
||||
if (this->m_imagination > 0 || !this->m_isEnemyFaction) {
|
||||
auto* entity = Game::entityManager->GetEntity(branch.target);
|
||||
auto* entity = EntityManager::Instance()->GetEntity(branch.target);
|
||||
|
||||
state = entity != nullptr;
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
#include "BehaviorContext.h"
|
||||
#include "BaseCombatAIComponent.h"
|
||||
#include "EntityManager.h"
|
||||
#include "RebuildComponent.h"
|
||||
#include "QuickBuildComponent.h"
|
||||
#include "DestroyableComponent.h"
|
||||
|
||||
#include <vector>
|
||||
@@ -76,7 +76,7 @@ void TacArcBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bitStre
|
||||
}
|
||||
|
||||
void TacArcBehavior::Calculate(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) {
|
||||
auto* self = Game::entityManager->GetEntity(context->originator);
|
||||
auto* self = EntityManager::Instance()->GetEntity(context->originator);
|
||||
if (self == nullptr) {
|
||||
Game::logger->Log("TacArcBehavior", "Invalid self for (%llu)!", context->originator);
|
||||
return;
|
||||
@@ -85,7 +85,7 @@ void TacArcBehavior::Calculate(BehaviorContext* context, RakNet::BitStream* bitS
|
||||
const auto* destroyableComponent = self->GetComponent<DestroyableComponent>();
|
||||
|
||||
if ((this->m_usePickedTarget || context->clientInitalized) && branch.target > 0) {
|
||||
const auto* target = Game::entityManager->GetEntity(branch.target);
|
||||
const auto* target = EntityManager::Instance()->GetEntity(branch.target);
|
||||
|
||||
if (target == nullptr) {
|
||||
return;
|
||||
@@ -120,7 +120,7 @@ void TacArcBehavior::Calculate(BehaviorContext* context, RakNet::BitStream* bitS
|
||||
// Find all valid targets, based on whether we target enemies or friends
|
||||
for (const auto& contextTarget : context->GetValidTargets()) {
|
||||
if (destroyableComponent != nullptr) {
|
||||
const auto* targetEntity = Game::entityManager->GetEntity(contextTarget);
|
||||
const auto* targetEntity = EntityManager::Instance()->GetEntity(contextTarget);
|
||||
|
||||
if (m_targetEnemy && destroyableComponent->IsEnemy(targetEntity)
|
||||
|| m_targetFriend && destroyableComponent->IsFriend(targetEntity)) {
|
||||
@@ -136,7 +136,7 @@ void TacArcBehavior::Calculate(BehaviorContext* context, RakNet::BitStream* bitS
|
||||
break;
|
||||
}
|
||||
|
||||
auto* entity = Game::entityManager->GetEntity(validTarget);
|
||||
auto* entity = EntityManager::Instance()->GetEntity(validTarget);
|
||||
|
||||
if (entity == nullptr) {
|
||||
Game::logger->Log("TacArcBehavior", "Invalid target (%llu) for (%llu)!", validTarget, context->originator);
|
||||
@@ -148,7 +148,7 @@ void TacArcBehavior::Calculate(BehaviorContext* context, RakNet::BitStream* bitS
|
||||
continue;
|
||||
}
|
||||
|
||||
if (entity->GetIsDead()) continue;
|
||||
if (entity->IsDead()) continue;
|
||||
|
||||
const auto otherPosition = entity->GetPosition();
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
|
||||
|
||||
void TauntBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) {
|
||||
auto* target = Game::entityManager->GetEntity(branch.target);
|
||||
auto* target = EntityManager::Instance()->GetEntity(branch.target);
|
||||
|
||||
if (target == nullptr) {
|
||||
Game::logger->Log("TauntBehavior", "Failed to find target (%llu)!", branch.target);
|
||||
@@ -23,7 +23,7 @@ void TauntBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bitStrea
|
||||
}
|
||||
|
||||
void TauntBehavior::Calculate(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) {
|
||||
auto* target = Game::entityManager->GetEntity(branch.target);
|
||||
auto* target = EntityManager::Instance()->GetEntity(branch.target);
|
||||
|
||||
if (target == nullptr) {
|
||||
Game::logger->Log("TauntBehavior", "Failed to find target (%llu)!", branch.target);
|
||||
|
||||
@@ -5,10 +5,10 @@
|
||||
|
||||
void VentureVisionBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) {
|
||||
|
||||
const auto targetEntity = Game::entityManager->GetEntity(branch.target);
|
||||
const auto targetEntity = EntityManager::Instance()->GetEntity(branch.target);
|
||||
|
||||
if (targetEntity) {
|
||||
auto characterComponent = targetEntity->GetComponent<CharacterComponent>();
|
||||
auto* characterComponent = targetEntity->GetComponent<CharacterComponent>();
|
||||
|
||||
if (characterComponent) {
|
||||
if (m_show_collectibles) characterComponent->AddVentureVisionEffect(m_ShowCollectibles);
|
||||
@@ -21,10 +21,10 @@ void VentureVisionBehavior::Handle(BehaviorContext* context, RakNet::BitStream*
|
||||
}
|
||||
|
||||
void VentureVisionBehavior::UnCast(BehaviorContext* context, BehaviorBranchContext branch) {
|
||||
const auto targetEntity = Game::entityManager->GetEntity(branch.target);
|
||||
const auto targetEntity = EntityManager::Instance()->GetEntity(branch.target);
|
||||
|
||||
if (targetEntity) {
|
||||
auto characterComponent = targetEntity->GetComponent<CharacterComponent>();
|
||||
auto* characterComponent = targetEntity->GetComponent<CharacterComponent>();
|
||||
|
||||
if (characterComponent) {
|
||||
if (m_show_collectibles) characterComponent->RemoveVentureVisionEffect(m_ShowCollectibles);
|
||||
|
||||
@@ -8,14 +8,14 @@
|
||||
|
||||
|
||||
void VerifyBehavior::Calculate(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) {
|
||||
auto* entity = Game::entityManager->GetEntity(branch.target);
|
||||
auto* entity = EntityManager::Instance()->GetEntity(branch.target);
|
||||
|
||||
auto success = true;
|
||||
|
||||
if (entity == nullptr) {
|
||||
success = false;
|
||||
} else if (this->m_rangeCheck) {
|
||||
auto* self = Game::entityManager->GetEntity(context->originator);
|
||||
auto* self = EntityManager::Instance()->GetEntity(context->originator);
|
||||
|
||||
if (self == nullptr) {
|
||||
Game::logger->Log("VerifyBehavior", "Invalid self for (%llu)", context->originator);
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
#include "eMissionTaskType.h"
|
||||
|
||||
#ifndef __ACHIEVEMENTCACHEKEY__H__
|
||||
#define __ACHIEVEMENTCACHEKEY__H__
|
||||
|
||||
#include "eMissionTaskType.h"
|
||||
#include "GeneralUtils.h"
|
||||
|
||||
class AchievementCacheKey {
|
||||
public:
|
||||
AchievementCacheKey() {
|
||||
|
||||
5
dGame/dComponents/AchievementVendorComponent.cpp
Normal file
5
dGame/dComponents/AchievementVendorComponent.cpp
Normal file
@@ -0,0 +1,5 @@
|
||||
#include "AchievementVendorComponent.h"
|
||||
|
||||
AchievementVendorComponent::AchievementVendorComponent(Entity* parent) : VendorComponent(parent) {
|
||||
|
||||
}
|
||||
16
dGame/dComponents/AchievementVendorComponent.h
Normal file
16
dGame/dComponents/AchievementVendorComponent.h
Normal file
@@ -0,0 +1,16 @@
|
||||
#ifndef __ACHIEVEMENTVENDORCOMPONENT__H__
|
||||
#define __ACHIEVEMENTVENDORCOMPONENT__H__
|
||||
|
||||
#include "VendorComponent.h"
|
||||
#include "eReplicaComponentType.h"
|
||||
|
||||
class Entity;
|
||||
|
||||
class AchievementVendorComponent final : public VendorComponent {
|
||||
public:
|
||||
inline static const eReplicaComponentType ComponentType = eReplicaComponentType::ACHIEVEMENT_VENDOR;
|
||||
AchievementVendorComponent(Entity* parent);
|
||||
};
|
||||
|
||||
|
||||
#endif //!__ACHIEVEMENTVENDORCOMPONENT__H__
|
||||
621
dGame/dComponents/ActivityComponent.cpp
Normal file
621
dGame/dComponents/ActivityComponent.cpp
Normal file
@@ -0,0 +1,621 @@
|
||||
#include "ActivityComponent.h"
|
||||
#include "GameMessages.h"
|
||||
#include "CDClientManager.h"
|
||||
#include "MissionComponent.h"
|
||||
#include "Character.h"
|
||||
#include "dZoneManager.h"
|
||||
#include "ZoneInstanceManager.h"
|
||||
#include "Game.h"
|
||||
#include "dLogger.h"
|
||||
#include <WorldPackets.h>
|
||||
#include "EntityManager.h"
|
||||
#include "ChatPackets.h"
|
||||
#include "Player.h"
|
||||
#include "PacketUtils.h"
|
||||
#include "dServer.h"
|
||||
#include "GeneralUtils.h"
|
||||
#include "dZoneManager.h"
|
||||
#include "dConfig.h"
|
||||
#include "InventoryComponent.h"
|
||||
#include "DestroyableComponent.h"
|
||||
#include "Loot.h"
|
||||
#include "eMissionTaskType.h"
|
||||
#include "eMatchUpdate.h"
|
||||
#include "eConnectionType.h"
|
||||
#include "eChatInternalMessageType.h"
|
||||
|
||||
#include "CDCurrencyTableTable.h"
|
||||
#include "CDActivityRewardsTable.h"
|
||||
#include "CDActivitiesTable.h"
|
||||
#include "LeaderboardManager.h"
|
||||
|
||||
ActivityComponent::ActivityComponent(Entity* parent, int32_t componentId) : Component(parent) {
|
||||
m_ActivityID = componentId;
|
||||
CDActivitiesTable* activitiesTable = CDClientManager::Instance().GetTable<CDActivitiesTable>();
|
||||
std::vector<CDActivities> activities = activitiesTable->Query([=](CDActivities entry) {return (entry.ActivityID == m_ActivityID); });
|
||||
|
||||
for (CDActivities activity : activities) {
|
||||
m_ActivityInfo = activity;
|
||||
if (static_cast<LeaderboardType>(activity.leaderboardType) == LeaderboardType::Racing && Game::config->GetValue("solo_racing") == "1") {
|
||||
m_ActivityInfo.minTeamSize = 1;
|
||||
m_ActivityInfo.minTeams = 1;
|
||||
}
|
||||
|
||||
const auto& transferOverride = parent->GetVar<std::u16string>(u"transferZoneID");
|
||||
if (!transferOverride.empty()) {
|
||||
m_ActivityInfo.instanceMapID = std::stoi(GeneralUtils::UTF16ToWTF8(transferOverride));
|
||||
|
||||
// TODO: LU devs made me do it (for some reason cannon cove instancer is marked to go to GF survival)
|
||||
// NOTE: 1301 is GF survival
|
||||
if (m_ActivityInfo.instanceMapID == 1301) {
|
||||
m_ActivityInfo.instanceMapID = 1302;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto* destroyableComponent = m_ParentEntity->GetComponent<DestroyableComponent>();
|
||||
|
||||
if (destroyableComponent) {
|
||||
// check for LMIs and set the loot LMIs
|
||||
CDActivityRewardsTable* activityRewardsTable = CDClientManager::Instance().GetTable<CDActivityRewardsTable>();
|
||||
std::vector<CDActivityRewards> activityRewards = activityRewardsTable->Query([=](CDActivityRewards entry) {return (entry.LootMatrixIndex == destroyableComponent->GetLootMatrixID()); });
|
||||
|
||||
uint32_t startingLMI = 0;
|
||||
|
||||
if (activityRewards.size() > 0) {
|
||||
startingLMI = activityRewards[0].LootMatrixIndex;
|
||||
}
|
||||
|
||||
if (startingLMI > 0) {
|
||||
// now time for bodge :)
|
||||
|
||||
std::vector<CDActivityRewards> objectTemplateActivities = activityRewardsTable->Query([=](CDActivityRewards entry) {return (activityRewards[0].objectTemplate == entry.objectTemplate); });
|
||||
for (const auto& item : objectTemplateActivities) {
|
||||
if (item.activityRating > 0 && item.activityRating < 5) {
|
||||
m_ActivityLootMatrices.insert({ item.activityRating, item.LootMatrixIndex });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ActivityComponent::~ActivityComponent()
|
||||
= default;
|
||||
|
||||
void ActivityComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags) const {
|
||||
outBitStream->Write(true);
|
||||
outBitStream->Write<uint32_t>(m_ActivityPlayers.size());
|
||||
|
||||
if (!m_ActivityPlayers.empty()) {
|
||||
for (const auto& activityPlayer : m_ActivityPlayers) {
|
||||
|
||||
outBitStream->Write<LWOOBJID>(activityPlayer->playerID);
|
||||
for (const auto& activityValue : activityPlayer->values) {
|
||||
outBitStream->Write<float_t>(activityValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ActivityComponent::ReloadConfig() {
|
||||
CDActivitiesTable* activitiesTable = CDClientManager::Instance().GetTable<CDActivitiesTable>();
|
||||
std::vector<CDActivities> activities = activitiesTable->Query([=](CDActivities entry) {return (entry.ActivityID == m_ActivityID); });
|
||||
for (auto activity : activities) {
|
||||
auto mapID = m_ActivityInfo.instanceMapID;
|
||||
if ((mapID == 1203 || mapID == 1261 || mapID == 1303 || mapID == 1403) && Game::config->GetValue("solo_racing") == "1") {
|
||||
m_ActivityInfo.minTeamSize = 1;
|
||||
m_ActivityInfo.minTeams = 1;
|
||||
} else {
|
||||
m_ActivityInfo.minTeamSize = activity.minTeamSize;
|
||||
m_ActivityInfo.minTeams = activity.minTeams;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ActivityComponent::HandleMessageBoxResponse(Entity* player, const std::string& id) {
|
||||
if (m_ActivityInfo.ActivityID == 103) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (id == "LobbyExit") {
|
||||
PlayerLeave(player->GetObjectID());
|
||||
} else if (id == "PlayButton") {
|
||||
PlayerJoin(player);
|
||||
}
|
||||
}
|
||||
|
||||
void ActivityComponent::PlayerJoin(Entity* player) {
|
||||
if (m_ActivityInfo.ActivityID == 103 || PlayerIsInQueue(player) || !IsValidActivity(player)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If we have a lobby, queue the player and allow others to join, otherwise spin up an instance on the spot
|
||||
if (HasLobby()) {
|
||||
PlayerJoinLobby(player);
|
||||
} else if (!IsPlayedBy(player)) {
|
||||
auto* instance = NewInstance();
|
||||
instance->AddParticipant(player);
|
||||
}
|
||||
|
||||
EntityManager::Instance()->SerializeEntity(m_ParentEntity);
|
||||
}
|
||||
|
||||
void ActivityComponent::PlayerJoinLobby(Entity* player) {
|
||||
if (!m_ParentEntity->HasComponent(eReplicaComponentType::QUICK_BUILD))
|
||||
GameMessages::SendMatchResponse(player, player->GetSystemAddress(), 0); // tell the client they joined a lobby
|
||||
LobbyPlayer* newLobbyPlayer = new LobbyPlayer();
|
||||
newLobbyPlayer->entityID = player->GetObjectID();
|
||||
Lobby* playerLobby = nullptr;
|
||||
|
||||
auto* character = player->GetCharacter();
|
||||
if (character != nullptr)
|
||||
character->SetLastNonInstanceZoneID(dZoneManager::Instance()->GetZone()->GetWorldID());
|
||||
|
||||
for (Lobby* lobby : m_Queue) {
|
||||
if (lobby->players.size() < m_ActivityInfo.maxTeamSize || m_ActivityInfo.maxTeamSize == 1 && lobby->players.size() < m_ActivityInfo.maxTeams) {
|
||||
// If an empty slot in an existing lobby is found
|
||||
lobby->players.push_back(newLobbyPlayer);
|
||||
playerLobby = lobby;
|
||||
|
||||
// Update the joining player on players already in the lobby, and update players already in the lobby on the joining player
|
||||
std::string matchUpdateJoined = "player=9:" + std::to_string(player->GetObjectID()) + "\nplayerName=0:" + player->GetCharacter()->GetName();
|
||||
for (LobbyPlayer* joinedPlayer : lobby->players) {
|
||||
auto* entity = joinedPlayer->GetEntity();
|
||||
|
||||
if (entity == nullptr) {
|
||||
continue;
|
||||
}
|
||||
|
||||
std::string matchUpdate = "player=9:" + std::to_string(entity->GetObjectID()) + "\nplayerName=0:" + entity->GetCharacter()->GetName();
|
||||
GameMessages::SendMatchUpdate(player, player->GetSystemAddress(), matchUpdate, eMatchUpdate::PLAYER_ADDED);
|
||||
PlayerReady(entity, joinedPlayer->ready);
|
||||
GameMessages::SendMatchUpdate(entity, entity->GetSystemAddress(), matchUpdateJoined, eMatchUpdate::PLAYER_ADDED);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!playerLobby) {
|
||||
// If all lobbies are full
|
||||
playerLobby = new Lobby();
|
||||
playerLobby->players.push_back(newLobbyPlayer);
|
||||
playerLobby->timer = m_ActivityInfo.waitTime / 1000;
|
||||
m_Queue.push_back(playerLobby);
|
||||
}
|
||||
|
||||
if (m_ActivityInfo.maxTeamSize != 1 && playerLobby->players.size() >= m_ActivityInfo.minTeamSize || m_ActivityInfo.maxTeamSize == 1 && playerLobby->players.size() >= m_ActivityInfo.minTeams) {
|
||||
// Update the joining player on the match timer
|
||||
std::string matchTimerUpdate = "time=3:" + std::to_string(playerLobby->timer);
|
||||
GameMessages::SendMatchUpdate(player, player->GetSystemAddress(), matchTimerUpdate, eMatchUpdate::PHASE_WAIT_READY);
|
||||
}
|
||||
}
|
||||
|
||||
void ActivityComponent::PlayerLeave(LWOOBJID playerID) {
|
||||
|
||||
// Removes the player from a lobby and notifies the others, not applicable for non-lobby instances
|
||||
for (Lobby* lobby : m_Queue) {
|
||||
for (int i = 0; i < lobby->players.size(); ++i) {
|
||||
if (lobby->players[i]->entityID == playerID) {
|
||||
std::string matchUpdateLeft = "player=9:" + std::to_string(playerID);
|
||||
for (LobbyPlayer* lobbyPlayer : lobby->players) {
|
||||
auto* entity = lobbyPlayer->GetEntity();
|
||||
if (entity == nullptr)
|
||||
continue;
|
||||
|
||||
GameMessages::SendMatchUpdate(entity, entity->GetSystemAddress(), matchUpdateLeft, eMatchUpdate::PLAYER_REMOVED);
|
||||
}
|
||||
|
||||
delete lobby->players[i];
|
||||
lobby->players[i] = nullptr;
|
||||
lobby->players.erase(lobby->players.begin() + i);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ActivityComponent::Update(float deltaTime) {
|
||||
std::vector<Lobby*> lobbiesToRemove{};
|
||||
// Ticks all the lobbies, not applicable for non-instance activities
|
||||
for (Lobby* lobby : m_Queue) {
|
||||
for (LobbyPlayer* player : lobby->players) {
|
||||
auto* entity = player->GetEntity();
|
||||
if (entity == nullptr) {
|
||||
PlayerLeave(player->entityID);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (lobby->players.empty()) {
|
||||
lobbiesToRemove.push_back(lobby);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Update the match time for all players
|
||||
if (m_ActivityInfo.maxTeamSize != 1 && lobby->players.size() >= m_ActivityInfo.minTeamSize
|
||||
|| m_ActivityInfo.maxTeamSize == 1 && lobby->players.size() >= m_ActivityInfo.minTeams) {
|
||||
if (lobby->timer == m_ActivityInfo.waitTime / 1000) {
|
||||
for (LobbyPlayer* joinedPlayer : lobby->players) {
|
||||
auto* entity = joinedPlayer->GetEntity();
|
||||
|
||||
if (entity == nullptr)
|
||||
continue;
|
||||
|
||||
std::string matchTimerUpdate = "time=3:" + std::to_string(lobby->timer);
|
||||
GameMessages::SendMatchUpdate(entity, entity->GetSystemAddress(), matchTimerUpdate, eMatchUpdate::PHASE_WAIT_READY);
|
||||
}
|
||||
}
|
||||
|
||||
lobby->timer -= deltaTime;
|
||||
}
|
||||
|
||||
bool lobbyReady = true;
|
||||
for (LobbyPlayer* player : lobby->players) {
|
||||
if (player->ready) continue;
|
||||
lobbyReady = false;
|
||||
}
|
||||
|
||||
// If everyone's ready, jump the timer
|
||||
if (lobbyReady && lobby->timer > m_ActivityInfo.startDelay / 1000) {
|
||||
lobby->timer = m_ActivityInfo.startDelay / 1000;
|
||||
|
||||
// Update players in lobby on switch to start delay
|
||||
std::string matchTimerUpdate = "time=3:" + std::to_string(lobby->timer);
|
||||
for (LobbyPlayer* player : lobby->players) {
|
||||
auto* entity = player->GetEntity();
|
||||
|
||||
if (entity == nullptr)
|
||||
continue;
|
||||
|
||||
GameMessages::SendMatchUpdate(entity, entity->GetSystemAddress(), matchTimerUpdate, eMatchUpdate::PHASE_WAIT_START);
|
||||
}
|
||||
}
|
||||
|
||||
// The timer has elapsed, start the instance
|
||||
if (lobby->timer <= 0.0f) {
|
||||
Game::logger->Log("ActivityComponent", "Setting up instance.");
|
||||
ActivityInstance* instance = NewInstance();
|
||||
LoadPlayersIntoInstance(instance, lobby->players);
|
||||
instance->StartZone();
|
||||
lobbiesToRemove.push_back(lobby);
|
||||
}
|
||||
}
|
||||
|
||||
while (!lobbiesToRemove.empty()) {
|
||||
RemoveLobby(lobbiesToRemove.front());
|
||||
lobbiesToRemove.erase(lobbiesToRemove.begin());
|
||||
}
|
||||
}
|
||||
|
||||
void ActivityComponent::RemoveLobby(Lobby* lobby) {
|
||||
for (int i = 0; i < m_Queue.size(); ++i) {
|
||||
if (m_Queue[i] == lobby) {
|
||||
m_Queue.erase(m_Queue.begin() + i);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool ActivityComponent::HasLobby() const {
|
||||
// If the player is not in the world he has to be, create a lobby for the transfer
|
||||
return m_ActivityInfo.instanceMapID != UINT_MAX && m_ActivityInfo.instanceMapID != Game::server->GetZoneID();
|
||||
}
|
||||
|
||||
bool ActivityComponent::IsValidActivity(Entity* player) {
|
||||
// Makes it so that scripted activities with an unimplemented map cannot be joined
|
||||
/*if (player->GetGMLevel() < eGameMasterLevel::DEVELOPER && (m_ActivityInfo.instanceMapID == 1302 || m_ActivityInfo.instanceMapID == 1301)) {
|
||||
if (m_ParentEntity->GetLOT() == 4860) {
|
||||
auto* missionComponent = player->GetComponent<MissionComponent>();
|
||||
missionComponent->CompleteMission(229);
|
||||
}
|
||||
|
||||
ChatPackets::SendSystemMessage(player->GetSystemAddress(), u"Sorry, this activity is not ready.");
|
||||
static_cast<Player*>(player)->SendToZone(dZoneManager::Instance()->GetZone()->GetWorldID()); // Gets them out of this stuck state
|
||||
|
||||
return false;
|
||||
}*/
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ActivityComponent::PlayerIsInQueue(Entity* player) {
|
||||
for (Lobby* lobby : m_Queue) {
|
||||
for (LobbyPlayer* lobbyPlayer : lobby->players) {
|
||||
if (player->GetObjectID() == lobbyPlayer->entityID) return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ActivityComponent::IsPlayedBy(Entity* player) const {
|
||||
for (const auto* instance : this->m_Instances) {
|
||||
for (const auto* instancePlayer : instance->GetParticipants()) {
|
||||
if (instancePlayer != nullptr && instancePlayer->GetObjectID() == player->GetObjectID())
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ActivityComponent::IsPlayedBy(LWOOBJID playerID) const {
|
||||
for (const auto* instance : this->m_Instances) {
|
||||
for (const auto* instancePlayer : instance->GetParticipants()) {
|
||||
if (instancePlayer != nullptr && instancePlayer->GetObjectID() == playerID)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ActivityComponent::TakeCost(Entity* player) const {
|
||||
if (m_ActivityInfo.optionalCostLOT <= 0 || m_ActivityInfo.optionalCostCount <= 0)
|
||||
return true;
|
||||
|
||||
auto* inventoryComponent = player->GetComponent<InventoryComponent>();
|
||||
if (inventoryComponent == nullptr)
|
||||
return false;
|
||||
|
||||
if (inventoryComponent->GetLotCount(m_ActivityInfo.optionalCostLOT) < m_ActivityInfo.optionalCostCount)
|
||||
return false;
|
||||
|
||||
inventoryComponent->RemoveItem(m_ActivityInfo.optionalCostLOT, m_ActivityInfo.optionalCostCount);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ActivityComponent::PlayerReady(Entity* player, bool bReady) {
|
||||
for (Lobby* lobby : m_Queue) {
|
||||
for (LobbyPlayer* lobbyPlayer : lobby->players) {
|
||||
if (lobbyPlayer->entityID == player->GetObjectID()) {
|
||||
|
||||
lobbyPlayer->ready = bReady;
|
||||
|
||||
// Update players in lobby on player being ready
|
||||
std::string matchReadyUpdate = "player=9:" + std::to_string(player->GetObjectID());
|
||||
eMatchUpdate readyStatus = eMatchUpdate::PLAYER_READY;
|
||||
if (!bReady) readyStatus = eMatchUpdate::PLAYER_NOT_READY;
|
||||
for (LobbyPlayer* otherPlayer : lobby->players) {
|
||||
auto* entity = otherPlayer->GetEntity();
|
||||
if (entity == nullptr)
|
||||
continue;
|
||||
|
||||
GameMessages::SendMatchUpdate(entity, entity->GetSystemAddress(), matchReadyUpdate, readyStatus);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ActivityInstance* ActivityComponent::NewInstance() {
|
||||
auto* instance = new ActivityInstance(m_ParentEntity, m_ActivityInfo);
|
||||
m_Instances.push_back(instance);
|
||||
return instance;
|
||||
}
|
||||
|
||||
void ActivityComponent::LoadPlayersIntoInstance(ActivityInstance* instance, const std::vector<LobbyPlayer*>& lobby) const {
|
||||
for (LobbyPlayer* player : lobby) {
|
||||
auto* entity = player->GetEntity();
|
||||
if (entity == nullptr || !TakeCost(entity)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
instance->AddParticipant(entity);
|
||||
}
|
||||
}
|
||||
|
||||
const std::vector<ActivityInstance*>& ActivityComponent::GetInstances() const {
|
||||
return m_Instances;
|
||||
}
|
||||
|
||||
ActivityInstance* ActivityComponent::GetInstance(const LWOOBJID playerID) {
|
||||
for (const auto* instance : GetInstances()) {
|
||||
for (const auto* participant : instance->GetParticipants()) {
|
||||
if (participant->GetObjectID() == playerID)
|
||||
return const_cast<ActivityInstance*>(instance);
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void ActivityComponent::ClearInstances() {
|
||||
for (ActivityInstance* instance : m_Instances) {
|
||||
delete instance;
|
||||
}
|
||||
m_Instances.clear();
|
||||
}
|
||||
|
||||
ActivityPlayer* ActivityComponent::GetActivityPlayerData(LWOOBJID playerID) {
|
||||
for (auto* activityData : m_ActivityPlayers) {
|
||||
if (activityData->playerID == playerID) {
|
||||
return activityData;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void ActivityComponent::RemoveActivityPlayerData(LWOOBJID playerID) {
|
||||
for (size_t i = 0; i < m_ActivityPlayers.size(); i++) {
|
||||
if (m_ActivityPlayers[i]->playerID == playerID) {
|
||||
delete m_ActivityPlayers[i];
|
||||
m_ActivityPlayers[i] = nullptr;
|
||||
|
||||
m_ActivityPlayers.erase(m_ActivityPlayers.begin() + i);
|
||||
EntityManager::Instance()->SerializeEntity(m_ParentEntity);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ActivityPlayer* ActivityComponent::AddActivityPlayerData(LWOOBJID playerID) {
|
||||
auto* data = GetActivityPlayerData(playerID);
|
||||
if (data != nullptr)
|
||||
return data;
|
||||
|
||||
m_ActivityPlayers.push_back(new ActivityPlayer{ playerID, {} });
|
||||
EntityManager::Instance()->SerializeEntity(m_ParentEntity);
|
||||
|
||||
return GetActivityPlayerData(playerID);
|
||||
}
|
||||
|
||||
float_t ActivityComponent::GetActivityValue(LWOOBJID playerID, uint32_t index) {
|
||||
auto value = -1.0f;
|
||||
|
||||
auto* data = GetActivityPlayerData(playerID);
|
||||
if (data != nullptr) {
|
||||
value = data->values[std::min(index, (uint32_t)9)];
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
void ActivityComponent::SetActivityValue(LWOOBJID playerID, uint32_t index, float_t value) {
|
||||
auto* data = AddActivityPlayerData(playerID);
|
||||
if (data != nullptr) {
|
||||
data->values[std::min(index, (uint32_t)9)] = value;
|
||||
}
|
||||
|
||||
EntityManager::Instance()->SerializeEntity(m_ParentEntity);
|
||||
}
|
||||
|
||||
void ActivityComponent::PlayerRemove(LWOOBJID playerID) {
|
||||
for (auto* instance : GetInstances()) {
|
||||
auto participants = instance->GetParticipants();
|
||||
for (const auto* participant : participants) {
|
||||
if (participant != nullptr && participant->GetObjectID() == playerID) {
|
||||
instance->RemoveParticipant(participant);
|
||||
RemoveActivityPlayerData(playerID);
|
||||
|
||||
// If the instance is empty after the delete of the participant, delete the instance too
|
||||
if (instance->GetParticipants().empty()) {
|
||||
m_Instances.erase(std::find(m_Instances.begin(), m_Instances.end(), instance));
|
||||
delete instance;
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ActivityInstance::StartZone() {
|
||||
if (m_Participants.empty())
|
||||
return;
|
||||
|
||||
const auto& participants = GetParticipants();
|
||||
if (participants.empty())
|
||||
return;
|
||||
|
||||
auto* leader = participants[0];
|
||||
LWOZONEID zoneId = LWOZONEID(m_ActivityInfo.instanceMapID, 0, leader->GetCharacter()->GetPropertyCloneID());
|
||||
|
||||
// only make a team if we have more than one participant
|
||||
if (participants.size() > 1) {
|
||||
CBITSTREAM;
|
||||
PacketUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::CREATE_TEAM);
|
||||
|
||||
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) {
|
||||
const auto objid = player->GetObjectID();
|
||||
ZoneInstanceManager::Instance()->RequestZoneTransfer(Game::server, m_ActivityInfo.instanceMapID, cloneId, false, [objid](bool mythranShift, uint32_t zoneID, uint32_t zoneInstance, uint32_t zoneClone, std::string serverIP, uint16_t serverPort) {
|
||||
|
||||
auto* player = EntityManager::Instance()->GetEntity(objid);
|
||||
if (player == nullptr)
|
||||
return;
|
||||
|
||||
Game::logger->Log("UserManager", "Transferring %s to Zone %i (Instance %i | Clone %i | Mythran Shift: %s) with IP %s and Port %i", player->GetCharacter()->GetName().c_str(), zoneID, zoneInstance, zoneClone, mythranShift == true ? "true" : "false", serverIP.c_str(), serverPort);
|
||||
if (player->GetCharacter()) {
|
||||
player->GetCharacter()->SetZoneID(zoneID);
|
||||
player->GetCharacter()->SetZoneInstance(zoneInstance);
|
||||
player->GetCharacter()->SetZoneClone(zoneClone);
|
||||
}
|
||||
|
||||
WorldPackets::SendTransferToWorld(player->GetSystemAddress(), serverIP, serverPort, mythranShift);
|
||||
return;
|
||||
});
|
||||
}
|
||||
|
||||
m_NextZoneCloneID++;
|
||||
}
|
||||
|
||||
void ActivityInstance::RewardParticipant(Entity* participant) {
|
||||
auto* missionComponent = participant->GetComponent<MissionComponent>();
|
||||
if (missionComponent) {
|
||||
missionComponent->Progress(eMissionTaskType::ACTIVITY, m_ActivityInfo.ActivityID);
|
||||
}
|
||||
|
||||
// First, get the activity data
|
||||
auto* activityRewardsTable = CDClientManager::Instance().GetTable<CDActivityRewardsTable>();
|
||||
std::vector<CDActivityRewards> activityRewards = activityRewardsTable->Query([=](CDActivityRewards entry) { return (entry.objectTemplate == m_ActivityInfo.ActivityID); });
|
||||
|
||||
if (!activityRewards.empty()) {
|
||||
uint32_t minCoins = 0;
|
||||
uint32_t maxCoins = 0;
|
||||
|
||||
auto* currencyTableTable = CDClientManager::Instance().GetTable<CDCurrencyTableTable>();
|
||||
std::vector<CDCurrencyTable> currencyTable = currencyTableTable->Query([=](CDCurrencyTable entry) { return (entry.currencyIndex == activityRewards[0].CurrencyIndex && entry.npcminlevel == 1); });
|
||||
|
||||
if (!currencyTable.empty()) {
|
||||
minCoins = currencyTable[0].minvalue;
|
||||
maxCoins = currencyTable[0].maxvalue;
|
||||
}
|
||||
|
||||
LootGenerator::Instance().DropLoot(participant, m_ParentEntity, activityRewards[0].LootMatrixIndex, minCoins, maxCoins);
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<Entity*> ActivityInstance::GetParticipants() const {
|
||||
std::vector<Entity*> entities;
|
||||
entities.reserve(m_Participants.size());
|
||||
|
||||
for (const auto& id : m_Participants) {
|
||||
auto* entity = EntityManager::Instance()->GetEntity(id);
|
||||
if (entity != nullptr)
|
||||
entities.push_back(entity);
|
||||
}
|
||||
|
||||
return entities;
|
||||
}
|
||||
|
||||
void ActivityInstance::AddParticipant(Entity* participant) {
|
||||
const auto id = participant->GetObjectID();
|
||||
if (std::count(m_Participants.begin(), m_Participants.end(), id))
|
||||
return;
|
||||
|
||||
m_Participants.push_back(id);
|
||||
}
|
||||
|
||||
void ActivityInstance::RemoveParticipant(const Entity* participant) {
|
||||
const auto loadedParticipant = std::find(m_Participants.begin(), m_Participants.end(), participant->GetObjectID());
|
||||
if (loadedParticipant != m_Participants.end()) {
|
||||
m_Participants.erase(loadedParticipant);
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t ActivityInstance::GetScore() const {
|
||||
return score;
|
||||
}
|
||||
|
||||
void ActivityInstance::SetScore(uint32_t score) {
|
||||
this->score = score;
|
||||
}
|
||||
|
||||
Entity* LobbyPlayer::GetEntity() const {
|
||||
return EntityManager::Instance()->GetEntity(entityID);
|
||||
}
|
||||
379
dGame/dComponents/ActivityComponent.h
Normal file
379
dGame/dComponents/ActivityComponent.h
Normal file
@@ -0,0 +1,379 @@
|
||||
/*
|
||||
* Darkflame Universe
|
||||
* Copyright 2018
|
||||
*/
|
||||
|
||||
#include "CDClientManager.h"
|
||||
|
||||
#ifndef ACTIVITYCOMPONENT_H
|
||||
#define ACTIVITYCOMPONENT_H
|
||||
|
||||
#include "BitStream.h"
|
||||
#include "Entity.h"
|
||||
#include "Component.h"
|
||||
#include "eReplicaComponentType.h"
|
||||
|
||||
#include "CDActivitiesTable.h"
|
||||
|
||||
/**
|
||||
* Represents an instance of an activity, having participants and score
|
||||
*/
|
||||
class ActivityInstance {
|
||||
public:
|
||||
ActivityInstance(Entity* parent, CDActivities activityInfo) { m_ParentEntity = parent; m_ActivityInfo = activityInfo; };
|
||||
//~ActivityInstance();
|
||||
|
||||
/**
|
||||
* Adds an entity to this activity
|
||||
* @param participant the entity to add
|
||||
*/
|
||||
void AddParticipant(Entity* participant);
|
||||
|
||||
/**
|
||||
* Removes all the participants from this activity
|
||||
*/
|
||||
void ClearParticipants() { m_Participants.clear(); };
|
||||
|
||||
/**
|
||||
* Starts the instance world for this activity and sends all participants there
|
||||
*/
|
||||
void StartZone();
|
||||
|
||||
/**
|
||||
* Gives the rewards for completing this activity to some participant
|
||||
* @param participant the participant to give rewards to
|
||||
*/
|
||||
void RewardParticipant(Entity* participant);
|
||||
|
||||
/**
|
||||
* Removes a participant from this activity
|
||||
* @param participant the participant to remove
|
||||
*/
|
||||
void RemoveParticipant(const Entity* participant);
|
||||
|
||||
/**
|
||||
* Returns all the participants of this activity
|
||||
* @return all the participants of this activity
|
||||
*/
|
||||
std::vector<Entity*> GetParticipants() const;
|
||||
|
||||
/**
|
||||
* Currently unused
|
||||
*/
|
||||
uint32_t GetScore() const;
|
||||
|
||||
/**
|
||||
* Currently unused
|
||||
*/
|
||||
void SetScore(uint32_t score);
|
||||
private:
|
||||
|
||||
/**
|
||||
* Currently unused
|
||||
*/
|
||||
uint32_t score = 0;
|
||||
|
||||
/**
|
||||
* The instance ID of this activity
|
||||
*/
|
||||
uint32_t m_NextZoneCloneID = 0;
|
||||
|
||||
/**
|
||||
* The database information for this activity
|
||||
*/
|
||||
CDActivities m_ActivityInfo;
|
||||
|
||||
/**
|
||||
* The entity that owns this activity (the entity that has the ActivityComponent)
|
||||
*/
|
||||
Entity* m_ParentEntity;
|
||||
|
||||
/**
|
||||
* All the participants of this activity
|
||||
*/
|
||||
std::vector<LWOOBJID> m_Participants;
|
||||
};
|
||||
|
||||
/**
|
||||
* Represents an entity in a lobby
|
||||
*/
|
||||
struct LobbyPlayer {
|
||||
|
||||
/**
|
||||
* The ID of the entity that is in the lobby
|
||||
*/
|
||||
LWOOBJID entityID;
|
||||
|
||||
/**
|
||||
* Whether or not the entity is ready
|
||||
*/
|
||||
bool ready = false;
|
||||
|
||||
/**
|
||||
* Returns the entity that is in the lobby
|
||||
* @return the entity that is in the lobby
|
||||
*/
|
||||
Entity* GetEntity() const;
|
||||
};
|
||||
|
||||
/**
|
||||
* Represents a lobby of players with a timer until it should start the activity
|
||||
*/
|
||||
struct Lobby {
|
||||
|
||||
/**
|
||||
* The lobby of players
|
||||
*/
|
||||
std::vector<LobbyPlayer*> players;
|
||||
|
||||
/**
|
||||
* The timer that determines when the activity should start
|
||||
*/
|
||||
float timer;
|
||||
};
|
||||
|
||||
/**
|
||||
* Represents the score for the player in an activity, one index might represent score, another one time, etc.
|
||||
*/
|
||||
struct ActivityPlayer {
|
||||
|
||||
/**
|
||||
* The entity that the score is tracked for
|
||||
*/
|
||||
LWOOBJID playerID;
|
||||
|
||||
/**
|
||||
* The list of score for this entity
|
||||
*/
|
||||
float values[10];
|
||||
};
|
||||
|
||||
/**
|
||||
* Welcome to the absolute behemoth that is the activity component. I have now clue how this was managed in
|
||||
* live but I figure somewhat similarly and it's terrible. In a nutshell, this components handles any activity that
|
||||
* can be done in the game from quick builds to boss fights to races. On top of that, this component handles instancing
|
||||
* and lobbying.
|
||||
*/
|
||||
class ActivityComponent : public Component {
|
||||
public:
|
||||
ActivityComponent(Entity* parent, int32_t componentId);
|
||||
~ActivityComponent() override;
|
||||
|
||||
void Update(float deltaTime) override;
|
||||
void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags) const;
|
||||
|
||||
/**
|
||||
* Makes some entity join the minigame, if it's a lobbied one, the entity will be placed in the lobby
|
||||
* @param player the entity to join the game
|
||||
*/
|
||||
void PlayerJoin(Entity* player);
|
||||
|
||||
/**
|
||||
* Makes an entity join the lobby for this minigame, if it exists
|
||||
* @param player the entity to join
|
||||
*/
|
||||
void PlayerJoinLobby(Entity* player);
|
||||
|
||||
/**
|
||||
* Makes the player leave the lobby
|
||||
* @param playerID the entity to leave the lobby
|
||||
*/
|
||||
void PlayerLeave(LWOOBJID playerID);
|
||||
|
||||
/**
|
||||
* Removes the entity from the minigame (and its score)
|
||||
* @param playerID the entity to remove from the minigame
|
||||
*/
|
||||
void PlayerRemove(LWOOBJID playerID);
|
||||
|
||||
/**
|
||||
* Adds all the players to an instance of some activity
|
||||
* @param instance the instance to load the players into
|
||||
* @param lobby the players to load into the instance
|
||||
*/
|
||||
void LoadPlayersIntoInstance(ActivityInstance* instance, const std::vector<LobbyPlayer*>& lobby) const;
|
||||
|
||||
/**
|
||||
* Removes a lobby from the activity manager
|
||||
* @param lobby the lobby to remove
|
||||
*/
|
||||
void RemoveLobby(Lobby* lobby);
|
||||
|
||||
/**
|
||||
* Marks a player as (un)ready in a lobby
|
||||
* @param player the entity to mark
|
||||
* @param bReady true if the entity is ready, false otherwise
|
||||
*/
|
||||
void PlayerReady(Entity* player, bool bReady);
|
||||
|
||||
/**
|
||||
* Returns the ID of this activity
|
||||
* @return the ID of this activity
|
||||
*/
|
||||
int GetActivityID() { return m_ActivityInfo.ActivityID; }
|
||||
|
||||
/**
|
||||
* Returns if this activity has a lobby, e.g. if it needs to instance players to some other map
|
||||
* @return true if this activity has a lobby, false otherwise
|
||||
*/
|
||||
bool HasLobby() const;
|
||||
|
||||
/**
|
||||
* Checks if a player is currently waiting in a lobby
|
||||
* @param player the entity to check for
|
||||
* @return true if the entity is waiting in a lobby, false otherwise
|
||||
*/
|
||||
bool PlayerIsInQueue(Entity* player);
|
||||
|
||||
/**
|
||||
* Checks if an entity is currently playing this activity
|
||||
* @param player the entity to check
|
||||
* @return true if the entity is playing this lobby, false otherwise
|
||||
*/
|
||||
bool IsPlayedBy(Entity* player) const;
|
||||
|
||||
/**
|
||||
* Checks if an entity is currently playing this activity
|
||||
* @param playerID the entity to check
|
||||
* @return true if the entity is playing this lobby, false otherwise
|
||||
*/
|
||||
bool IsPlayedBy(LWOOBJID playerID) const;
|
||||
|
||||
/**
|
||||
* Legacy: used to check for unimplemented maps, gladly, this now just returns true :)
|
||||
*/
|
||||
bool IsValidActivity(Entity* player);
|
||||
|
||||
/**
|
||||
* Removes the cost of the activity (e.g. green imaginate) for the entity that plays this activity
|
||||
* @param player the entity to take cost for
|
||||
* @return true if the cost was successfully deducted, false otherwise
|
||||
*/
|
||||
bool TakeCost(Entity* player) const;
|
||||
|
||||
/**
|
||||
* Handles any response from a player clicking on a lobby / instance menu
|
||||
* @param player the entity that clicked
|
||||
* @param id the message that was passed
|
||||
*/
|
||||
void HandleMessageBoxResponse(Entity* player, const std::string& id);
|
||||
|
||||
/**
|
||||
* Creates a new instance for this activity
|
||||
* @return a new instance for this activity
|
||||
*/
|
||||
ActivityInstance* NewInstance();
|
||||
|
||||
/**
|
||||
* Returns all the currently active instances of this activity
|
||||
* @return all the currently active instances of this activity
|
||||
*/
|
||||
const std::vector<ActivityInstance*>& GetInstances() const;
|
||||
|
||||
/**
|
||||
* Returns the instance that some entity is currently playing in
|
||||
* @param playerID the entity to check for
|
||||
* @return if any, the instance that the entity is currently in
|
||||
*/
|
||||
ActivityInstance* GetInstance(const LWOOBJID playerID);
|
||||
|
||||
/**
|
||||
* @brief Reloads the config settings for this component
|
||||
*
|
||||
*/
|
||||
void ReloadConfig();
|
||||
|
||||
/**
|
||||
* Removes all the instances
|
||||
*/
|
||||
void ClearInstances();
|
||||
|
||||
/**
|
||||
* Returns all the score for the players that are currently playing this activity
|
||||
* @return
|
||||
*/
|
||||
std::vector<ActivityPlayer*> GetActivityPlayers() { return m_ActivityPlayers; };
|
||||
|
||||
/**
|
||||
* Returns activity data for a specific entity (e.g. score and such).
|
||||
* @param playerID the entity to get data for
|
||||
* @return the activity data (score) for the passed player in this activity, if it exists
|
||||
*/
|
||||
ActivityPlayer* GetActivityPlayerData(LWOOBJID playerID);
|
||||
|
||||
/**
|
||||
* Sets some score value for an entity
|
||||
* @param playerID the entity to set score for
|
||||
* @param index the score index to set
|
||||
* @param value the value to set in for that index
|
||||
*/
|
||||
void SetActivityValue(LWOOBJID playerID, uint32_t index, float_t value);
|
||||
|
||||
/**
|
||||
* Returns activity score for the passed parameters
|
||||
* @param playerID the entity to get score for
|
||||
* @param index the index to get score for
|
||||
* @return activity score for the passed parameters
|
||||
*/
|
||||
float_t GetActivityValue(LWOOBJID playerID, uint32_t index);
|
||||
|
||||
/**
|
||||
* Removes activity score tracking for some entity
|
||||
* @param playerID the entity to remove score for
|
||||
*/
|
||||
void RemoveActivityPlayerData(LWOOBJID playerID);
|
||||
|
||||
/**
|
||||
* Adds activity score tracking for some entity
|
||||
* @param playerID the entity to add the activity score for
|
||||
* @return the created entry
|
||||
*/
|
||||
ActivityPlayer* AddActivityPlayerData(LWOOBJID playerID);
|
||||
|
||||
/**
|
||||
* Sets the mapID that this activity points to
|
||||
* @param mapID the map ID to set
|
||||
*/
|
||||
void SetInstanceMapID(uint32_t mapID) { m_ActivityInfo.instanceMapID = mapID; };
|
||||
|
||||
/**
|
||||
* Returns the LMI that this activity points to for a team size
|
||||
* @param teamSize the team size to get the LMI for
|
||||
* @return the LMI that this activity points to for a team size
|
||||
*/
|
||||
uint32_t GetLootMatrixForTeamSize(uint32_t teamSize) { return m_ActivityLootMatrices[teamSize]; }
|
||||
private:
|
||||
|
||||
/**
|
||||
* The database information for this activity
|
||||
*/
|
||||
CDActivities m_ActivityInfo;
|
||||
|
||||
/**
|
||||
* All the active instances of this activity
|
||||
*/
|
||||
std::vector<ActivityInstance*> m_Instances;
|
||||
|
||||
/**
|
||||
* The current lobbies for this activity
|
||||
*/
|
||||
std::vector<Lobby*> m_Queue;
|
||||
|
||||
/**
|
||||
* All the activity score for the players in this activity
|
||||
*/
|
||||
std::vector<ActivityPlayer*> m_ActivityPlayers;
|
||||
|
||||
/**
|
||||
* LMIs for team sizes
|
||||
*/
|
||||
std::unordered_map<uint32_t, uint32_t> m_ActivityLootMatrices;
|
||||
|
||||
/**
|
||||
* The activity id
|
||||
*
|
||||
*/
|
||||
int32_t m_ActivityID;
|
||||
};
|
||||
|
||||
#endif // ACTIVITYCOMPONENT_H
|
||||
@@ -1,6 +1,10 @@
|
||||
#include "BaseCombatAIComponent.h"
|
||||
#include <BitStream.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
|
||||
#include "BitStream.h"
|
||||
#include "Entity.h"
|
||||
#include "EntityManager.h"
|
||||
#include "ControllablePhysicsComponent.h"
|
||||
@@ -15,32 +19,32 @@
|
||||
#include "CDClientManager.h"
|
||||
#include "DestroyableComponent.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
|
||||
#include "SkillComponent.h"
|
||||
#include "RebuildComponent.h"
|
||||
#include "QuickBuildComponent.h"
|
||||
#include "DestroyableComponent.h"
|
||||
#include "Metrics.hpp"
|
||||
#include "CDComponentsRegistryTable.h"
|
||||
#include "CDPhysicsComponentTable.h"
|
||||
|
||||
BaseCombatAIComponent::BaseCombatAIComponent(Entity* parent, const uint32_t id): Component(parent) {
|
||||
BaseCombatAIComponent::BaseCombatAIComponent(Entity* parent, const uint32_t componentId) : Component(parent) {
|
||||
m_Target = LWOOBJID_EMPTY;
|
||||
SetAiState(AiState::spawn);
|
||||
m_ComponentId = componentId;
|
||||
SetAiState(AiState::Spawn);
|
||||
m_Timer = 1.0f;
|
||||
m_StartPosition = parent->GetPosition();
|
||||
m_MovementAI = nullptr;
|
||||
m_Disabled = false;
|
||||
m_SkillEntries = {};
|
||||
m_MovementAI = nullptr;
|
||||
m_SoftTimer = 5.0f;
|
||||
m_dpEntity = nullptr;
|
||||
m_dpEntityEnemy = nullptr;
|
||||
}
|
||||
|
||||
void BaseCombatAIComponent::LoadTemplateData() {
|
||||
//Grab the aggro information from BaseCombatAI:
|
||||
auto componentQuery = CDClientDatabase::CreatePreppedStmt(
|
||||
"SELECT aggroRadius, tetherSpeed, pursuitSpeed, softTetherRadius, hardTetherRadius FROM BaseCombatAIComponent WHERE id = ?;");
|
||||
componentQuery.bind(1, (int)id);
|
||||
componentQuery.bind(1, m_ComponentId);
|
||||
|
||||
auto componentResult = componentQuery.execQuery();
|
||||
|
||||
@@ -63,21 +67,12 @@ BaseCombatAIComponent::BaseCombatAIComponent(Entity* parent, const uint32_t id):
|
||||
|
||||
componentResult.finalize();
|
||||
|
||||
// Get aggro and tether radius from settings and use this if it is present. Only overwrite the
|
||||
// radii if it is greater than the one in the database.
|
||||
if (m_Parent) {
|
||||
auto aggroRadius = m_Parent->GetVar<float>(u"aggroRadius");
|
||||
m_AggroRadius = aggroRadius != 0 ? aggroRadius : m_AggroRadius;
|
||||
auto tetherRadius = m_Parent->GetVar<float>(u"tetherRadius");
|
||||
m_HardTetherRadius = tetherRadius != 0 ? tetherRadius : m_HardTetherRadius;
|
||||
}
|
||||
|
||||
/*
|
||||
* Find skills
|
||||
*/
|
||||
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());
|
||||
skillQuery.bind(1, m_ParentEntity->GetLOT());
|
||||
|
||||
auto result = skillQuery.execQuery();
|
||||
|
||||
@@ -90,44 +85,51 @@ BaseCombatAIComponent::BaseCombatAIComponent(Entity* parent, const uint32_t id):
|
||||
|
||||
auto* behavior = Behavior::CreateBehavior(behaviorId);
|
||||
|
||||
std::stringstream behaviorQuery;
|
||||
|
||||
AiSkillEntry entry = { skillId, 0, abilityCooldown, behavior };
|
||||
|
||||
m_SkillEntries.push_back(entry);
|
||||
m_SkillEntries.push_back(AiSkillEntry(skillId, 0, abilityCooldown, behavior));
|
||||
|
||||
result.nextRow();
|
||||
}
|
||||
}
|
||||
|
||||
void BaseCombatAIComponent::LoadConfigData() {
|
||||
// Get aggro and tether radius from settings and use this if it is present. Only overwrite the
|
||||
// radii if it is greater than the one in the database.
|
||||
if (m_ParentEntity) {
|
||||
auto aggroRadius = m_ParentEntity->GetVar<float>(u"aggroRadius");
|
||||
m_AggroRadius = aggroRadius != 0 ? aggroRadius : m_AggroRadius;
|
||||
auto tetherRadius = m_ParentEntity->GetVar<float>(u"tetherRadius");
|
||||
m_HardTetherRadius = tetherRadius != 0 ? tetherRadius : m_HardTetherRadius;
|
||||
}
|
||||
}
|
||||
|
||||
void BaseCombatAIComponent::Startup() {
|
||||
Stun(1.0f);
|
||||
|
||||
/*
|
||||
* Add physics
|
||||
*/
|
||||
|
||||
// Add physics
|
||||
int32_t collisionGroup = (COLLISION_GROUP_DYNAMIC | COLLISION_GROUP_ENEMY);
|
||||
|
||||
CDComponentsRegistryTable* componentRegistryTable = CDClientManager::Instance().GetTable<CDComponentsRegistryTable>();
|
||||
auto componentID = componentRegistryTable->GetByIDAndType(parent->GetLOT(), eReplicaComponentType::CONTROLLABLE_PHYSICS);
|
||||
auto* componentRegistryTable = CDClientManager::Instance().GetTable<CDComponentsRegistryTable>();
|
||||
if (!componentRegistryTable) return;
|
||||
|
||||
CDPhysicsComponentTable* physicsComponentTable = CDClientManager::Instance().GetTable<CDPhysicsComponentTable>();
|
||||
auto componentID = componentRegistryTable->GetByIDAndType(m_ParentEntity->GetLOT(), eReplicaComponentType::CONTROLLABLE_PHYSICS);
|
||||
|
||||
if (physicsComponentTable != nullptr) {
|
||||
auto* info = physicsComponentTable->GetByID(componentID);
|
||||
if (info != nullptr) {
|
||||
collisionGroup = info->bStatic ? COLLISION_GROUP_NEUTRAL : info->collisionGroup;
|
||||
}
|
||||
}
|
||||
auto* physicsComponentTable = CDClientManager::Instance().GetTable<CDPhysicsComponentTable>();
|
||||
|
||||
if (!physicsComponentTable) return;
|
||||
|
||||
auto* info = physicsComponentTable->GetByID(componentID);
|
||||
if (info) collisionGroup = info->bStatic ? COLLISION_GROUP_NEUTRAL : info->collisionGroup;
|
||||
|
||||
// Why are these new'd here and then deleted by the dpworld??
|
||||
//Create a phantom physics volume so we can detect when we're aggro'd.
|
||||
m_dpEntity = new dpEntity(m_Parent->GetObjectID(), m_AggroRadius);
|
||||
m_dpEntityEnemy = new dpEntity(m_Parent->GetObjectID(), m_AggroRadius, false);
|
||||
m_dpEntity = new dpEntity(m_ParentEntity->GetObjectID(), m_AggroRadius);
|
||||
m_dpEntityEnemy = new dpEntity(m_ParentEntity->GetObjectID(), m_AggroRadius, false);
|
||||
|
||||
m_dpEntity->SetCollisionGroup(collisionGroup);
|
||||
m_dpEntityEnemy->SetCollisionGroup(collisionGroup);
|
||||
|
||||
m_dpEntity->SetPosition(m_Parent->GetPosition());
|
||||
m_dpEntityEnemy->SetPosition(m_Parent->GetPosition());
|
||||
m_dpEntity->SetPosition(m_ParentEntity->GetPosition());
|
||||
m_dpEntityEnemy->SetPosition(m_ParentEntity->GetPosition());
|
||||
|
||||
dpWorld::Instance().AddEntity(m_dpEntity);
|
||||
dpWorld::Instance().AddEntity(m_dpEntityEnemy);
|
||||
@@ -135,28 +137,29 @@ BaseCombatAIComponent::BaseCombatAIComponent(Entity* parent, const uint32_t id):
|
||||
}
|
||||
|
||||
BaseCombatAIComponent::~BaseCombatAIComponent() {
|
||||
if (m_dpEntity)
|
||||
dpWorld::Instance().RemoveEntity(m_dpEntity);
|
||||
if (m_dpEntity) dpWorld::Instance().RemoveEntity(m_dpEntity);
|
||||
|
||||
if (m_dpEntityEnemy)
|
||||
dpWorld::Instance().RemoveEntity(m_dpEntityEnemy);
|
||||
if (m_dpEntityEnemy) dpWorld::Instance().RemoveEntity(m_dpEntityEnemy);
|
||||
m_MovementAI = nullptr;
|
||||
m_dpEntity = nullptr;
|
||||
m_dpEntityEnemy = nullptr;
|
||||
}
|
||||
|
||||
void BaseCombatAIComponent::Update(const float deltaTime) {
|
||||
//First, we need to process physics:
|
||||
if (!m_dpEntity) return;
|
||||
|
||||
m_dpEntity->SetPosition(m_Parent->GetPosition()); //make sure our position is synced with our dpEntity
|
||||
m_dpEntityEnemy->SetPosition(m_Parent->GetPosition());
|
||||
m_dpEntity->SetPosition(m_ParentEntity->GetPosition()); //make sure our position is synced with our dpEntity
|
||||
m_dpEntityEnemy->SetPosition(m_ParentEntity->GetPosition());
|
||||
|
||||
//Process enter events
|
||||
for (auto en : m_dpEntity->GetNewObjects()) {
|
||||
m_Parent->OnCollisionPhantom(en->GetObjectID());
|
||||
m_ParentEntity->OnCollisionPhantom(en->GetObjectID());
|
||||
}
|
||||
|
||||
//Process exit events
|
||||
for (auto en : m_dpEntity->GetRemovedObjects()) {
|
||||
m_Parent->OnCollisionLeavePhantom(en->GetObjectID());
|
||||
m_ParentEntity->OnCollisionLeavePhantom(en->GetObjectID());
|
||||
}
|
||||
|
||||
// Check if we should stop the tether effect
|
||||
@@ -165,39 +168,35 @@ void BaseCombatAIComponent::Update(const float deltaTime) {
|
||||
const auto& info = m_MovementAI->GetInfo();
|
||||
if (m_Target != LWOOBJID_EMPTY || (NiPoint3::DistanceSquared(
|
||||
m_StartPosition,
|
||||
m_Parent->GetPosition()) < 20 * 20 && m_TetherTime <= 0)
|
||||
m_ParentEntity->GetPosition()) < 20 * 20 && m_TetherTime <= 0)
|
||||
) {
|
||||
GameMessages::SendStopFXEffect(m_Parent, true, "tether");
|
||||
GameMessages::SendStopFXEffect(m_ParentEntity, true, "tether");
|
||||
m_TetherEffectActive = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (m_SoftTimer <= 0.0f) {
|
||||
Game::entityManager->SerializeEntity(m_Parent);
|
||||
|
||||
EntityManager::Instance()->SerializeEntity(m_ParentEntity);
|
||||
m_SoftTimer = 5.0f;
|
||||
} else {
|
||||
m_SoftTimer -= deltaTime;
|
||||
}
|
||||
|
||||
if (m_Disabled || m_Parent->GetIsDead())
|
||||
return;
|
||||
if (m_Disabled || m_ParentEntity->IsDead()) return;
|
||||
bool stunnedThisFrame = m_Stunned;
|
||||
CalculateCombat(deltaTime); // Putting this here for now
|
||||
|
||||
if (m_StartPosition == NiPoint3::ZERO) {
|
||||
m_StartPosition = m_Parent->GetPosition();
|
||||
m_StartPosition = m_ParentEntity->GetPosition();
|
||||
}
|
||||
|
||||
m_MovementAI = m_Parent->GetComponent<MovementAIComponent>();
|
||||
|
||||
if (m_MovementAI == nullptr) {
|
||||
return;
|
||||
if (!m_MovementAI) {
|
||||
m_MovementAI = m_ParentEntity->GetComponent<MovementAIComponent>();
|
||||
if (!m_MovementAI) return;
|
||||
}
|
||||
|
||||
if (stunnedThisFrame) {
|
||||
m_MovementAI->Stop();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -207,24 +206,25 @@ void BaseCombatAIComponent::Update(const float deltaTime) {
|
||||
}
|
||||
|
||||
switch (m_State) {
|
||||
case AiState::spawn:
|
||||
case AiState::Spawn:
|
||||
Stun(2.0f);
|
||||
SetAiState(AiState::idle);
|
||||
SetAiState(AiState::Idle);
|
||||
break;
|
||||
|
||||
case AiState::idle:
|
||||
case AiState::Idle:
|
||||
Wander();
|
||||
break;
|
||||
|
||||
case AiState::aggro:
|
||||
case AiState::Aggro:
|
||||
OnAggro();
|
||||
break;
|
||||
|
||||
case AiState::tether:
|
||||
case AiState::Tether:
|
||||
OnTether();
|
||||
break;
|
||||
|
||||
default:
|
||||
Game::logger->Log("BaseCombatAIComponent", "Entity %i is in an invalid state %i", m_ParentEntity->GetLOT(), m_State);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -243,7 +243,7 @@ void BaseCombatAIComponent::CalculateCombat(const float deltaTime) {
|
||||
bool hadRemainingDowntime = m_SkillTime > 0.0f;
|
||||
if (m_SkillTime > 0.0f) m_SkillTime -= deltaTime;
|
||||
|
||||
auto* rebuild = m_Parent->GetComponent<RebuildComponent>();
|
||||
auto* rebuild = m_ParentEntity->GetComponent<QuickBuildComponent>();
|
||||
|
||||
if (rebuild != nullptr) {
|
||||
const auto state = rebuild->GetState();
|
||||
@@ -253,11 +253,9 @@ void BaseCombatAIComponent::CalculateCombat(const float deltaTime) {
|
||||
}
|
||||
}
|
||||
|
||||
auto* skillComponent = m_Parent->GetComponent<SkillComponent>();
|
||||
auto* skillComponent = m_ParentEntity->GetComponent<SkillComponent>();
|
||||
|
||||
if (skillComponent == nullptr) {
|
||||
return;
|
||||
}
|
||||
if (!skillComponent) return;
|
||||
|
||||
skillComponent->CalculateUpdate(deltaTime);
|
||||
|
||||
@@ -287,7 +285,7 @@ void BaseCombatAIComponent::CalculateCombat(const float deltaTime) {
|
||||
}
|
||||
|
||||
if (!m_TetherEffectActive && m_OutOfCombat && (m_OutOfCombatTime -= deltaTime) <= 0) {
|
||||
auto* destroyableComponent = m_Parent->GetComponent<DestroyableComponent>();
|
||||
auto* destroyableComponent = m_ParentEntity->GetComponent<DestroyableComponent>();
|
||||
|
||||
if (destroyableComponent != nullptr && destroyableComponent->HasFaction(4)) {
|
||||
auto serilizationRequired = false;
|
||||
@@ -305,10 +303,10 @@ void BaseCombatAIComponent::CalculateCombat(const float deltaTime) {
|
||||
}
|
||||
|
||||
if (serilizationRequired) {
|
||||
Game::entityManager->SerializeEntity(m_Parent);
|
||||
EntityManager::Instance()->SerializeEntity(m_ParentEntity);
|
||||
}
|
||||
|
||||
GameMessages::SendPlayFXEffect(m_Parent->GetObjectID(), 6270, u"tether", "tether");
|
||||
GameMessages::SendPlayFXEffect(m_ParentEntity->GetObjectID(), 6270, u"tether", "tether");
|
||||
|
||||
m_TetherEffectActive = true;
|
||||
|
||||
@@ -318,7 +316,7 @@ void BaseCombatAIComponent::CalculateCombat(const float deltaTime) {
|
||||
// Speed towards start position
|
||||
if (m_MovementAI != nullptr) {
|
||||
m_MovementAI->SetHaltDistance(0);
|
||||
m_MovementAI->SetMaxSpeed(m_PursuitSpeed);
|
||||
m_MovementAI->SetSpeed(m_PursuitSpeed);
|
||||
m_MovementAI->SetDestination(m_StartPosition);
|
||||
}
|
||||
|
||||
@@ -329,19 +327,19 @@ void BaseCombatAIComponent::CalculateCombat(const float deltaTime) {
|
||||
SetTarget(newTarget);
|
||||
|
||||
if (m_Target != LWOOBJID_EMPTY) {
|
||||
if (m_State == AiState::idle) {
|
||||
if (m_State == AiState::Idle) {
|
||||
m_Timer = 0;
|
||||
}
|
||||
|
||||
SetAiState(AiState::aggro);
|
||||
SetAiState(AiState::Aggro);
|
||||
} else {
|
||||
SetAiState(AiState::idle);
|
||||
SetAiState(AiState::Idle);
|
||||
}
|
||||
|
||||
if (!hasSkillToCast) return;
|
||||
|
||||
if (m_Target == LWOOBJID_EMPTY) {
|
||||
SetAiState(AiState::idle);
|
||||
SetAiState(AiState::Idle);
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -366,7 +364,7 @@ void BaseCombatAIComponent::CalculateCombat(const float deltaTime) {
|
||||
m_MovementAI->Stop();
|
||||
}
|
||||
|
||||
SetAiState(AiState::aggro);
|
||||
SetAiState(AiState::Aggro);
|
||||
|
||||
m_Timer = 0;
|
||||
|
||||
@@ -410,7 +408,7 @@ LWOOBJID BaseCombatAIComponent::FindTarget() {
|
||||
float biggestThreat = 0;
|
||||
|
||||
for (const auto& entry : possibleTargets) {
|
||||
auto* entity = Game::entityManager->GetEntity(entry);
|
||||
auto* entity = EntityManager::Instance()->GetEntity(entry);
|
||||
|
||||
if (entity == nullptr) {
|
||||
continue;
|
||||
@@ -456,7 +454,7 @@ LWOOBJID BaseCombatAIComponent::FindTarget() {
|
||||
std::vector<LWOOBJID> deadThreats{};
|
||||
|
||||
for (const auto& threatTarget : m_ThreatEntries) {
|
||||
auto* entity = Game::entityManager->GetEntity(threatTarget.first);
|
||||
auto* entity = EntityManager::Instance()->GetEntity(threatTarget.first);
|
||||
|
||||
if (entity == nullptr) {
|
||||
deadThreats.push_back(threatTarget.first);
|
||||
@@ -494,10 +492,10 @@ LWOOBJID BaseCombatAIComponent::FindTarget() {
|
||||
std::vector<LWOOBJID> BaseCombatAIComponent::GetTargetWithinAggroRange() const {
|
||||
std::vector<LWOOBJID> targets;
|
||||
|
||||
for (auto id : m_Parent->GetTargetsInPhantom()) {
|
||||
auto* other = Game::entityManager->GetEntity(id);
|
||||
for (auto id : m_ParentEntity->GetTargetsInPhantom()) {
|
||||
auto* other = EntityManager::Instance()->GetEntity(id);
|
||||
|
||||
const auto distance = Vector3::DistanceSquared(m_Parent->GetPosition(), other->GetPosition());
|
||||
const auto distance = Vector3::DistanceSquared(m_ParentEntity->GetPosition(), other->GetPosition());
|
||||
|
||||
if (distance > m_AggroRadius * m_AggroRadius) continue;
|
||||
|
||||
@@ -507,25 +505,12 @@ std::vector<LWOOBJID> BaseCombatAIComponent::GetTargetWithinAggroRange() const {
|
||||
return targets;
|
||||
}
|
||||
|
||||
bool BaseCombatAIComponent::IsMech() {
|
||||
switch (m_Parent->GetLOT()) {
|
||||
case 6253:
|
||||
return true;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
void BaseCombatAIComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags) {
|
||||
outBitStream->Write(m_DirtyStateOrTarget || bIsInitialUpdate);
|
||||
if (m_DirtyStateOrTarget || bIsInitialUpdate) {
|
||||
outBitStream->Write(uint32_t(m_State));
|
||||
outBitStream->Write(m_DirtyStateOrTarget);
|
||||
if (bIsInitialUpdate || m_DirtyStateOrTarget) {
|
||||
outBitStream->Write(m_State);
|
||||
outBitStream->Write(m_Target);
|
||||
m_DirtyStateOrTarget = false;
|
||||
if (!bIsInitialUpdate) m_DirtyStateOrTarget = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -533,13 +518,13 @@ void BaseCombatAIComponent::SetAiState(AiState newState) {
|
||||
if (newState == this->m_State) return;
|
||||
this->m_State = newState;
|
||||
m_DirtyStateOrTarget = true;
|
||||
Game::entityManager->SerializeEntity(m_Parent);
|
||||
EntityManager::Instance()->SerializeEntity(m_ParentEntity);
|
||||
}
|
||||
|
||||
bool BaseCombatAIComponent::IsEnemy(LWOOBJID target) const {
|
||||
auto* entity = Game::entityManager->GetEntity(target);
|
||||
auto* entity = EntityManager::Instance()->GetEntity(target);
|
||||
|
||||
if (entity == nullptr) {
|
||||
if (!entity) {
|
||||
Game::logger->Log("BaseCombatAIComponent", "Invalid entity for checking validity (%llu)!", target);
|
||||
|
||||
return false;
|
||||
@@ -547,21 +532,19 @@ bool BaseCombatAIComponent::IsEnemy(LWOOBJID target) const {
|
||||
|
||||
auto* destroyable = entity->GetComponent<DestroyableComponent>();
|
||||
|
||||
if (destroyable == nullptr) {
|
||||
return false;
|
||||
}
|
||||
if (!destroyable) return false;
|
||||
|
||||
auto* referenceDestroyable = m_Parent->GetComponent<DestroyableComponent>();
|
||||
auto* referenceDestroyable = m_ParentEntity->GetComponent<DestroyableComponent>();
|
||||
|
||||
if (referenceDestroyable == nullptr) {
|
||||
Game::logger->Log("BaseCombatAIComponent", "Invalid reference destroyable component on (%llu)!", m_Parent->GetObjectID());
|
||||
if (!referenceDestroyable) {
|
||||
Game::logger->Log("BaseCombatAIComponent", "Invalid reference destroyable component on (%llu)!", m_ParentEntity->GetObjectID());
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
auto* quickbuild = entity->GetComponent<RebuildComponent>();
|
||||
auto* quickbuild = entity->GetComponent<QuickBuildComponent>();
|
||||
|
||||
if (quickbuild != nullptr) {
|
||||
if (quickbuild) {
|
||||
const auto state = quickbuild->GetState();
|
||||
|
||||
if (state != eRebuildState::COMPLETED) {
|
||||
@@ -573,7 +556,7 @@ bool BaseCombatAIComponent::IsEnemy(LWOOBJID target) const {
|
||||
|
||||
auto candidateList = destroyable->GetFactionIDs();
|
||||
|
||||
for (auto value : candidateList) {
|
||||
for (const auto value : candidateList) {
|
||||
if (std::find(enemyList.begin(), enemyList.end(), value) != enemyList.end()) {
|
||||
return true;
|
||||
}
|
||||
@@ -586,17 +569,16 @@ void BaseCombatAIComponent::SetTarget(const LWOOBJID target) {
|
||||
if (this->m_Target == target) return;
|
||||
m_Target = target;
|
||||
m_DirtyStateOrTarget = true;
|
||||
Game::entityManager->SerializeEntity(m_Parent);
|
||||
EntityManager::Instance()->SerializeEntity(m_ParentEntity);
|
||||
}
|
||||
|
||||
Entity* BaseCombatAIComponent::GetTargetEntity() const {
|
||||
return Game::entityManager->GetEntity(m_Target);
|
||||
return EntityManager::Instance()->GetEntity(m_Target);
|
||||
}
|
||||
|
||||
void BaseCombatAIComponent::Taunt(LWOOBJID offender, float threat) {
|
||||
// Can't taunt self
|
||||
if (offender == m_Parent->GetObjectID())
|
||||
return;
|
||||
if (offender == m_ParentEntity->GetObjectID()) return;
|
||||
|
||||
m_ThreatEntries[offender] += threat;
|
||||
m_DirtyThreat = true;
|
||||
@@ -631,9 +613,7 @@ void BaseCombatAIComponent::ClearThreat() {
|
||||
}
|
||||
|
||||
void BaseCombatAIComponent::Wander() {
|
||||
if (!m_MovementAI->AtFinalWaypoint()) {
|
||||
return;
|
||||
}
|
||||
if (!m_MovementAI->AtFinalWaypoint()) return;
|
||||
|
||||
m_MovementAI->SetHaltDistance(0);
|
||||
|
||||
@@ -658,17 +638,17 @@ void BaseCombatAIComponent::Wander() {
|
||||
destination.y = dpWorld::Instance().GetNavMesh()->GetHeightAtPoint(destination);
|
||||
}
|
||||
|
||||
if (Vector3::DistanceSquared(destination, m_MovementAI->GetParent()->GetPosition()) < 2 * 2) {
|
||||
if (Vector3::DistanceSquared(destination, m_MovementAI->GetCurrentPosition()) < 2 * 2) {
|
||||
m_MovementAI->Stop();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
m_MovementAI->SetMaxSpeed(m_TetherSpeed);
|
||||
m_MovementAI->SetSpeed(m_TetherSpeed);
|
||||
|
||||
m_MovementAI->SetDestination(destination);
|
||||
|
||||
m_Timer += (m_MovementAI->GetParent()->GetPosition().x - destination.x) / m_TetherSpeed;
|
||||
m_Timer += (m_MovementAI->GetCurrentPosition().x - destination.x) / m_TetherSpeed;
|
||||
}
|
||||
|
||||
void BaseCombatAIComponent::OnAggro() {
|
||||
@@ -676,32 +656,30 @@ void BaseCombatAIComponent::OnAggro() {
|
||||
|
||||
auto* target = GetTargetEntity();
|
||||
|
||||
if (target == nullptr) {
|
||||
return;
|
||||
}
|
||||
if (!target) return;
|
||||
|
||||
m_MovementAI->SetHaltDistance(m_AttackRadius);
|
||||
|
||||
NiPoint3 targetPos = target->GetPosition();
|
||||
NiPoint3 currentPos = m_MovementAI->GetParent()->GetPosition();
|
||||
NiPoint3 currentPos = m_MovementAI->GetCurrentPosition();
|
||||
|
||||
// If the player's position is within range, attack
|
||||
if (Vector3::DistanceSquared(currentPos, targetPos) <= m_AttackRadius * m_AttackRadius) {
|
||||
m_MovementAI->Stop();
|
||||
} else if (Vector3::DistanceSquared(m_StartPosition, targetPos) > m_HardTetherRadius * m_HardTetherRadius) //Return to spawn if we're too far
|
||||
{
|
||||
m_MovementAI->SetMaxSpeed(m_PursuitSpeed);
|
||||
m_MovementAI->SetSpeed(m_PursuitSpeed);
|
||||
|
||||
m_MovementAI->SetDestination(m_StartPosition);
|
||||
} else //Chase the player's new position
|
||||
{
|
||||
if (IsMech() && Vector3::DistanceSquared(targetPos, currentPos) > m_AttackRadius * m_AttackRadius * 3 * 3) return;
|
||||
|
||||
m_MovementAI->SetMaxSpeed(m_PursuitSpeed);
|
||||
m_MovementAI->SetSpeed(m_PursuitSpeed);
|
||||
|
||||
m_MovementAI->SetDestination(targetPos);
|
||||
|
||||
SetAiState(AiState::tether);
|
||||
SetAiState(AiState::Tether);
|
||||
}
|
||||
|
||||
m_Timer += 0.5f;
|
||||
@@ -723,15 +701,15 @@ void BaseCombatAIComponent::OnTether() {
|
||||
m_MovementAI->Stop();
|
||||
} else if (Vector3::DistanceSquared(m_StartPosition, targetPos) > m_HardTetherRadius * m_HardTetherRadius) //Return to spawn if we're too far
|
||||
{
|
||||
m_MovementAI->SetMaxSpeed(m_PursuitSpeed);
|
||||
m_MovementAI->SetSpeed(m_PursuitSpeed);
|
||||
|
||||
m_MovementAI->SetDestination(m_StartPosition);
|
||||
|
||||
SetAiState(AiState::aggro);
|
||||
SetAiState(AiState::Aggro);
|
||||
} else {
|
||||
if (IsMech() && Vector3::DistanceSquared(targetPos, currentPos) > m_AttackRadius * m_AttackRadius * 3 * 3) return;
|
||||
|
||||
m_MovementAI->SetMaxSpeed(m_PursuitSpeed);
|
||||
m_MovementAI->SetSpeed(m_PursuitSpeed);
|
||||
|
||||
m_MovementAI->SetDestination(targetPos);
|
||||
}
|
||||
@@ -739,62 +717,18 @@ void BaseCombatAIComponent::OnTether() {
|
||||
m_Timer += 0.5f;
|
||||
}
|
||||
|
||||
bool BaseCombatAIComponent::GetStunned() const {
|
||||
return m_Stunned;
|
||||
}
|
||||
|
||||
void BaseCombatAIComponent::SetStunned(const bool value) {
|
||||
m_Stunned = value;
|
||||
}
|
||||
|
||||
bool BaseCombatAIComponent::GetStunImmune() const {
|
||||
return m_StunImmune;
|
||||
}
|
||||
|
||||
void BaseCombatAIComponent::SetStunImmune(bool value) {
|
||||
m_StunImmune = value;
|
||||
}
|
||||
|
||||
float BaseCombatAIComponent::GetTetherSpeed() const {
|
||||
return m_TetherSpeed;
|
||||
}
|
||||
|
||||
void BaseCombatAIComponent::SetTetherSpeed(float value) {
|
||||
m_TetherSpeed = value;
|
||||
}
|
||||
|
||||
void BaseCombatAIComponent::Stun(const float time) {
|
||||
if (m_StunImmune || m_StunTime > time) {
|
||||
return;
|
||||
}
|
||||
if (m_StunImmune || m_StunTime > time) return;
|
||||
|
||||
m_StunTime = time;
|
||||
|
||||
m_Stunned = true;
|
||||
}
|
||||
|
||||
float BaseCombatAIComponent::GetAggroRadius() const {
|
||||
return m_AggroRadius;
|
||||
}
|
||||
|
||||
void BaseCombatAIComponent::SetAggroRadius(const float value) {
|
||||
m_AggroRadius = value;
|
||||
}
|
||||
|
||||
void BaseCombatAIComponent::LookAt(const NiPoint3& point) {
|
||||
if (m_Stunned) {
|
||||
return;
|
||||
}
|
||||
if (m_Stunned) return;
|
||||
|
||||
m_Parent->SetRotation(NiQuaternion::LookAt(m_Parent->GetPosition(), point));
|
||||
}
|
||||
|
||||
void BaseCombatAIComponent::SetDisabled(bool value) {
|
||||
m_Disabled = value;
|
||||
}
|
||||
|
||||
bool BaseCombatAIComponent::GetDistabled() const {
|
||||
return m_Disabled;
|
||||
m_ParentEntity->SetRotation(NiQuaternion::LookAt(m_ParentEntity->GetPosition(), point));
|
||||
}
|
||||
|
||||
void BaseCombatAIComponent::Sleep() {
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include "Component.h"
|
||||
#include "eReplicaComponentType.h"
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
|
||||
@@ -19,20 +20,25 @@ class Entity;
|
||||
/**
|
||||
* The current state of the AI
|
||||
*/
|
||||
enum class AiState : int {
|
||||
idle = 0, // Doing nothing
|
||||
aggro, // Waiting for an enemy to cross / running back to spawn
|
||||
tether, // Chasing an enemy
|
||||
spawn, // Spawning into the world
|
||||
dead // Killed
|
||||
enum class AiState : int32_t {
|
||||
Idle = 0, // Doing nothing
|
||||
Aggro, // Waiting for an enemy to cross / running back to spawn
|
||||
Tether, // Chasing an enemy
|
||||
Spawn, // Spawning into the world
|
||||
Dead // Killed
|
||||
};
|
||||
|
||||
/**
|
||||
* Represents a skill that can be cast by this enemy, including its cooldowns, which determines how often the skill
|
||||
* may be cast.
|
||||
*/
|
||||
struct AiSkillEntry
|
||||
{
|
||||
struct AiSkillEntry {
|
||||
AiSkillEntry(uint32_t skillId, float cooldown, float abilityCooldown, Behavior* behavior) {
|
||||
this->skillId = skillId;
|
||||
this->cooldown = cooldown;
|
||||
this->abilityCooldown = abilityCooldown;
|
||||
this->behavior = behavior;
|
||||
}
|
||||
uint32_t skillId;
|
||||
|
||||
float cooldown;
|
||||
@@ -45,13 +51,16 @@ struct AiSkillEntry
|
||||
/**
|
||||
* Handles the AI of entities, making them wander, tether and attack their enemies
|
||||
*/
|
||||
class BaseCombatAIComponent : public Component {
|
||||
class BaseCombatAIComponent final : public Component {
|
||||
public:
|
||||
static const eReplicaComponentType ComponentType = eReplicaComponentType::BASE_COMBAT_AI;
|
||||
inline static const eReplicaComponentType ComponentType = eReplicaComponentType::BASE_COMBAT_AI;
|
||||
|
||||
BaseCombatAIComponent(Entity* parentEntity, uint32_t id);
|
||||
~BaseCombatAIComponent() override;
|
||||
|
||||
void LoadTemplateData() override;
|
||||
void LoadConfigData() override;
|
||||
void Startup() override;
|
||||
void Update(float deltaTime) override;
|
||||
void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags);
|
||||
|
||||
@@ -146,37 +155,37 @@ public:
|
||||
* Gets whether or not the entity is currently stunned
|
||||
* @return whether the entity is currently stunned
|
||||
*/
|
||||
bool GetStunned() const;
|
||||
bool GetStunned() const { return m_Stunned; }
|
||||
|
||||
/**
|
||||
* (un)stuns the entity, determining whether it'll be able to attack other entities
|
||||
* @param value whether the enemy is stunned
|
||||
*/
|
||||
void SetStunned(bool value);
|
||||
void SetStunned(bool value) { m_Stunned = value; }
|
||||
|
||||
/**
|
||||
* Gets if this entity may be stunned
|
||||
* @return if this entity may be stunned
|
||||
*/
|
||||
bool GetStunImmune() const;
|
||||
bool GetStunImmune() const { return m_StunImmune; }
|
||||
|
||||
/**
|
||||
* Set the stun immune value, determining if the entity may be stunned
|
||||
* @param value
|
||||
*/
|
||||
void SetStunImmune(bool value);
|
||||
void SetStunImmune(bool value) { m_StunImmune = value; }
|
||||
|
||||
/**
|
||||
* Gets the current speed at which an entity runs when tethering
|
||||
* @return the current speed at which an entity runs when tethering
|
||||
*/
|
||||
float GetTetherSpeed() const;
|
||||
float GetTetherSpeed() const { return m_TetherSpeed; }
|
||||
|
||||
/**
|
||||
* Sets the speed at which an entity will tether
|
||||
* @param value the new tether speed
|
||||
*/
|
||||
void SetTetherSpeed(float value);
|
||||
void SetTetherSpeed(float value) { m_TetherSpeed = value; }
|
||||
|
||||
/**
|
||||
* Stuns the entity for a certain amount of time, will not work if the entity is stun immune
|
||||
@@ -188,13 +197,13 @@ public:
|
||||
* Gets the radius that will cause this entity to get aggro'd, causing a target chase
|
||||
* @return the aggro radius of the entity
|
||||
*/
|
||||
float GetAggroRadius() const;
|
||||
float GetAggroRadius() const { return m_AggroRadius; }
|
||||
|
||||
/**
|
||||
* Sets the aggro radius, causing the entity to start chasing enemies in this range
|
||||
* @param value the aggro radius to set
|
||||
*/
|
||||
void SetAggroRadius(float value);
|
||||
void SetAggroRadius(float value) { m_AggroRadius = value; }
|
||||
|
||||
/**
|
||||
* Makes the entity look at a certain point in space
|
||||
@@ -206,13 +215,13 @@ public:
|
||||
* (dis)ables the AI, causing it to stop/start attacking enemies
|
||||
* @param value
|
||||
*/
|
||||
void SetDisabled(bool value);
|
||||
void SetDisabled(bool value) { m_Disabled = value; }
|
||||
|
||||
/**
|
||||
* Gets the current state of the AI, whether or not it's looking for enemies to attack
|
||||
* @return
|
||||
*/
|
||||
bool GetDistabled() const;
|
||||
bool GetDistabled() const { return m_Disabled; }
|
||||
|
||||
/**
|
||||
* Turns the entity asleep, stopping updates to its physics volumes
|
||||
@@ -386,7 +395,9 @@ private:
|
||||
* Whether the current entity is a mech enemy, needed as mechs tether radius works differently
|
||||
* @return whether this entity is a mech
|
||||
*/
|
||||
bool IsMech();
|
||||
bool IsMech() const { return m_ParentEntity->GetLOT() == 6253; };
|
||||
|
||||
int32_t m_ComponentId;
|
||||
};
|
||||
|
||||
#endif // BASECOMBATAICOMPONENT_H
|
||||
|
||||
@@ -6,91 +6,73 @@
|
||||
#include "Game.h"
|
||||
#include "dLogger.h"
|
||||
#include "GameMessages.h"
|
||||
#include <BitStream.h>
|
||||
#include "BitStream.h"
|
||||
#include "eTriggerEventType.h"
|
||||
|
||||
BouncerComponent::BouncerComponent(Entity* parent) : Component(parent) {
|
||||
m_PetEnabled = false;
|
||||
m_PetBouncerEnabled = false;
|
||||
m_PetSwitchLoaded = false;
|
||||
|
||||
if (parent->GetLOT() == 7625) {
|
||||
LookupPetSwitch();
|
||||
}
|
||||
m_BounceOnCollision = false;
|
||||
m_DirtyBounceInfo = false;
|
||||
}
|
||||
|
||||
BouncerComponent::~BouncerComponent() {
|
||||
void BouncerComponent::Startup() {
|
||||
if (m_ParentEntity->GetLOT() == 7625) LookupPetSwitch();
|
||||
}
|
||||
|
||||
void BouncerComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags) {
|
||||
outBitStream->Write(m_PetEnabled);
|
||||
if (m_PetEnabled) {
|
||||
outBitStream->Write(m_PetBouncerEnabled);
|
||||
outBitStream->Write(bIsInitialUpdate || m_DirtyBounceInfo);
|
||||
if (bIsInitialUpdate || m_DirtyBounceInfo) {
|
||||
outBitStream->Write(m_BounceOnCollision);
|
||||
if (!bIsInitialUpdate) m_DirtyBounceInfo = false;
|
||||
}
|
||||
}
|
||||
|
||||
Entity* BouncerComponent::GetParentEntity() const {
|
||||
return m_Parent;
|
||||
void BouncerComponent::SetBounceOnCollision(bool value) {
|
||||
if (m_BounceOnCollision == value) return;
|
||||
m_BounceOnCollision = value;
|
||||
m_DirtyBounceInfo = true;
|
||||
}
|
||||
|
||||
void BouncerComponent::SetPetEnabled(bool value) {
|
||||
m_PetEnabled = value;
|
||||
void BouncerComponent::SetBouncerEnabled(bool value) {
|
||||
m_BounceOnCollision = value;
|
||||
|
||||
Game::entityManager->SerializeEntity(m_Parent);
|
||||
}
|
||||
GameMessages::SendBouncerActiveStatus(m_ParentEntity->GetObjectID(), value, UNASSIGNED_SYSTEM_ADDRESS);
|
||||
|
||||
void BouncerComponent::SetPetBouncerEnabled(bool value) {
|
||||
m_PetBouncerEnabled = value;
|
||||
|
||||
GameMessages::SendBouncerActiveStatus(m_Parent->GetObjectID(), value, UNASSIGNED_SYSTEM_ADDRESS);
|
||||
|
||||
Game::entityManager->SerializeEntity(m_Parent);
|
||||
EntityManager::Instance()->SerializeEntity(m_ParentEntity);
|
||||
|
||||
if (value) {
|
||||
m_Parent->TriggerEvent(eTriggerEventType::PET_ON_SWITCH, m_Parent);
|
||||
GameMessages::SendPlayFXEffect(m_Parent->GetObjectID(), 1513, u"create", "PetOnSwitch", LWOOBJID_EMPTY, 1, 1, true);
|
||||
m_ParentEntity->TriggerEvent(eTriggerEventType::PET_ON_SWITCH, m_ParentEntity);
|
||||
GameMessages::SendPlayFXEffect(m_ParentEntity->GetObjectID(), 1513, u"create", "PetOnSwitch");
|
||||
} else {
|
||||
m_Parent->TriggerEvent(eTriggerEventType::PET_OFF_SWITCH, m_Parent);
|
||||
GameMessages::SendStopFXEffect(m_Parent, true, "PetOnSwitch");
|
||||
m_ParentEntity->TriggerEvent(eTriggerEventType::PET_OFF_SWITCH, m_ParentEntity);
|
||||
GameMessages::SendStopFXEffect(m_ParentEntity, true, "PetOnSwitch");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
bool BouncerComponent::GetPetEnabled() const {
|
||||
return m_PetEnabled;
|
||||
}
|
||||
|
||||
bool BouncerComponent::GetPetBouncerEnabled() const {
|
||||
return m_PetBouncerEnabled;
|
||||
}
|
||||
|
||||
void BouncerComponent::LookupPetSwitch() {
|
||||
const auto& groups = m_Parent->GetGroups();
|
||||
const auto& groups = m_ParentEntity->GetGroups();
|
||||
|
||||
for (const auto& group : groups) {
|
||||
const auto& entities = Game::entityManager->GetEntitiesInGroup(group);
|
||||
const auto& entities = EntityManager::Instance()->GetEntitiesInGroup(group);
|
||||
|
||||
for (auto* entity : entities) {
|
||||
auto* switchComponent = entity->GetComponent<SwitchComponent>();
|
||||
|
||||
if (switchComponent != nullptr) {
|
||||
switchComponent->SetPetBouncer(this);
|
||||
if (!switchComponent) continue;
|
||||
switchComponent->SetPetBouncer(this);
|
||||
|
||||
m_PetSwitchLoaded = true;
|
||||
m_PetEnabled = true;
|
||||
m_DirtyBounceInfo = true;
|
||||
|
||||
Game::entityManager->SerializeEntity(m_Parent);
|
||||
EntityManager::Instance()->SerializeEntity(m_ParentEntity);
|
||||
|
||||
Game::logger->Log("BouncerComponent", "Loaded pet bouncer");
|
||||
}
|
||||
Game::logger->Log("BouncerComponent", "Loaded bouncer %i:%llu", m_ParentEntity->GetLOT(), m_ParentEntity->GetObjectID());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!m_PetSwitchLoaded) {
|
||||
Game::logger->Log("BouncerComponent", "Failed to load pet bouncer");
|
||||
float retryTime = 0.5f;
|
||||
Game::logger->Log("BouncerComponent", "Failed to load pet bouncer for %i:%llu, trying again in %f seconds", m_ParentEntity->GetLOT(), m_ParentEntity->GetObjectID(), retryTime);
|
||||
|
||||
m_Parent->AddCallbackTimer(0.5f, [this]() {
|
||||
LookupPetSwitch();
|
||||
});
|
||||
}
|
||||
m_ParentEntity->AddCallbackTimer(retryTime, [this]() {
|
||||
LookupPetSwitch();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -10,41 +10,27 @@
|
||||
/**
|
||||
* Attached to bouncer entities, allowing other entities to bounce off of it
|
||||
*/
|
||||
class BouncerComponent : public Component {
|
||||
class BouncerComponent final : public Component {
|
||||
public:
|
||||
static const eReplicaComponentType ComponentType = eReplicaComponentType::BOUNCER;
|
||||
inline static const eReplicaComponentType ComponentType = eReplicaComponentType::BOUNCER;
|
||||
|
||||
BouncerComponent(Entity* parentEntity);
|
||||
~BouncerComponent() override;
|
||||
|
||||
void Startup() override;
|
||||
void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags);
|
||||
|
||||
Entity* GetParentEntity() const;
|
||||
|
||||
/**
|
||||
* Sets whether or not this bouncer needs to be activated by a pet
|
||||
* @param value whether or not this bouncer needs to be activated by a pet
|
||||
*/
|
||||
void SetPetEnabled(bool value);
|
||||
void SetBounceOnCollision(bool value);
|
||||
|
||||
/**
|
||||
* Sets whether or not this bouncer is currently being activated by a pet, allowing entities to bounce off of it,
|
||||
* also displays FX accordingly.
|
||||
* @param value whether or not this bouncer is activated by a pet
|
||||
*/
|
||||
void SetPetBouncerEnabled(bool value);
|
||||
|
||||
/**
|
||||
* Gets whether this bouncer should be enabled using pets
|
||||
* @return whether this bouncer should be enabled using pets
|
||||
*/
|
||||
bool GetPetEnabled() const;
|
||||
|
||||
/**
|
||||
* Gets whether this bouncer is currently activated by a pet
|
||||
* @return whether this bouncer is currently activated by a pet
|
||||
*/
|
||||
bool GetPetBouncerEnabled() const;
|
||||
void SetBouncerEnabled(bool value);
|
||||
|
||||
/**
|
||||
* Finds the switch used to activate this bouncer if its pet-enabled and stores this components' state there
|
||||
@@ -52,20 +38,12 @@ public:
|
||||
void LookupPetSwitch();
|
||||
|
||||
private:
|
||||
/**
|
||||
* Whether this bouncer needs to be activated by a pet
|
||||
*/
|
||||
bool m_PetEnabled;
|
||||
|
||||
/**
|
||||
* Whether this bouncer is currently being activated by a pet
|
||||
*/
|
||||
bool m_PetBouncerEnabled;
|
||||
bool m_BounceOnCollision;
|
||||
|
||||
/**
|
||||
* Whether the pet switch for this bouncer has been located
|
||||
*/
|
||||
bool m_PetSwitchLoaded;
|
||||
bool m_DirtyBounceInfo;
|
||||
};
|
||||
|
||||
#endif // BOUNCERCOMPONENT_H
|
||||
|
||||
@@ -14,20 +14,12 @@
|
||||
|
||||
std::unordered_map<int32_t, std::vector<BuffParameter>> BuffComponent::m_Cache{};
|
||||
|
||||
BuffComponent::BuffComponent(Entity* parent) : Component(parent) {
|
||||
}
|
||||
|
||||
BuffComponent::~BuffComponent() {
|
||||
}
|
||||
|
||||
void BuffComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags) {
|
||||
if (!bIsInitialUpdate) return;
|
||||
if (m_Buffs.empty()) {
|
||||
outBitStream->Write0();
|
||||
} else {
|
||||
outBitStream->Write1();
|
||||
outBitStream->Write<uint32_t>(m_Buffs.size());
|
||||
|
||||
outBitStream->Write(!m_Buffs.empty());
|
||||
if (!m_Buffs.empty()) {
|
||||
outBitStream->Write<uint32_t>(m_Buffs.size());
|
||||
for (const auto& buff : m_Buffs) {
|
||||
outBitStream->Write<uint32_t>(buff.first);
|
||||
outBitStream->Write0();
|
||||
@@ -47,7 +39,7 @@ void BuffComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUp
|
||||
}
|
||||
}
|
||||
|
||||
outBitStream->Write0();
|
||||
outBitStream->Write0(); // immunities
|
||||
}
|
||||
|
||||
void BuffComponent::Update(float deltaTime) {
|
||||
@@ -55,30 +47,28 @@ void BuffComponent::Update(float deltaTime) {
|
||||
* Loop through all buffs and apply deltaTime to ther time.
|
||||
* If they have expired, remove the buff and break.
|
||||
*/
|
||||
for (auto& buff : m_Buffs) {
|
||||
for (auto& [buffId, buffInfo] : m_Buffs) {
|
||||
// For damage buffs
|
||||
if (buff.second.tick != 0.0f && buff.second.stacks > 0) {
|
||||
buff.second.tickTime -= deltaTime;
|
||||
if (buffInfo.tick != 0.0f && buffInfo.stacks > 0) {
|
||||
buffInfo.tickTime -= deltaTime;
|
||||
|
||||
if (buff.second.tickTime <= 0.0f) {
|
||||
buff.second.tickTime = buff.second.tick;
|
||||
buff.second.stacks--;
|
||||
if (buffInfo.tickTime <= 0.0f) {
|
||||
buffInfo.tickTime = buffInfo.tick;
|
||||
buffInfo.stacks--;
|
||||
|
||||
SkillComponent::HandleUnmanaged(buff.second.behaviorID, m_Parent->GetObjectID(), buff.second.source);
|
||||
SkillComponent::HandleUnmanaged(buffInfo.behaviorID, m_ParentEntity->GetObjectID(), buffInfo.source);
|
||||
}
|
||||
}
|
||||
|
||||
// These are indefinate buffs, don't update them.
|
||||
if (buff.second.time == 0.0f) {
|
||||
continue;
|
||||
}
|
||||
if (buffInfo.time == 0.0f) continue;
|
||||
|
||||
buff.second.time -= deltaTime;
|
||||
buffInfo.time -= deltaTime;
|
||||
|
||||
if (buff.second.time <= 0.0f) {
|
||||
RemoveBuff(buff.first);
|
||||
if (buffInfo.time <= 0.0f) {
|
||||
RemoveBuff(buffId);
|
||||
|
||||
break;
|
||||
break; // Break because we modified or may modify the map.
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -87,11 +77,9 @@ void BuffComponent::ApplyBuff(const int32_t id, const float duration, const LWOO
|
||||
bool cancelOnDamaged, bool cancelOnDeath, bool cancelOnLogout, bool cancelOnRemoveBuff,
|
||||
bool cancelOnUi, bool cancelOnUnequip, bool cancelOnZone) {
|
||||
// Prevent buffs from stacking.
|
||||
if (HasBuff(id)) {
|
||||
return;
|
||||
}
|
||||
if (HasBuff(id)) return;
|
||||
|
||||
GameMessages::SendAddBuff(const_cast<LWOOBJID&>(m_Parent->GetObjectID()), source, (uint32_t)id,
|
||||
GameMessages::SendAddBuff(m_ParentEntity->GetObjectID(), source, (uint32_t)id,
|
||||
(uint32_t)duration * 1000, addImmunity, cancelOnDamaged, cancelOnDeath,
|
||||
cancelOnLogout, cancelOnRemoveBuff, cancelOnUi, cancelOnUnequip, cancelOnZone);
|
||||
|
||||
@@ -100,14 +88,14 @@ void BuffComponent::ApplyBuff(const int32_t id, const float duration, const LWOO
|
||||
int32_t behaviorID = 0;
|
||||
|
||||
const auto& parameters = GetBuffParameters(id);
|
||||
auto* skillBehaviorTable = CDClientManager::Instance().GetTable<CDSkillBehaviorTable>();
|
||||
for (const auto& parameter : parameters) {
|
||||
if (parameter.name == "overtime") {
|
||||
auto* behaviorTemplateTable = CDClientManager::Instance().GetTable<CDSkillBehaviorTable>();
|
||||
|
||||
behaviorID = behaviorTemplateTable->GetSkillByID(parameter.values[0]).behaviorID;
|
||||
stacks = static_cast<int32_t>(parameter.values[1]);
|
||||
tick = parameter.values[2];
|
||||
const auto unknown2 = parameter.values[3]; // Always 0
|
||||
behaviorID = skillBehaviorTable->GetSkillByID(parameter.values.skillId).behaviorID;
|
||||
stacks = static_cast<int32_t>(parameter.values.stacks);
|
||||
tick = parameter.values.tick;
|
||||
const auto unknown2 = parameter.values.unknown2; // Always 0
|
||||
}
|
||||
}
|
||||
|
||||
@@ -122,34 +110,28 @@ void BuffComponent::ApplyBuff(const int32_t id, const float duration, const LWOO
|
||||
buff.source = source;
|
||||
buff.behaviorID = behaviorID;
|
||||
|
||||
m_Buffs.emplace(id, buff);
|
||||
m_Buffs.insert_or_assign(id, buff);
|
||||
}
|
||||
|
||||
void BuffComponent::RemoveBuff(int32_t id, bool fromUnEquip, bool removeImmunity) {
|
||||
const auto& iter = m_Buffs.find(id);
|
||||
|
||||
if (iter == m_Buffs.end()) {
|
||||
return;
|
||||
}
|
||||
if (iter == m_Buffs.end()) return;
|
||||
|
||||
GameMessages::SendRemoveBuff(m_Parent, fromUnEquip, removeImmunity, id);
|
||||
GameMessages::SendRemoveBuff(m_ParentEntity, fromUnEquip, removeImmunity, id);
|
||||
|
||||
m_Buffs.erase(iter);
|
||||
|
||||
RemoveBuffEffect(id);
|
||||
}
|
||||
|
||||
bool BuffComponent::HasBuff(int32_t id) {
|
||||
return m_Buffs.find(id) != m_Buffs.end();
|
||||
}
|
||||
|
||||
void BuffComponent::ApplyBuffEffect(int32_t id) {
|
||||
const auto& parameters = GetBuffParameters(id);
|
||||
for (const auto& parameter : parameters) {
|
||||
if (parameter.name == "max_health") {
|
||||
const auto maxHealth = parameter.value;
|
||||
|
||||
auto* destroyable = this->GetParent()->GetComponent<DestroyableComponent>();
|
||||
auto* destroyable = this->GetParentEntity()->GetComponent<DestroyableComponent>();
|
||||
|
||||
if (destroyable == nullptr) return;
|
||||
|
||||
@@ -157,7 +139,7 @@ void BuffComponent::ApplyBuffEffect(int32_t id) {
|
||||
} else if (parameter.name == "max_armor") {
|
||||
const auto maxArmor = parameter.value;
|
||||
|
||||
auto* destroyable = this->GetParent()->GetComponent<DestroyableComponent>();
|
||||
auto* destroyable = this->GetParentEntity()->GetComponent<DestroyableComponent>();
|
||||
|
||||
if (destroyable == nullptr) return;
|
||||
|
||||
@@ -165,13 +147,13 @@ void BuffComponent::ApplyBuffEffect(int32_t id) {
|
||||
} else if (parameter.name == "max_imagination") {
|
||||
const auto maxImagination = parameter.value;
|
||||
|
||||
auto* destroyable = this->GetParent()->GetComponent<DestroyableComponent>();
|
||||
auto* destroyable = this->GetParentEntity()->GetComponent<DestroyableComponent>();
|
||||
|
||||
if (destroyable == nullptr) return;
|
||||
|
||||
destroyable->SetMaxImagination(destroyable->GetMaxImagination() + maxImagination);
|
||||
} else if (parameter.name == "speed") {
|
||||
auto* controllablePhysicsComponent = this->GetParent()->GetComponent<ControllablePhysicsComponent>();
|
||||
auto* controllablePhysicsComponent = this->GetParentEntity()->GetComponent<ControllablePhysicsComponent>();
|
||||
if (!controllablePhysicsComponent) return;
|
||||
const auto speed = parameter.value;
|
||||
controllablePhysicsComponent->AddSpeedboost(speed);
|
||||
@@ -185,29 +167,29 @@ void BuffComponent::RemoveBuffEffect(int32_t id) {
|
||||
if (parameter.name == "max_health") {
|
||||
const auto maxHealth = parameter.value;
|
||||
|
||||
auto* destroyable = this->GetParent()->GetComponent<DestroyableComponent>();
|
||||
auto* destroyable = this->GetParentEntity()->GetComponent<DestroyableComponent>();
|
||||
|
||||
if (destroyable == nullptr) return;
|
||||
if (!destroyable) return;
|
||||
|
||||
destroyable->SetMaxHealth(destroyable->GetMaxHealth() - maxHealth);
|
||||
} else if (parameter.name == "max_armor") {
|
||||
const auto maxArmor = parameter.value;
|
||||
|
||||
auto* destroyable = this->GetParent()->GetComponent<DestroyableComponent>();
|
||||
auto* destroyable = this->GetParentEntity()->GetComponent<DestroyableComponent>();
|
||||
|
||||
if (destroyable == nullptr) return;
|
||||
if (!destroyable) return;
|
||||
|
||||
destroyable->SetMaxArmor(destroyable->GetMaxArmor() - maxArmor);
|
||||
} else if (parameter.name == "max_imagination") {
|
||||
const auto maxImagination = parameter.value;
|
||||
|
||||
auto* destroyable = this->GetParent()->GetComponent<DestroyableComponent>();
|
||||
auto* destroyable = this->GetParentEntity()->GetComponent<DestroyableComponent>();
|
||||
|
||||
if (destroyable == nullptr) return;
|
||||
if (!destroyable) return;
|
||||
|
||||
destroyable->SetMaxImagination(destroyable->GetMaxImagination() - maxImagination);
|
||||
} else if (parameter.name == "speed") {
|
||||
auto* controllablePhysicsComponent = this->GetParent()->GetComponent<ControllablePhysicsComponent>();
|
||||
auto* controllablePhysicsComponent = this->GetParentEntity()->GetComponent<ControllablePhysicsComponent>();
|
||||
if (!controllablePhysicsComponent) return;
|
||||
const auto speed = parameter.value;
|
||||
controllablePhysicsComponent->RemoveSpeedboost(speed);
|
||||
@@ -216,27 +198,19 @@ void BuffComponent::RemoveBuffEffect(int32_t id) {
|
||||
}
|
||||
|
||||
void BuffComponent::RemoveAllBuffs() {
|
||||
for (const auto& buff : m_Buffs) {
|
||||
RemoveBuffEffect(buff.first);
|
||||
for (const auto& [buffId, buffInfo] : m_Buffs) {
|
||||
RemoveBuffEffect(buffId);
|
||||
}
|
||||
|
||||
m_Buffs.clear();
|
||||
}
|
||||
|
||||
void BuffComponent::Reset() {
|
||||
RemoveAllBuffs();
|
||||
}
|
||||
|
||||
void BuffComponent::ReApplyBuffs() {
|
||||
for (const auto& buff : m_Buffs) {
|
||||
ApplyBuffEffect(buff.first);
|
||||
for (const auto& [buffId, buffInfo] : m_Buffs) {
|
||||
ApplyBuffEffect(buffId);
|
||||
}
|
||||
}
|
||||
|
||||
Entity* BuffComponent::GetParent() const {
|
||||
return m_Parent;
|
||||
}
|
||||
|
||||
void BuffComponent::LoadFromXml(tinyxml2::XMLDocument* doc) {
|
||||
// Load buffs
|
||||
auto* dest = doc->FirstChildElement("obj")->FirstChildElement("dest");
|
||||
@@ -245,9 +219,7 @@ void BuffComponent::LoadFromXml(tinyxml2::XMLDocument* doc) {
|
||||
auto* buffElement = dest->FirstChildElement("buff");
|
||||
|
||||
// Old character, no buffs to load
|
||||
if (buffElement == nullptr) {
|
||||
return;
|
||||
}
|
||||
if (buffElement) return;
|
||||
|
||||
auto* buffEntry = buffElement->FirstChildElement("b");
|
||||
|
||||
@@ -305,12 +277,9 @@ void BuffComponent::UpdateXml(tinyxml2::XMLDocument* doc) {
|
||||
const std::vector<BuffParameter>& BuffComponent::GetBuffParameters(int32_t buffId) {
|
||||
const auto& pair = m_Cache.find(buffId);
|
||||
|
||||
if (pair != m_Cache.end()) {
|
||||
return pair->second;
|
||||
}
|
||||
if (pair != m_Cache.end()) return pair->second;
|
||||
|
||||
auto query = CDClientDatabase::CreatePreppedStmt(
|
||||
"SELECT * FROM BuffParameters WHERE BuffID = ?;");
|
||||
auto query = CDClientDatabase::CreatePreppedStmt("SELECT * FROM BuffParameters WHERE BuffID = ?;");
|
||||
query.bind(1, (int)buffId);
|
||||
|
||||
auto result = query.execQuery();
|
||||
@@ -325,17 +294,15 @@ const std::vector<BuffParameter>& BuffComponent::GetBuffParameters(int32_t buffI
|
||||
param.value = result.getFloatField(2);
|
||||
|
||||
if (!result.fieldIsNull(3)) {
|
||||
std::istringstream stream(result.getStringField(3));
|
||||
std::string token;
|
||||
|
||||
while (std::getline(stream, token, ',')) {
|
||||
try {
|
||||
const auto value = std::stof(token);
|
||||
|
||||
param.values.push_back(value);
|
||||
} catch (std::invalid_argument& exception) {
|
||||
Game::logger->Log("BuffComponent", "Failed to parse value (%s): (%s)!", token.c_str(), exception.what());
|
||||
}
|
||||
const auto parameterInfo = result.getStringField(3);
|
||||
const auto values = GeneralUtils::SplitString(parameterInfo, ',');
|
||||
if (values.size() >= 4) {
|
||||
GeneralUtils::TryParse(values.at(0), param.values.skillId);
|
||||
GeneralUtils::TryParse(values.at(1), param.values.stacks);
|
||||
GeneralUtils::TryParse(values.at(2), param.values.tick);
|
||||
GeneralUtils::TryParse(values.at(3), param.values.unknown2);
|
||||
} else {
|
||||
Game::logger->Log("BuffComponent", "Failed to parse %s into parameter struct. Too few parameters to split on.", parameterInfo);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -14,20 +14,24 @@ class Entity;
|
||||
/**
|
||||
* Extra information on effects to apply after applying a buff, for example whether to buff armor, imag or health and by how much
|
||||
*/
|
||||
struct BuffParameter
|
||||
{
|
||||
int32_t buffId;
|
||||
struct BuffParameter {
|
||||
struct ParameterValues {
|
||||
int32_t skillId = 0;
|
||||
int32_t stacks = 0;
|
||||
float tick = 0.0f;
|
||||
int32_t unknown2 = 0;
|
||||
};
|
||||
int32_t buffId = 0;
|
||||
std::string name;
|
||||
float value;
|
||||
std::vector<float> values;
|
||||
int32_t effectId;
|
||||
float value = 0.0f;
|
||||
ParameterValues values;
|
||||
int32_t effectId = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* Meta information about a buff that can be applied, e.g. how long it's applied, who applied it, etc.
|
||||
*/
|
||||
struct Buff
|
||||
{
|
||||
struct Buff {
|
||||
int32_t id = 0;
|
||||
float time = 0;
|
||||
float tick = 0;
|
||||
@@ -40,15 +44,11 @@ struct Buff
|
||||
/**
|
||||
* Allows for the application of buffs to the parent entity, altering health, armor and imagination.
|
||||
*/
|
||||
class BuffComponent : public Component {
|
||||
class BuffComponent final : public Component {
|
||||
public:
|
||||
static const eReplicaComponentType ComponentType = eReplicaComponentType::BUFF;
|
||||
inline static const eReplicaComponentType ComponentType = eReplicaComponentType::BUFF;
|
||||
|
||||
explicit BuffComponent(Entity* parent);
|
||||
|
||||
~BuffComponent();
|
||||
|
||||
Entity* GetParent() const;
|
||||
explicit BuffComponent(Entity* parent) : Component(parent) {};
|
||||
|
||||
void LoadFromXml(tinyxml2::XMLDocument* doc) override;
|
||||
|
||||
@@ -88,7 +88,7 @@ public:
|
||||
* @param id the id of the buff to find
|
||||
* @return whether or not the entity has a buff with the specified id active
|
||||
*/
|
||||
bool HasBuff(int32_t id);
|
||||
bool HasBuff(int32_t id) { return m_Buffs.find(id) != m_Buffs.end(); };
|
||||
|
||||
/**
|
||||
* Applies the effects of the buffs on the entity, e.g.: changing armor, health, imag, etc.
|
||||
@@ -110,7 +110,7 @@ public:
|
||||
/**
|
||||
* Removes all buffs for the entity and reverses all of their effects
|
||||
*/
|
||||
void Reset();
|
||||
void Reset() { RemoveAllBuffs(); };
|
||||
|
||||
/**
|
||||
* Applies all effects for all buffs, active or not, again
|
||||
|
||||
@@ -9,62 +9,40 @@
|
||||
#include "Item.h"
|
||||
#include "PropertyManagementComponent.h"
|
||||
|
||||
BuildBorderComponent::BuildBorderComponent(Entity* parent) : Component(parent) {
|
||||
}
|
||||
|
||||
BuildBorderComponent::~BuildBorderComponent() {
|
||||
}
|
||||
|
||||
void BuildBorderComponent::OnUse(Entity* originator) {
|
||||
if (originator->GetCharacter()) {
|
||||
const auto& entities = Game::entityManager->GetEntitiesInGroup("PropertyPlaque");
|
||||
if (!originator->GetCharacter()) return;
|
||||
|
||||
auto buildArea = m_Parent->GetObjectID();
|
||||
const auto& entities = EntityManager::Instance()->GetEntitiesInGroup("PropertyPlaque");
|
||||
|
||||
if (!entities.empty()) {
|
||||
buildArea = entities[0]->GetObjectID();
|
||||
auto buildArea = entities.empty() ? m_ParentEntity->GetObjectID() : entities.front()->GetObjectID();
|
||||
|
||||
Game::logger->Log("BuildBorderComponent", "Using PropertyPlaque");
|
||||
}
|
||||
auto* inventoryComponent = originator->GetComponent<InventoryComponent>();
|
||||
|
||||
auto* inventoryComponent = originator->GetComponent<InventoryComponent>();
|
||||
if (!inventoryComponent) return;
|
||||
|
||||
if (inventoryComponent == nullptr) {
|
||||
return;
|
||||
}
|
||||
auto* thinkingHat = inventoryComponent->FindItemByLot(LOT_THINKING_CAP);
|
||||
|
||||
auto* thinkingHat = inventoryComponent->FindItemByLot(6086);
|
||||
if (!thinkingHat) return;
|
||||
|
||||
if (thinkingHat == nullptr) {
|
||||
return;
|
||||
}
|
||||
Game::logger->Log("BuildBorderComponent", "Using BuildArea %llu for player %llu", buildArea, originator->GetObjectID());
|
||||
|
||||
inventoryComponent->PushEquippedItems();
|
||||
inventoryComponent->PushEquippedItems();
|
||||
|
||||
Game::logger->Log("BuildBorderComponent", "Starting with %llu", buildArea);
|
||||
|
||||
if (PropertyManagementComponent::Instance() != nullptr) {
|
||||
GameMessages::SendStartArrangingWithItem(
|
||||
originator,
|
||||
originator->GetSystemAddress(),
|
||||
true,
|
||||
buildArea,
|
||||
originator->GetPosition(),
|
||||
0,
|
||||
thinkingHat->GetId(),
|
||||
thinkingHat->GetLot(),
|
||||
4,
|
||||
0,
|
||||
-1,
|
||||
NiPoint3::ZERO,
|
||||
0
|
||||
);
|
||||
} else {
|
||||
GameMessages::SendStartArrangingWithItem(originator, originator->GetSystemAddress(), true, buildArea, originator->GetPosition());
|
||||
}
|
||||
|
||||
InventoryComponent* inv = m_Parent->GetComponent<InventoryComponent>();
|
||||
if (!inv) return;
|
||||
inv->PushEquippedItems(); // technically this is supposed to happen automatically... but it doesnt? so just keep this here
|
||||
if (PropertyManagementComponent::Instance()) {
|
||||
GameMessages::SendStartArrangingWithItem(
|
||||
originator,
|
||||
originator->GetSystemAddress(),
|
||||
true,
|
||||
buildArea,
|
||||
originator->GetPosition(),
|
||||
0,
|
||||
thinkingHat->GetId(),
|
||||
thinkingHat->GetLot(),
|
||||
4,
|
||||
0,
|
||||
-1
|
||||
);
|
||||
} else {
|
||||
GameMessages::SendStartArrangingWithItem(originator, originator->GetSystemAddress(), true, buildArea, originator->GetPosition());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,27 +6,23 @@
|
||||
#ifndef BUILDBORDERCOMPONENT_H
|
||||
#define BUILDBORDERCOMPONENT_H
|
||||
|
||||
#include "BitStream.h"
|
||||
#include "Entity.h"
|
||||
#include "Component.h"
|
||||
#include "eReplicaComponentType.h"
|
||||
|
||||
/**
|
||||
* Component for the build border, allowing the user to start building when interacting with it
|
||||
*/
|
||||
class BuildBorderComponent : public Component {
|
||||
class BuildBorderComponent final : public Component {
|
||||
public:
|
||||
static const eReplicaComponentType ComponentType = eReplicaComponentType::BUILD_BORDER;
|
||||
inline static const eReplicaComponentType ComponentType = eReplicaComponentType::BUILD_BORDER;
|
||||
|
||||
BuildBorderComponent(Entity* parent);
|
||||
~BuildBorderComponent() override;
|
||||
BuildBorderComponent(Entity* parent) : Component(parent) { };
|
||||
|
||||
/**
|
||||
* Causes the originator to start build with this entity as a reference point
|
||||
* @param originator the entity (probably a player) that triggered the event
|
||||
*/
|
||||
void OnUse(Entity* originator) override;
|
||||
private:
|
||||
};
|
||||
|
||||
#endif // BUILDBORDERCOMPONENT_H
|
||||
|
||||
@@ -1,38 +1,49 @@
|
||||
set(DGAME_DCOMPONENTS_SOURCES "BaseCombatAIComponent.cpp"
|
||||
set(DGAME_DCOMPONENTS_SOURCES "AchievementVendorComponent.cpp"
|
||||
"ActivityComponent.cpp"
|
||||
"BaseCombatAIComponent.cpp"
|
||||
"BouncerComponent.cpp"
|
||||
"BuffComponent.cpp"
|
||||
"BuildBorderComponent.cpp"
|
||||
"CharacterComponent.cpp"
|
||||
"CollectibleComponent.cpp"
|
||||
"Component.cpp"
|
||||
"ControllablePhysicsComponent.cpp"
|
||||
"DestroyableComponent.cpp"
|
||||
"DonationVendorComponent.cpp"
|
||||
"GateRushComponent.cpp"
|
||||
"InventoryComponent.cpp"
|
||||
"ItemComponent.cpp"
|
||||
"LevelProgressionComponent.cpp"
|
||||
"LUPExhibitComponent.cpp"
|
||||
"MinigameControlComponent.cpp"
|
||||
"MissionComponent.cpp"
|
||||
"MissionOfferComponent.cpp"
|
||||
"ModelComponent.cpp"
|
||||
"ModelBehaviorComponent.cpp"
|
||||
"ModuleAssemblyComponent.cpp"
|
||||
"MovementAIComponent.cpp"
|
||||
"MovingPlatformComponent.cpp"
|
||||
"MutableModelBehaviorComponent.cpp"
|
||||
"PetComponent.cpp"
|
||||
"PhantomPhysicsComponent.cpp"
|
||||
"PlayerForcedMovementComponent.cpp"
|
||||
"PossessableComponent.cpp"
|
||||
"PossessorComponent.cpp"
|
||||
"PossessionComponent.cpp"
|
||||
"PropertyComponent.cpp"
|
||||
"PropertyEntranceComponent.cpp"
|
||||
"PropertyManagementComponent.cpp"
|
||||
"PropertyVendorComponent.cpp"
|
||||
"ProximityMonitorComponent.cpp"
|
||||
"RacingComponent.cpp"
|
||||
"RacingControlComponent.cpp"
|
||||
"RacingSoundTriggerComponent.cpp"
|
||||
"RacingStatsComponent.cpp"
|
||||
"RailActivatorComponent.cpp"
|
||||
"RebuildComponent.cpp"
|
||||
"QuickBuildComponent.cpp"
|
||||
"RenderComponent.cpp"
|
||||
"RigidbodyPhantomPhysicsComponent.cpp"
|
||||
"RocketLaunchLupComponent.cpp"
|
||||
"MultiZoneEntranceComponent.cpp"
|
||||
"RocketLaunchpadControlComponent.cpp"
|
||||
"ScriptComponent.cpp"
|
||||
"ScriptedActivityComponent.cpp"
|
||||
"ShootingGalleryComponent.cpp"
|
||||
"SimplePhysicsComponent.cpp"
|
||||
@@ -40,5 +51,5 @@ set(DGAME_DCOMPONENTS_SOURCES "BaseCombatAIComponent.cpp"
|
||||
"SoundTriggerComponent.cpp"
|
||||
"SwitchComponent.cpp"
|
||||
"TriggerComponent.cpp"
|
||||
"VehiclePhysicsComponent.cpp"
|
||||
"HavokVehiclePhysicsComponent.cpp"
|
||||
"VendorComponent.cpp" PARENT_SCOPE)
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
#include "InventoryComponent.h"
|
||||
#include "ControllablePhysicsComponent.h"
|
||||
#include "EntityManager.h"
|
||||
#include "VehiclePhysicsComponent.h"
|
||||
#include "GameMessages.h"
|
||||
#include "Item.h"
|
||||
#include "Amf3.h"
|
||||
@@ -67,16 +66,12 @@ bool CharacterComponent::LandingAnimDisabled(int zoneID) {
|
||||
return false;
|
||||
}
|
||||
|
||||
CharacterComponent::~CharacterComponent() {
|
||||
}
|
||||
|
||||
void CharacterComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags) {
|
||||
|
||||
if (bIsInitialUpdate) {
|
||||
outBitStream->Write0();
|
||||
outBitStream->Write0();
|
||||
outBitStream->Write0();
|
||||
outBitStream->Write0();
|
||||
outBitStream->Write0(); // Claim codes. Dont't belive these have an effect.
|
||||
outBitStream->Write0(); // Claim codes. Dont't belive these have an effect.
|
||||
outBitStream->Write0(); // Claim codes. Dont't belive these have an effect.
|
||||
outBitStream->Write0(); // Claim codes. Dont't belive these have an effect.
|
||||
|
||||
outBitStream->Write(m_Character->GetHairColor());
|
||||
outBitStream->Write(m_Character->GetHairStyle());
|
||||
@@ -123,6 +118,8 @@ void CharacterComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInit
|
||||
outBitStream->Write(m_RacesFinished);
|
||||
outBitStream->Write(m_FirstPlaceRaceFinishes);
|
||||
|
||||
// This is a really weird bit of serialization. This is serialized as a two bit integer, but the first bit written is always zero.
|
||||
// If the 2 bit integer is exactly 1, we write the rocket configuration.
|
||||
outBitStream->Write0();
|
||||
outBitStream->Write(m_IsLanding);
|
||||
if (m_IsLanding) {
|
||||
@@ -133,20 +130,24 @@ void CharacterComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInit
|
||||
}
|
||||
}
|
||||
|
||||
outBitStream->Write(m_DirtyGMInfo);
|
||||
if (m_DirtyGMInfo) {
|
||||
outBitStream->Write(bIsInitialUpdate || m_DirtyGMInfo);
|
||||
if (bIsInitialUpdate || m_DirtyGMInfo) {
|
||||
outBitStream->Write(m_PvpEnabled);
|
||||
outBitStream->Write(m_IsGM);
|
||||
outBitStream->Write(m_GMLevel);
|
||||
outBitStream->Write(m_EditorEnabled);
|
||||
outBitStream->Write(m_EditorLevel);
|
||||
if (!bIsInitialUpdate) m_DirtyGMInfo = false;
|
||||
}
|
||||
|
||||
outBitStream->Write(m_DirtyCurrentActivity);
|
||||
if (m_DirtyCurrentActivity) outBitStream->Write(m_CurrentActivity);
|
||||
outBitStream->Write(bIsInitialUpdate || m_DirtyCurrentActivity);
|
||||
if (bIsInitialUpdate || m_DirtyCurrentActivity) {
|
||||
outBitStream->Write(m_CurrentActivity);
|
||||
if (!bIsInitialUpdate) m_DirtyCurrentActivity = false;
|
||||
}
|
||||
|
||||
outBitStream->Write(m_DirtySocialInfo);
|
||||
if (m_DirtySocialInfo) {
|
||||
outBitStream->Write(bIsInitialUpdate || m_DirtySocialInfo);
|
||||
if (bIsInitialUpdate || m_DirtySocialInfo) {
|
||||
outBitStream->Write(m_GuildID);
|
||||
outBitStream->Write<unsigned char>(static_cast<unsigned char>(m_GuildName.size()));
|
||||
if (!m_GuildName.empty())
|
||||
@@ -154,6 +155,7 @@ void CharacterComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInit
|
||||
|
||||
outBitStream->Write(m_IsLEGOClubMember);
|
||||
outBitStream->Write(m_CountryCode);
|
||||
if (!bIsInitialUpdate) m_DirtySocialInfo = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -162,21 +164,21 @@ bool CharacterComponent::GetPvpEnabled() const {
|
||||
}
|
||||
|
||||
void CharacterComponent::SetPvpEnabled(const bool value) {
|
||||
if (m_PvpEnabled == value) return;
|
||||
m_DirtyGMInfo = true;
|
||||
|
||||
m_PvpEnabled = value;
|
||||
}
|
||||
|
||||
void CharacterComponent::SetGMLevel(eGameMasterLevel gmlevel) {
|
||||
if (m_GMLevel == gmlevel) return;
|
||||
m_DirtyGMInfo = true;
|
||||
if (gmlevel > eGameMasterLevel::CIVILIAN) m_IsGM = true;
|
||||
else m_IsGM = false;
|
||||
m_IsGM = gmlevel > eGameMasterLevel::CIVILIAN;
|
||||
m_GMLevel = gmlevel;
|
||||
}
|
||||
|
||||
void CharacterComponent::LoadFromXml(tinyxml2::XMLDocument* doc) {
|
||||
|
||||
tinyxml2::XMLElement* character = doc->FirstChildElement("obj")->FirstChildElement("char");
|
||||
auto* character = doc->FirstChildElement("obj")->FirstChildElement("char");
|
||||
if (!character) {
|
||||
Game::logger->Log("CharacterComponent", "Failed to find char tag while loading XML!");
|
||||
return;
|
||||
@@ -196,7 +198,7 @@ void CharacterComponent::LoadFromXml(tinyxml2::XMLDocument* doc) {
|
||||
}
|
||||
|
||||
// Load the zone statistics
|
||||
m_ZoneStatistics = {};
|
||||
m_ZoneStatistics.clear();
|
||||
auto zoneStatistics = character->FirstChildElement("zs");
|
||||
|
||||
if (zoneStatistics) {
|
||||
@@ -219,20 +221,16 @@ void CharacterComponent::LoadFromXml(tinyxml2::XMLDocument* doc) {
|
||||
}
|
||||
}
|
||||
|
||||
const tinyxml2::XMLAttribute* rocketConfig = character->FindAttribute("lcbp");
|
||||
const auto* rocketConfig = character->FindAttribute("lcbp");
|
||||
|
||||
if (rocketConfig) {
|
||||
m_LastRocketConfig = GeneralUtils::ASCIIToUTF16(rocketConfig->Value());
|
||||
} else {
|
||||
m_LastRocketConfig = u"";
|
||||
}
|
||||
m_LastRocketConfig = rocketConfig ? GeneralUtils::ASCIIToUTF16(rocketConfig->Value()) : u"";
|
||||
|
||||
//
|
||||
// Begin custom attributes
|
||||
//
|
||||
|
||||
// Load the last rocket item ID
|
||||
const tinyxml2::XMLAttribute* lastRocketItemID = character->FindAttribute("lrid");
|
||||
const auto* lastRocketItemID = character->FindAttribute("lrid");
|
||||
if (lastRocketItemID) {
|
||||
m_LastRocketItemID = lastRocketItemID->Int64Value();
|
||||
}
|
||||
@@ -245,11 +243,11 @@ void CharacterComponent::LoadFromXml(tinyxml2::XMLDocument* doc) {
|
||||
m_IsGM = true;
|
||||
m_DirtyGMInfo = true;
|
||||
m_EditorLevel = m_GMLevel;
|
||||
m_EditorEnabled = false; //We're not currently in HF if we're loading in
|
||||
m_EditorEnabled = false; // We're not currently in HF if we're loading in
|
||||
}
|
||||
|
||||
//Annoying guild bs:
|
||||
const tinyxml2::XMLAttribute* guildName = character->FindAttribute("gn");
|
||||
const auto* guildName = character->FindAttribute("gn");
|
||||
if (guildName) {
|
||||
const char* gn = guildName->Value();
|
||||
int64_t gid = 0;
|
||||
@@ -270,10 +268,8 @@ void CharacterComponent::LoadFromXml(tinyxml2::XMLDocument* doc) {
|
||||
|
||||
if (!m_Character) return;
|
||||
|
||||
//Check to see if we're landing:
|
||||
if (m_Character->GetZoneID() != Game::server->GetZoneID()) {
|
||||
m_IsLanding = true;
|
||||
}
|
||||
// If the loaded zoneID is different from the current zoneID, we are landing
|
||||
m_IsLanding = m_Character->GetZoneID() != Game::server->GetZoneID();
|
||||
|
||||
if (LandingAnimDisabled(m_Character->GetZoneID()) || LandingAnimDisabled(Game::server->GetZoneID()) || m_LastRocketConfig.empty()) {
|
||||
m_IsLanding = false; //Don't make us land on VE/minigames lol
|
||||
@@ -301,7 +297,7 @@ void CharacterComponent::UpdateXml(tinyxml2::XMLDocument* doc) {
|
||||
|
||||
// done with minifig
|
||||
|
||||
tinyxml2::XMLElement* character = doc->FirstChildElement("obj")->FirstChildElement("char");
|
||||
auto* character = doc->FirstChildElement("obj")->FirstChildElement("char");
|
||||
if (!character) {
|
||||
Game::logger->Log("CharacterComponent", "Failed to find char tag while updating XML!");
|
||||
return;
|
||||
@@ -317,15 +313,15 @@ void CharacterComponent::UpdateXml(tinyxml2::XMLDocument* doc) {
|
||||
if (!zoneStatistics) zoneStatistics = doc->NewElement("zs");
|
||||
zoneStatistics->DeleteChildren();
|
||||
|
||||
for (auto pair : m_ZoneStatistics) {
|
||||
for (const auto&[mapId, zoneStatisticToSave] : m_ZoneStatistics) {
|
||||
auto zoneStatistic = doc->NewElement("s");
|
||||
|
||||
zoneStatistic->SetAttribute("map", pair.first);
|
||||
zoneStatistic->SetAttribute("ac", pair.second.m_AchievementsCollected);
|
||||
zoneStatistic->SetAttribute("bc", pair.second.m_BricksCollected);
|
||||
zoneStatistic->SetAttribute("cc", pair.second.m_CoinsCollected);
|
||||
zoneStatistic->SetAttribute("es", pair.second.m_EnemiesSmashed);
|
||||
zoneStatistic->SetAttribute("qbc", pair.second.m_QuickBuildsCompleted);
|
||||
zoneStatistic->SetAttribute("map", mapId);
|
||||
zoneStatistic->SetAttribute("ac", zoneStatisticToSave.m_AchievementsCollected);
|
||||
zoneStatistic->SetAttribute("bc", zoneStatisticToSave.m_BricksCollected);
|
||||
zoneStatistic->SetAttribute("cc", zoneStatisticToSave.m_CoinsCollected);
|
||||
zoneStatistic->SetAttribute("es", zoneStatisticToSave.m_EnemiesSmashed);
|
||||
zoneStatistic->SetAttribute("qbc", zoneStatisticToSave.m_QuickBuildsCompleted);
|
||||
|
||||
zoneStatistics->LinkEndChild(zoneStatistic);
|
||||
}
|
||||
@@ -351,7 +347,7 @@ void CharacterComponent::UpdateXml(tinyxml2::XMLDocument* doc) {
|
||||
//
|
||||
|
||||
auto newUpdateTimestamp = std::time(nullptr);
|
||||
Game::logger->Log("TotalTimePlayed", "Time since last save: %d", newUpdateTimestamp - m_LastUpdateTimestamp);
|
||||
Game::logger->Log("TotalTimePlayed", "Time since %i last saved: %d", m_Character->GetID(), newUpdateTimestamp - m_LastUpdateTimestamp);
|
||||
|
||||
m_TotalTimePlayed += newUpdateTimestamp - m_LastUpdateTimestamp;
|
||||
character->SetAttribute("time", m_TotalTimePlayed);
|
||||
@@ -392,7 +388,7 @@ Item* CharacterComponent::RocketEquip(Entity* player) {
|
||||
if (!rocket) return rocket;
|
||||
|
||||
// build and define the rocket config
|
||||
for (LDFBaseData* data : rocket->GetConfig()) {
|
||||
for (auto* data : rocket->GetConfig()) {
|
||||
if (data->GetKey() == u"assemblyPartLOTs") {
|
||||
std::string newRocketStr = data->GetValueAsString() + ";";
|
||||
GeneralUtils::ReplaceInString(newRocketStr, "+", ";");
|
||||
@@ -419,7 +415,7 @@ void CharacterComponent::TrackMissionCompletion(bool isAchievement) {
|
||||
|
||||
// Achievements are tracked separately for the zone
|
||||
if (isAchievement) {
|
||||
const auto mapID = Game::zoneManager->GetZoneID().GetMapID();
|
||||
const auto mapID = dZoneManager::Instance()->GetZoneID().GetMapID();
|
||||
GetZoneStatisticsForMap(mapID).m_AchievementsCollected++;
|
||||
}
|
||||
}
|
||||
@@ -472,31 +468,28 @@ void CharacterComponent::TrackImaginationDelta(int32_t imagination) {
|
||||
}
|
||||
|
||||
void CharacterComponent::TrackArmorDelta(int32_t armor) {
|
||||
if (armor > 0) {
|
||||
UpdatePlayerStatistic(TotalArmorRepaired, armor);
|
||||
}
|
||||
if (armor > 0) UpdatePlayerStatistic(TotalArmorRepaired, armor);
|
||||
}
|
||||
|
||||
void CharacterComponent::TrackRebuildComplete() {
|
||||
UpdatePlayerStatistic(QuickBuildsCompleted);
|
||||
|
||||
const auto mapID = Game::zoneManager->GetZoneID().GetMapID();
|
||||
const auto mapID = dZoneManager::Instance()->GetZoneID().GetMapID();
|
||||
GetZoneStatisticsForMap(mapID).m_QuickBuildsCompleted++;
|
||||
}
|
||||
|
||||
void CharacterComponent::TrackRaceCompleted(bool won) {
|
||||
m_RacesFinished++;
|
||||
if (won)
|
||||
m_FirstPlaceRaceFinishes++;
|
||||
if (won) m_FirstPlaceRaceFinishes++;
|
||||
}
|
||||
|
||||
void CharacterComponent::TrackPositionUpdate(const NiPoint3& newPosition) {
|
||||
const auto distance = NiPoint3::Distance(newPosition, m_Parent->GetPosition());
|
||||
const auto distance = NiPoint3::Distance(newPosition, m_ParentEntity->GetPosition());
|
||||
|
||||
if (m_IsRacing) {
|
||||
UpdatePlayerStatistic(DistanceDriven, (uint64_t)distance);
|
||||
UpdatePlayerStatistic(DistanceDriven, static_cast<uint64_t>(distance));
|
||||
} else {
|
||||
UpdatePlayerStatistic(MetersTraveled, (uint64_t)distance);
|
||||
UpdatePlayerStatistic(MetersTraveled, static_cast<uint64_t>(distance));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -667,45 +660,45 @@ void CharacterComponent::InitializeEmptyStatistics() {
|
||||
|
||||
std::string CharacterComponent::StatisticsToString() const {
|
||||
std::stringstream result;
|
||||
result << std::to_string(m_CurrencyCollected) << ';'
|
||||
<< std::to_string(m_BricksCollected) << ';'
|
||||
<< std::to_string(m_SmashablesSmashed) << ';'
|
||||
<< std::to_string(m_QuickBuildsCompleted) << ';'
|
||||
<< std::to_string(m_EnemiesSmashed) << ';'
|
||||
<< std::to_string(m_RocketsUsed) << ';'
|
||||
<< std::to_string(m_MissionsCompleted) << ';'
|
||||
<< std::to_string(m_PetsTamed) << ';'
|
||||
<< std::to_string(m_ImaginationPowerUpsCollected) << ';'
|
||||
<< std::to_string(m_LifePowerUpsCollected) << ';'
|
||||
<< std::to_string(m_ArmorPowerUpsCollected) << ';'
|
||||
<< std::to_string(m_MetersTraveled) << ';'
|
||||
<< std::to_string(m_TimesSmashed) << ';'
|
||||
<< std::to_string(m_TotalDamageTaken) << ';'
|
||||
<< std::to_string(m_TotalDamageHealed) << ';'
|
||||
<< std::to_string(m_TotalArmorRepaired) << ';'
|
||||
<< std::to_string(m_TotalImaginationRestored) << ';'
|
||||
<< std::to_string(m_TotalImaginationUsed) << ';'
|
||||
<< std::to_string(m_DistanceDriven) << ';'
|
||||
<< std::to_string(m_TimeAirborneInCar) << ';'
|
||||
<< std::to_string(m_RacingImaginationPowerUpsCollected) << ';'
|
||||
<< std::to_string(m_RacingImaginationCratesSmashed) << ';'
|
||||
<< std::to_string(m_RacingCarBoostsActivated) << ';'
|
||||
<< std::to_string(m_RacingTimesWrecked) << ';'
|
||||
<< std::to_string(m_RacingSmashablesSmashed) << ';'
|
||||
<< std::to_string(m_RacesFinished) << ';'
|
||||
<< std::to_string(m_FirstPlaceRaceFinishes) << ';';
|
||||
result
|
||||
<< m_CurrencyCollected << ';'
|
||||
<< m_BricksCollected << ';'
|
||||
<< m_SmashablesSmashed << ';'
|
||||
<< m_QuickBuildsCompleted << ';'
|
||||
<< m_EnemiesSmashed << ';'
|
||||
<< m_RocketsUsed << ';'
|
||||
<< m_MissionsCompleted << ';'
|
||||
<< m_PetsTamed << ';'
|
||||
<< m_ImaginationPowerUpsCollected << ';'
|
||||
<< m_LifePowerUpsCollected << ';'
|
||||
<< m_ArmorPowerUpsCollected << ';'
|
||||
<< m_MetersTraveled << ';'
|
||||
<< m_TimesSmashed << ';'
|
||||
<< m_TotalDamageTaken << ';'
|
||||
<< m_TotalDamageHealed << ';'
|
||||
<< m_TotalArmorRepaired << ';'
|
||||
<< m_TotalImaginationRestored << ';'
|
||||
<< m_TotalImaginationUsed << ';'
|
||||
<< m_DistanceDriven << ';'
|
||||
<< m_TimeAirborneInCar << ';'
|
||||
<< m_RacingImaginationPowerUpsCollected << ';'
|
||||
<< m_RacingImaginationCratesSmashed << ';'
|
||||
<< m_RacingCarBoostsActivated << ';'
|
||||
<< m_RacingTimesWrecked << ';'
|
||||
<< m_RacingSmashablesSmashed << ';'
|
||||
<< m_RacesFinished << ';'
|
||||
<< m_FirstPlaceRaceFinishes << ';';
|
||||
|
||||
return result.str();
|
||||
}
|
||||
|
||||
uint64_t CharacterComponent::GetStatisticFromSplit(std::vector<std::string> split, uint32_t index) {
|
||||
return split.size() > index ? std::stoul(split.at(index)) : 0;
|
||||
return index < split.size() ? std::stoul(split.at(index)) : 0;
|
||||
}
|
||||
|
||||
ZoneStatistics& CharacterComponent::GetZoneStatisticsForMap(LWOMAPID mapID) {
|
||||
auto stats = m_ZoneStatistics.find(mapID);
|
||||
if (stats == m_ZoneStatistics.end())
|
||||
m_ZoneStatistics.insert({ mapID, {0, 0, 0, 0, 0 } });
|
||||
if (stats == m_ZoneStatistics.end()) m_ZoneStatistics.insert({ mapID, {0, 0, 0, 0, 0 } });
|
||||
return m_ZoneStatistics.at(mapID);
|
||||
}
|
||||
|
||||
@@ -732,8 +725,8 @@ void CharacterComponent::RemoveVentureVisionEffect(std::string ventureVisionType
|
||||
}
|
||||
|
||||
void CharacterComponent::UpdateClientMinimap(bool showFaction, std::string ventureVisionType) const {
|
||||
if (!m_Parent) return;
|
||||
if (!m_ParentEntity) return;
|
||||
AMFArrayValue arrayToSend;
|
||||
arrayToSend.Insert(ventureVisionType, showFaction);
|
||||
GameMessages::SendUIMessageServerToSingleClient(m_Parent, m_Parent ? m_Parent->GetSystemAddress() : UNASSIGNED_SYSTEM_ADDRESS, "SetFactionVisibility", arrayToSend);
|
||||
GameMessages::SendUIMessageServerToSingleClient(m_ParentEntity, m_ParentEntity ? m_ParentEntity->GetSystemAddress() : UNASSIGNED_SYSTEM_ADDRESS, "SetFactionVisibility", arrayToSend);
|
||||
}
|
||||
|
||||
@@ -60,12 +60,11 @@ enum StatisticID {
|
||||
/**
|
||||
* Represents a character, including their rockets and stats
|
||||
*/
|
||||
class CharacterComponent : public Component {
|
||||
class CharacterComponent final : public Component {
|
||||
public:
|
||||
static const eReplicaComponentType ComponentType = eReplicaComponentType::CHARACTER;
|
||||
inline static const eReplicaComponentType ComponentType = eReplicaComponentType::CHARACTER;
|
||||
|
||||
CharacterComponent(Entity* parent, Character* character);
|
||||
~CharacterComponent() override;
|
||||
|
||||
void LoadFromXml(tinyxml2::XMLDocument* doc) override;
|
||||
void UpdateXml(tinyxml2::XMLDocument* doc) override;
|
||||
@@ -276,10 +275,6 @@ public:
|
||||
*/
|
||||
void UpdateClientMinimap(bool showFaction, std::string ventureVisionType) const;
|
||||
|
||||
void SetCurrentInteracting(LWOOBJID objectID) {m_CurrentInteracting = objectID;};
|
||||
|
||||
LWOOBJID GetCurrentInteracting() {return m_CurrentInteracting;};
|
||||
|
||||
/**
|
||||
* Character info regarding this character, including clothing styles, etc.
|
||||
*/
|
||||
@@ -564,8 +559,6 @@ private:
|
||||
* ID of the last rocket used
|
||||
*/
|
||||
LWOOBJID m_LastRocketItemID = LWOOBJID_EMPTY;
|
||||
|
||||
LWOOBJID m_CurrentInteracting = LWOOBJID_EMPTY;
|
||||
};
|
||||
|
||||
#endif // CHARACTERCOMPONENT_H
|
||||
|
||||
7
dGame/dComponents/CollectibleComponent.cpp
Normal file
7
dGame/dComponents/CollectibleComponent.cpp
Normal file
@@ -0,0 +1,7 @@
|
||||
#include "CollectibleComponent.h"
|
||||
|
||||
#include "Entity.h"
|
||||
|
||||
void CollectibleComponent::Startup() {
|
||||
m_CollectibleId = GetParentEntity()->GetVarAs<int32_t>(u"collectible_id");
|
||||
}
|
||||
22
dGame/dComponents/CollectibleComponent.h
Normal file
22
dGame/dComponents/CollectibleComponent.h
Normal file
@@ -0,0 +1,22 @@
|
||||
#ifndef __COLLECTIBLECOMPONENT__H__
|
||||
#define __COLLECTIBLECOMPONENT__H__
|
||||
|
||||
#include "Component.h"
|
||||
#include "eReplicaComponentType.h"
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
class CollectibleComponent : public Component {
|
||||
public:
|
||||
inline static const eReplicaComponentType ComponentType = eReplicaComponentType::COLLECTIBLE;
|
||||
CollectibleComponent(Entity* parent) : Component(parent) { };
|
||||
|
||||
void Startup() override;
|
||||
|
||||
uint32_t GetCollectibleId() const { return m_CollectibleId; }
|
||||
private:
|
||||
uint32_t m_CollectibleId;
|
||||
};
|
||||
|
||||
|
||||
#endif //!__COLLECTIBLECOMPONENT__H__
|
||||
@@ -1,30 +1,7 @@
|
||||
#include "Component.h"
|
||||
#include "DluAssert.h"
|
||||
|
||||
|
||||
Component::Component(Entity* parent) {
|
||||
m_Parent = parent;
|
||||
}
|
||||
|
||||
Component::~Component() {
|
||||
|
||||
}
|
||||
|
||||
Entity* Component::GetParent() const {
|
||||
return m_Parent;
|
||||
}
|
||||
|
||||
void Component::Update(float deltaTime) {
|
||||
|
||||
}
|
||||
|
||||
void Component::OnUse(Entity* originator) {
|
||||
|
||||
}
|
||||
|
||||
void Component::UpdateXml(tinyxml2::XMLDocument* doc) {
|
||||
|
||||
}
|
||||
|
||||
void Component::LoadFromXml(tinyxml2::XMLDocument* doc) {
|
||||
|
||||
Component::Component(Entity* owningEntity) {
|
||||
DluAssert(owningEntity != nullptr);
|
||||
m_ParentEntity = owningEntity;
|
||||
}
|
||||
|
||||
@@ -1,52 +1,74 @@
|
||||
#pragma once
|
||||
|
||||
#include "../thirdparty/tinyxml2/tinyxml2.h"
|
||||
#include "tinyxml2.h"
|
||||
|
||||
class Entity;
|
||||
|
||||
namespace RakNet {
|
||||
class BitStream;
|
||||
}
|
||||
|
||||
/**
|
||||
* Component base class, provides methods for game loop updates, usage events and loading and saving to XML.
|
||||
*/
|
||||
class Component
|
||||
{
|
||||
class Component {
|
||||
public:
|
||||
Component(Entity* parent);
|
||||
virtual ~Component();
|
||||
Component(Entity* owningEntity);
|
||||
virtual ~Component() {};
|
||||
|
||||
/**
|
||||
* Gets the owner of this component
|
||||
* @return the owner of this component
|
||||
*/
|
||||
Entity* GetParent() const;
|
||||
|
||||
/**
|
||||
* Updates the component in the game loop
|
||||
* @param deltaTime time passed since last update
|
||||
*/
|
||||
virtual void Update(float deltaTime);
|
||||
Entity* GetParentEntity() const { return m_ParentEntity; };
|
||||
|
||||
/**
|
||||
* Event called when this component is being used, e.g. when some entity interacted with it
|
||||
* @param originator
|
||||
*/
|
||||
virtual void OnUse(Entity* originator);
|
||||
virtual void OnUse(Entity* originator) {};
|
||||
|
||||
/**
|
||||
* Save data from this componennt to character XML
|
||||
* @param doc the document to write data to
|
||||
*/
|
||||
virtual void UpdateXml(tinyxml2::XMLDocument* doc);
|
||||
virtual void UpdateXml(tinyxml2::XMLDocument* doc) {};
|
||||
|
||||
/**
|
||||
* Load base data for this component from character XML
|
||||
* @param doc the document to read data from
|
||||
*/
|
||||
virtual void LoadFromXml(tinyxml2::XMLDocument* doc);
|
||||
virtual void LoadFromXml(tinyxml2::XMLDocument* doc) {};
|
||||
|
||||
/**
|
||||
* Call after you have newed the component to initialize it
|
||||
*/
|
||||
virtual void Startup() {};
|
||||
|
||||
/**
|
||||
* Updates the component in the game loop
|
||||
* @param deltaTime time passed since last update
|
||||
*/
|
||||
virtual void Update(float deltaTime) {};
|
||||
|
||||
/**
|
||||
* Loads the data of this component from the luz/lvl configuration
|
||||
*/
|
||||
virtual void LoadConfigData() {};
|
||||
|
||||
/**
|
||||
* Loads the data of this component from the cdclient database
|
||||
*/
|
||||
virtual void LoadTemplateData() {};
|
||||
|
||||
/**
|
||||
* Serializes the component for delivery to the client(s)
|
||||
*/
|
||||
virtual void Serialize(RakNet::BitStream* bitStream, bool isConstruction = false) {};
|
||||
protected:
|
||||
|
||||
/**
|
||||
* The entity that owns this component
|
||||
*/
|
||||
Entity* m_Parent;
|
||||
Entity* m_ParentEntity;
|
||||
};
|
||||
|
||||
153
dGame/dComponents/ComponentHeirachy.md
Normal file
153
dGame/dComponents/ComponentHeirachy.md
Normal file
@@ -0,0 +1,153 @@
|
||||
Legend
|
||||
```
|
||||
├── Explicit inheritance
|
||||
├~~ Loaded with Parent
|
||||
├-> SubComponent
|
||||
├-? idk lol, but related
|
||||
|
||||
originalName -> newName
|
||||
|
||||
```
|
||||
|
||||
|
||||
```tree
|
||||
|
||||
LWOActivityComponent
|
||||
├── LWOQuickBuildComponent
|
||||
├── LWOMiniGameControlComponent
|
||||
├── LWOShootingGalleryComponent
|
||||
├── LWOScriptedActivityComponent
|
||||
| └── LWOBaseRacingControlComponent -> RacingControlComponent
|
||||
| ├── LWORacingControlComponent -> RacingComponent
|
||||
| └── LWOGateRushControlComponent -> GateRushComponent
|
||||
LWOBaseCombatAIComponent
|
||||
├~~ LWOPathfindingControlComponent
|
||||
├~~ LWOProximityMonitorComponent
|
||||
LWOProjectilePhysComponent
|
||||
LWOBasePhysComponent
|
||||
├── LWORigidBodyPhantomComponent
|
||||
├── LWOPhantomPhysComponent
|
||||
├── LWOVehiclePhysicsComponent
|
||||
├── LWOSimplePhysComponent
|
||||
| ├~~ LWOPhantomPhysicsComponent
|
||||
├── LWOPhysicsSystemComponent
|
||||
├── LWOHavokVehiclePhysicsComponent
|
||||
├── LWOControllablePhysComponent
|
||||
LWOBaseRenderComponent
|
||||
├── LWOSkinnedRenderComponent
|
||||
├~~ LWOSkinnedRenderComponent
|
||||
├~~ LWOFXComponent
|
||||
LWOBaseVendorComponent
|
||||
├── LWOVendorComponent
|
||||
| ├~~ LWOProximityMonitorComponent
|
||||
├── LWODonationVendorComponent
|
||||
| ├~~ LWOProximityMonitorComponent
|
||||
├── LWOAchievementVendorComponent
|
||||
| ├~~ LWOProximityMonitorComponent
|
||||
LWOBBBComponent_Common
|
||||
├── LWOBBBComponent_Client
|
||||
LWOBlueprintComponent
|
||||
LWOBouncerComponent
|
||||
LWOBuildControllerComponentCommon
|
||||
├── LWOBuildControllerComponent
|
||||
LWOChangelingBuildComponent
|
||||
LWOCharacterComponent
|
||||
├── LWOMinifigComponent
|
||||
├~~ LWOPossessionControlComponent
|
||||
├~~ LWOMountControlComponent
|
||||
├~~ LWOPetCreatorComponent
|
||||
├~~ LWOLevelProgressionComponent
|
||||
├~~ LWOPlayerForcedMovementComponent
|
||||
| ├~? LWOPathfindingControlComponent
|
||||
LWOChestComponent
|
||||
LWOChoiceBuildComponent
|
||||
LWOCollectibleComponent
|
||||
LWOCustomBuildAssemblyComponent
|
||||
LWODestroyableComponent
|
||||
├~~ LWOBuffComponent
|
||||
├~~ LWOStatusEffectComponent
|
||||
LWODropEffectComponent
|
||||
LWODroppedLootComponent
|
||||
LWOExhibitComponent
|
||||
LWOGenericActivatorComponent
|
||||
LWOGhostComponent
|
||||
LWOHFLightDirectionGadgetComponent
|
||||
LWOInventoryComponent_Common
|
||||
├── LWOInventoryComponent_Client
|
||||
| └── LWOInventoryComponent_EquippedItem
|
||||
LWOItemComponent
|
||||
LWOLUPExhibitComponent
|
||||
LWOMissionOfferComponent
|
||||
LWOModelBehaviorComponent
|
||||
├~~ LWOSimplePhysComponent
|
||||
├~~ LWOControllablePhysComponent
|
||||
├~~ LWOPathfindingControlComponent
|
||||
├~~ LWOMutableModelBehaviorComponent
|
||||
LWOModelBuilderComponent
|
||||
LWOModularBuildComponentCommon
|
||||
├── LWOModularBuildComponent
|
||||
LWOModuleAssemblyComponent
|
||||
├── LWOModuleAssemblyComponentCommon
|
||||
LWOModuleComponentCommon
|
||||
├── LWOModuleComponent
|
||||
LWOMovementAIComponent
|
||||
LWOMultiZoneEntranceComponent
|
||||
LWOOverheadIconComponent
|
||||
LWOPetComponent
|
||||
├~~ LWOPathfindingControlComponent
|
||||
├~? LWOItemComponent
|
||||
├~? LWOModelBehaviorComponent
|
||||
| ├~~ ...
|
||||
LWOPlatformBoundaryComponent
|
||||
LWOPlatformComponent
|
||||
├-> LWOMoverPlatformSubComponent
|
||||
├-> LWOSimpleMoverPlatformSubComponent
|
||||
├-> LWORotaterPlatformSubComponent
|
||||
LWOPropertyComponent
|
||||
LWOPropertyEntranceComponent
|
||||
LWOPropertyManagementComponent
|
||||
LWOPropertyVendorComponent
|
||||
LWOProximityMonitorComponent
|
||||
LWOSoundTriggerComponent
|
||||
├── LWORacingSoundTriggerComponent
|
||||
LWORacingStatsComponentCommon
|
||||
├── LWORacingStatsComponent
|
||||
LWORocketAnimationControlComponentCommon
|
||||
├── LWORocketAnimationControlComponent
|
||||
LWORocketLaunchpadControlComponentCommon
|
||||
├── LWORocketLaunchpadControlComponent
|
||||
LWOScriptComponent
|
||||
├~~ LWOPathfindingControlComponent
|
||||
├~~ LWOProximityMonitorComponent
|
||||
LWOShowcaseModelHandlerComponent
|
||||
LWOSkillComponent
|
||||
LWOSoundAmbient2DComponent
|
||||
LWOSoundAmbient3DComponent
|
||||
LWOSoundRepeaterComponent
|
||||
LWOSpawnComponent
|
||||
LWOSpringpadComponent
|
||||
LWOSwitchComponent
|
||||
LWOTriggerComponent
|
||||
LocalPlayer - This is a function call in the client which, if the generated Entity is a player, the below components are added. This **must** be done for all players.
|
||||
├~~ LWOInteractionManagerComponent
|
||||
├~~ LWOUserControlComponent
|
||||
├~~ LWOFriendsListComponent
|
||||
├~~ LWOIgnoreListComponent
|
||||
├~~ LWOTextEffectComponent
|
||||
├~~ LWOChatBubbleComponent
|
||||
├~~ LWOGuildComponent
|
||||
├~~ LWOPlayerPetTamingComponent
|
||||
├~~ LWOLocalSystemsComponent
|
||||
├~~ LWOSlashCommandComponent
|
||||
├~~ LWOMissionComponent
|
||||
├~~ LWOPropertyEditorComponent
|
||||
├~~ LWOComponent115
|
||||
├~~ LWOTeamsComponent
|
||||
├~~ LWOChatComponent
|
||||
├~~ LWOPetControlComponent
|
||||
├~~ LWOTradeComponent
|
||||
├~~ LWOPreconditionComponent
|
||||
├~~ LWOFlagComponent
|
||||
├~~ LWOFactionTriggerComponent
|
||||
|
||||
```
|
||||
@@ -3,6 +3,7 @@
|
||||
#include "BitStream.h"
|
||||
#include "dLogger.h"
|
||||
#include "Game.h"
|
||||
#include "dServer.h"
|
||||
|
||||
#include "dpWorld.h"
|
||||
#include "dpEntity.h"
|
||||
@@ -51,34 +52,30 @@ ControllablePhysicsComponent::ControllablePhysicsComponent(Entity* entity) : Com
|
||||
m_ImmuneToStunTurnCount = 0;
|
||||
m_ImmuneToStunUseItemCount = 0;
|
||||
|
||||
if (entity->GetLOT() != 1) // Other physics entities we care about will be added by BaseCombatAI
|
||||
return;
|
||||
// Other physics entities we care about will be added by BaseCombatAI
|
||||
if (entity->GetLOT() != 1) return;
|
||||
|
||||
if (entity->GetLOT() == 1) {
|
||||
Game::logger->Log("ControllablePhysicsComponent", "Using patch to load minifig physics");
|
||||
Game::logger->Log("ControllablePhysicsComponent", "Using patch to load minifig physics");
|
||||
|
||||
float radius = 1.5f;
|
||||
m_dpEntity = new dpEntity(m_Parent->GetObjectID(), radius, false);
|
||||
m_dpEntity->SetCollisionGroup(COLLISION_GROUP_DYNAMIC | COLLISION_GROUP_FRIENDLY);
|
||||
dpWorld::Instance().AddEntity(m_dpEntity);
|
||||
}
|
||||
float radius = 1.5f;
|
||||
m_dpEntity = new dpEntity(m_ParentEntity->GetObjectID(), radius, false);
|
||||
m_dpEntity->SetCollisionGroup(COLLISION_GROUP_DYNAMIC | COLLISION_GROUP_FRIENDLY);
|
||||
dpWorld::Instance().AddEntity(m_dpEntity);
|
||||
}
|
||||
|
||||
ControllablePhysicsComponent::~ControllablePhysicsComponent() {
|
||||
if (m_dpEntity) {
|
||||
dpWorld::Instance().RemoveEntity(m_dpEntity);
|
||||
}
|
||||
if (!m_dpEntity) return;
|
||||
dpWorld::Instance().RemoveEntity(m_dpEntity);
|
||||
}
|
||||
|
||||
void ControllablePhysicsComponent::Update(float deltaTime) {
|
||||
|
||||
void ControllablePhysicsComponent::Startup() {
|
||||
NiPoint3 pos = m_ParentEntity->GetDefaultPosition();
|
||||
NiQuaternion rot = m_ParentEntity->GetDefaultRotation();
|
||||
SetPosition(pos);
|
||||
SetRotation(rot);
|
||||
}
|
||||
|
||||
void ControllablePhysicsComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags) {
|
||||
//If this is a creation, then we assume the position is dirty, even when it isn't.
|
||||
//This is because new clients will still need to receive the position.
|
||||
//if (bIsInitialUpdate) m_DirtyPosition = true;
|
||||
|
||||
if (bIsInitialUpdate) {
|
||||
outBitStream->Write(m_InJetpackMode);
|
||||
if (m_InJetpackMode) {
|
||||
@@ -99,33 +96,32 @@ void ControllablePhysicsComponent::Serialize(RakNet::BitStream* outBitStream, bo
|
||||
|
||||
if (m_IgnoreMultipliers) m_DirtyCheats = false;
|
||||
|
||||
outBitStream->Write(m_DirtyCheats);
|
||||
if (m_DirtyCheats) {
|
||||
outBitStream->Write(bIsInitialUpdate || m_DirtyCheats);
|
||||
if (bIsInitialUpdate || m_DirtyCheats) {
|
||||
outBitStream->Write(m_GravityScale);
|
||||
outBitStream->Write(m_SpeedMultiplier);
|
||||
|
||||
m_DirtyCheats = false;
|
||||
if (!bIsInitialUpdate) m_DirtyCheats = false;
|
||||
}
|
||||
|
||||
outBitStream->Write(m_DirtyEquippedItemInfo);
|
||||
if (m_DirtyEquippedItemInfo) {
|
||||
outBitStream->Write(bIsInitialUpdate || m_DirtyEquippedItemInfo);
|
||||
if (bIsInitialUpdate || m_DirtyEquippedItemInfo) {
|
||||
outBitStream->Write(m_PickupRadius);
|
||||
outBitStream->Write(m_InJetpackMode);
|
||||
m_DirtyEquippedItemInfo = false;
|
||||
if (!bIsInitialUpdate) m_DirtyEquippedItemInfo = false;
|
||||
}
|
||||
|
||||
outBitStream->Write(m_DirtyBubble);
|
||||
if (m_DirtyBubble) {
|
||||
outBitStream->Write(bIsInitialUpdate || m_DirtyBubble);
|
||||
if (bIsInitialUpdate || m_DirtyBubble) {
|
||||
outBitStream->Write(m_IsInBubble);
|
||||
if (m_IsInBubble) {
|
||||
outBitStream->Write(m_BubbleType);
|
||||
outBitStream->Write(m_SpecialAnims);
|
||||
}
|
||||
m_DirtyBubble = false;
|
||||
if (!bIsInitialUpdate) m_DirtyBubble = false;
|
||||
}
|
||||
|
||||
outBitStream->Write(m_DirtyPosition || bIsInitialUpdate);
|
||||
if (m_DirtyPosition || bIsInitialUpdate) {
|
||||
outBitStream->Write(bIsInitialUpdate || m_DirtyPosition);
|
||||
if (bIsInitialUpdate || m_DirtyPosition) {
|
||||
outBitStream->Write(m_Position.x);
|
||||
outBitStream->Write(m_Position.y);
|
||||
outBitStream->Write(m_Position.z);
|
||||
@@ -138,20 +134,22 @@ void ControllablePhysicsComponent::Serialize(RakNet::BitStream* outBitStream, bo
|
||||
outBitStream->Write(m_IsOnGround);
|
||||
outBitStream->Write(m_IsOnRail);
|
||||
|
||||
outBitStream->Write(m_DirtyVelocity);
|
||||
if (m_DirtyVelocity) {
|
||||
outBitStream->Write(bIsInitialUpdate || m_DirtyVelocity);
|
||||
if (bIsInitialUpdate || m_DirtyVelocity) {
|
||||
outBitStream->Write(m_Velocity.x);
|
||||
outBitStream->Write(m_Velocity.y);
|
||||
outBitStream->Write(m_Velocity.z);
|
||||
if (!bIsInitialUpdate) m_DirtyVelocity = false;
|
||||
}
|
||||
|
||||
outBitStream->Write(m_DirtyAngularVelocity);
|
||||
if (m_DirtyAngularVelocity) {
|
||||
outBitStream->Write(bIsInitialUpdate || m_DirtyAngularVelocity);
|
||||
if (bIsInitialUpdate || m_DirtyAngularVelocity) {
|
||||
outBitStream->Write(m_AngularVelocity.x);
|
||||
outBitStream->Write(m_AngularVelocity.y);
|
||||
outBitStream->Write(m_AngularVelocity.z);
|
||||
if (!bIsInitialUpdate) m_DirtyAngularVelocity = false;
|
||||
}
|
||||
|
||||
if (!bIsInitialUpdate) m_DirtyPosition = false;
|
||||
outBitStream->Write0();
|
||||
}
|
||||
|
||||
@@ -162,22 +160,45 @@ void ControllablePhysicsComponent::Serialize(RakNet::BitStream* outBitStream, bo
|
||||
}
|
||||
|
||||
void ControllablePhysicsComponent::LoadFromXml(tinyxml2::XMLDocument* doc) {
|
||||
tinyxml2::XMLElement* character = doc->FirstChildElement("obj")->FirstChildElement("char");
|
||||
if (!character) {
|
||||
tinyxml2::XMLElement* characterElem = doc->FirstChildElement("obj")->FirstChildElement("char");
|
||||
if (!characterElem) {
|
||||
Game::logger->Log("ControllablePhysicsComponent", "Failed to find char tag!");
|
||||
return;
|
||||
}
|
||||
|
||||
m_Parent->GetCharacter()->LoadXmlRespawnCheckpoints();
|
||||
m_ParentEntity->GetCharacter()->LoadXmlRespawnCheckpoints();
|
||||
|
||||
character->QueryAttribute("lzx", &m_Position.x);
|
||||
character->QueryAttribute("lzy", &m_Position.y);
|
||||
character->QueryAttribute("lzz", &m_Position.z);
|
||||
character->QueryAttribute("lzrx", &m_Rotation.x);
|
||||
character->QueryAttribute("lzry", &m_Rotation.y);
|
||||
character->QueryAttribute("lzrz", &m_Rotation.z);
|
||||
character->QueryAttribute("lzrw", &m_Rotation.w);
|
||||
characterElem->QueryAttribute("lzx", &m_Position.x);
|
||||
characterElem->QueryAttribute("lzy", &m_Position.y);
|
||||
characterElem->QueryAttribute("lzz", &m_Position.z);
|
||||
characterElem->QueryAttribute("lzrx", &m_Rotation.x);
|
||||
characterElem->QueryAttribute("lzry", &m_Rotation.y);
|
||||
characterElem->QueryAttribute("lzrz", &m_Rotation.z);
|
||||
characterElem->QueryAttribute("lzrw", &m_Rotation.w);
|
||||
|
||||
auto* character = GetParentEntity()->GetCharacter();
|
||||
const auto mapID = Game::server->GetZoneID();
|
||||
|
||||
//If we came from another zone, put us in the starting loc
|
||||
if (character->GetZoneID() != Game::server->GetZoneID() || mapID == 1603) { // Exception for Moon Base as you tend to spawn on the roof.
|
||||
const auto& targetSceneName = character->GetTargetScene();
|
||||
auto* targetScene = EntityManager::Instance()->GetSpawnPointEntity(targetSceneName);
|
||||
|
||||
NiPoint3 pos;
|
||||
NiQuaternion rot;
|
||||
if (character->HasBeenToWorld(mapID) && targetSceneName.empty()) {
|
||||
pos = character->GetRespawnPoint(mapID);
|
||||
rot = dZoneManager::Instance()->GetZone()->GetSpawnRot();
|
||||
} else if (targetScene) {
|
||||
pos = targetScene->GetPosition();
|
||||
rot = targetScene->GetRotation();
|
||||
} else {
|
||||
pos = dZoneManager::Instance()->GetZone()->GetSpawnPos();
|
||||
rot = dZoneManager::Instance()->GetZone()->GetSpawnRot();
|
||||
}
|
||||
SetPosition(pos);
|
||||
SetRotation(rot);
|
||||
}
|
||||
m_DirtyPosition = true;
|
||||
}
|
||||
|
||||
@@ -194,7 +215,7 @@ void ControllablePhysicsComponent::UpdateXml(tinyxml2::XMLDocument* doc) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto zoneInfo = Game::zoneManager->GetZone()->GetZoneID();
|
||||
auto zoneInfo = dZoneManager::Instance()->GetZone()->GetZoneID();
|
||||
|
||||
if (zoneInfo.GetMapID() != 0 && zoneInfo.GetCloneID() == 0) {
|
||||
character->SetAttribute("lzx", m_Position.x);
|
||||
@@ -208,9 +229,7 @@ void ControllablePhysicsComponent::UpdateXml(tinyxml2::XMLDocument* doc) {
|
||||
}
|
||||
|
||||
void ControllablePhysicsComponent::SetPosition(const NiPoint3& pos) {
|
||||
if (m_Static) {
|
||||
return;
|
||||
}
|
||||
if (m_Static || pos == m_Position) return;
|
||||
|
||||
m_Position.x = pos.x;
|
||||
m_Position.y = pos.y;
|
||||
@@ -221,9 +240,7 @@ void ControllablePhysicsComponent::SetPosition(const NiPoint3& pos) {
|
||||
}
|
||||
|
||||
void ControllablePhysicsComponent::SetRotation(const NiQuaternion& rot) {
|
||||
if (m_Static) {
|
||||
return;
|
||||
}
|
||||
if (m_Static || rot == m_Rotation) return;
|
||||
|
||||
m_Rotation = rot;
|
||||
m_DirtyPosition = true;
|
||||
@@ -232,9 +249,7 @@ void ControllablePhysicsComponent::SetRotation(const NiQuaternion& rot) {
|
||||
}
|
||||
|
||||
void ControllablePhysicsComponent::SetVelocity(const NiPoint3& vel) {
|
||||
if (m_Static) {
|
||||
return;
|
||||
}
|
||||
if (m_Static || m_Velocity == vel) return;
|
||||
|
||||
m_Velocity = vel;
|
||||
m_DirtyPosition = true;
|
||||
@@ -244,9 +259,7 @@ void ControllablePhysicsComponent::SetVelocity(const NiPoint3& vel) {
|
||||
}
|
||||
|
||||
void ControllablePhysicsComponent::SetAngularVelocity(const NiPoint3& vel) {
|
||||
if (m_Static) {
|
||||
return;
|
||||
}
|
||||
if (m_Static || vel == m_AngularVelocity) return;
|
||||
|
||||
m_AngularVelocity = vel;
|
||||
m_DirtyPosition = true;
|
||||
@@ -254,25 +267,15 @@ void ControllablePhysicsComponent::SetAngularVelocity(const NiPoint3& vel) {
|
||||
}
|
||||
|
||||
void ControllablePhysicsComponent::SetIsOnGround(bool val) {
|
||||
m_DirtyPosition = true;
|
||||
if (m_IsOnGround == val) return;
|
||||
m_IsOnGround = val;
|
||||
m_DirtyPosition = true;
|
||||
}
|
||||
|
||||
void ControllablePhysicsComponent::SetIsOnRail(bool val) {
|
||||
m_DirtyPosition = true;
|
||||
if (m_IsOnRail == val) return;
|
||||
m_IsOnRail = val;
|
||||
}
|
||||
|
||||
void ControllablePhysicsComponent::SetDirtyPosition(bool val) {
|
||||
m_DirtyPosition = val;
|
||||
}
|
||||
|
||||
void ControllablePhysicsComponent::SetDirtyVelocity(bool val) {
|
||||
m_DirtyVelocity = val;
|
||||
}
|
||||
|
||||
void ControllablePhysicsComponent::SetDirtyAngularVelocity(bool val) {
|
||||
m_DirtyAngularVelocity = val;
|
||||
m_DirtyPosition = true;
|
||||
}
|
||||
|
||||
void ControllablePhysicsComponent::AddPickupRadiusScale(float value) {
|
||||
@@ -297,10 +300,10 @@ void ControllablePhysicsComponent::RemovePickupRadiusScale(float value) {
|
||||
m_PickupRadius = 0.0f;
|
||||
m_DirtyEquippedItemInfo = true;
|
||||
for (uint32_t i = 0; i < m_ActivePickupRadiusScales.size(); i++) {
|
||||
auto candidateRadius = m_ActivePickupRadiusScales[i];
|
||||
auto candidateRadius = m_ActivePickupRadiusScales.at(i);
|
||||
if (m_PickupRadius < candidateRadius) m_PickupRadius = candidateRadius;
|
||||
}
|
||||
Game::entityManager->SerializeEntity(m_Parent);
|
||||
EntityManager::Instance()->SerializeEntity(m_ParentEntity);
|
||||
}
|
||||
|
||||
void ControllablePhysicsComponent::AddSpeedboost(float value) {
|
||||
@@ -319,33 +322,33 @@ void ControllablePhysicsComponent::RemoveSpeedboost(float value) {
|
||||
}
|
||||
|
||||
// Recalculate speedboost since we removed one
|
||||
m_SpeedBoost = 0.0f;
|
||||
m_SpeedBoost = 500.0f;
|
||||
if (m_ActiveSpeedBoosts.empty()) { // no active speed boosts left, so return to base speed
|
||||
auto* levelProgressionComponent = m_Parent->GetComponent<LevelProgressionComponent>();
|
||||
auto* levelProgressionComponent = m_ParentEntity->GetComponent<LevelProgressionComponent>();
|
||||
if (levelProgressionComponent) m_SpeedBoost = levelProgressionComponent->GetSpeedBase();
|
||||
} else { // Used the last applied speedboost
|
||||
m_SpeedBoost = m_ActiveSpeedBoosts.back();
|
||||
}
|
||||
SetSpeedMultiplier(m_SpeedBoost / 500.0f); // 500 being the base speed
|
||||
Game::entityManager->SerializeEntity(m_Parent);
|
||||
EntityManager::Instance()->SerializeEntity(m_ParentEntity);
|
||||
}
|
||||
|
||||
void ControllablePhysicsComponent::ActivateBubbleBuff(eBubbleType bubbleType, bool specialAnims){
|
||||
void ControllablePhysicsComponent::ActivateBubbleBuff(eBubbleType bubbleType, bool specialAnims) {
|
||||
if (m_IsInBubble) {
|
||||
Game::logger->Log("ControllablePhysicsComponent", "Already in bubble");
|
||||
Game::logger->Log("ControllablePhysicsComponent", "%llu is already in bubble", m_ParentEntity->GetObjectID());
|
||||
return;
|
||||
}
|
||||
m_BubbleType = bubbleType;
|
||||
m_IsInBubble = true;
|
||||
m_DirtyBubble = true;
|
||||
m_SpecialAnims = specialAnims;
|
||||
Game::entityManager->SerializeEntity(m_Parent);
|
||||
EntityManager::Instance()->SerializeEntity(m_ParentEntity);
|
||||
}
|
||||
|
||||
void ControllablePhysicsComponent::DeactivateBubbleBuff(){
|
||||
void ControllablePhysicsComponent::DeactivateBubbleBuff() {
|
||||
m_DirtyBubble = true;
|
||||
m_IsInBubble = false;
|
||||
Game::entityManager->SerializeEntity(m_Parent);
|
||||
EntityManager::Instance()->SerializeEntity(m_ParentEntity);
|
||||
};
|
||||
|
||||
void ControllablePhysicsComponent::SetStunImmunity(
|
||||
@@ -357,9 +360,9 @@ void ControllablePhysicsComponent::SetStunImmunity(
|
||||
const bool bImmuneToStunJump,
|
||||
const bool bImmuneToStunMove,
|
||||
const bool bImmuneToStunTurn,
|
||||
const bool bImmuneToStunUseItem){
|
||||
const bool bImmuneToStunUseItem) {
|
||||
|
||||
if (state == eStateChangeType::POP){
|
||||
if (state == eStateChangeType::POP) {
|
||||
if (bImmuneToStunAttack && m_ImmuneToStunAttackCount > 0) m_ImmuneToStunAttackCount -= 1;
|
||||
if (bImmuneToStunEquip && m_ImmuneToStunEquipCount > 0) m_ImmuneToStunEquipCount -= 1;
|
||||
if (bImmuneToStunInteract && m_ImmuneToStunInteractCount > 0) m_ImmuneToStunInteractCount -= 1;
|
||||
@@ -378,7 +381,7 @@ void ControllablePhysicsComponent::SetStunImmunity(
|
||||
}
|
||||
|
||||
GameMessages::SendSetStunImmunity(
|
||||
m_Parent->GetObjectID(), state, m_Parent->GetSystemAddress(), originator,
|
||||
m_ParentEntity->GetObjectID(), state, m_ParentEntity->GetSystemAddress(), originator,
|
||||
bImmuneToStunAttack,
|
||||
bImmuneToStunEquip,
|
||||
bImmuneToStunInteract,
|
||||
|
||||
@@ -19,18 +19,18 @@ enum class eStateChangeType : uint32_t;
|
||||
/**
|
||||
* Handles the movement of controllable Entities, e.g. enemies and players
|
||||
*/
|
||||
class ControllablePhysicsComponent : public Component {
|
||||
class ControllablePhysicsComponent final : public Component {
|
||||
public:
|
||||
static const eReplicaComponentType ComponentType = eReplicaComponentType::CONTROLLABLE_PHYSICS;
|
||||
inline static const eReplicaComponentType ComponentType = eReplicaComponentType::CONTROLLABLE_PHYSICS;
|
||||
|
||||
ControllablePhysicsComponent(Entity* entity);
|
||||
~ControllablePhysicsComponent() override;
|
||||
|
||||
void Update(float deltaTime) override;
|
||||
void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags);
|
||||
void LoadFromXml(tinyxml2::XMLDocument* doc) override;
|
||||
void ResetFlags();
|
||||
void UpdateXml(tinyxml2::XMLDocument* doc) override;
|
||||
void Startup() override;
|
||||
|
||||
/**
|
||||
* Sets the position of this entity, also ensures this update is serialized next tick.
|
||||
@@ -115,19 +115,19 @@ public:
|
||||
* Mark the position as dirty, forcing a serialization update next tick
|
||||
* @param val whether or not the position is dirty
|
||||
*/
|
||||
void SetDirtyPosition(bool val);
|
||||
void SetDirtyPosition(bool val) { m_DirtyPosition = val; }
|
||||
|
||||
/**
|
||||
* Mark the velocity as dirty, forcing a serializtion update next tick
|
||||
* @param val whether or not the velocity is dirty
|
||||
*/
|
||||
void SetDirtyVelocity(bool val);
|
||||
void SetDirtyVelocity(bool val) { m_DirtyVelocity = val; }
|
||||
|
||||
/**
|
||||
* Mark the angular velocity as dirty, forcing a serialization update next tick
|
||||
* @param val whether or not the angular velocity is dirty
|
||||
*/
|
||||
void SetDirtyAngularVelocity(bool val);
|
||||
void SetDirtyAngularVelocity(bool val) { m_DirtyAngularVelocity = val; }
|
||||
|
||||
/**
|
||||
* Sets whether or not the entity is currently wearing a jetpack
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
#include "CDClientManager.h"
|
||||
#include "CDDestructibleComponentTable.h"
|
||||
#include "EntityManager.h"
|
||||
#include "RebuildComponent.h"
|
||||
#include "QuickBuildComponent.h"
|
||||
#include "CppScripts.h"
|
||||
#include "Loot.h"
|
||||
#include "Character.h"
|
||||
@@ -28,7 +28,7 @@
|
||||
#include "MissionComponent.h"
|
||||
#include "CharacterComponent.h"
|
||||
#include "PossessableComponent.h"
|
||||
#include "PossessorComponent.h"
|
||||
#include "PossessionComponent.h"
|
||||
#include "InventoryComponent.h"
|
||||
#include "dZoneManager.h"
|
||||
#include "WorldConfig.h"
|
||||
@@ -37,8 +37,9 @@
|
||||
#include "eGameActivity.h"
|
||||
|
||||
#include "CDComponentsRegistryTable.h"
|
||||
#include "CDCurrencyTableTable.h"
|
||||
|
||||
DestroyableComponent::DestroyableComponent(Entity* parent) : Component(parent) {
|
||||
DestroyableComponent::DestroyableComponent(Entity* parent, int32_t componentId) : Component(parent) {
|
||||
m_iArmor = 0;
|
||||
m_fMaxArmor = 0.0f;
|
||||
m_iImagination = 0;
|
||||
@@ -51,7 +52,7 @@ DestroyableComponent::DestroyableComponent(Entity* parent) : Component(parent) {
|
||||
m_IsGMImmune = false;
|
||||
m_IsShielded = false;
|
||||
m_DamageToAbsorb = 0;
|
||||
m_IsModuleAssembly = m_Parent->HasComponent(eReplicaComponentType::MODULE_ASSEMBLY);
|
||||
m_IsModuleAssembly = m_ParentEntity->HasComponent(eReplicaComponentType::MODULE_ASSEMBLY);
|
||||
m_DirtyThreatList = false;
|
||||
m_HasThreats = false;
|
||||
m_ExplodeFactor = 1.0f;
|
||||
@@ -62,6 +63,7 @@ DestroyableComponent::DestroyableComponent(Entity* parent) : Component(parent) {
|
||||
m_MinCoins = 0;
|
||||
m_MaxCoins = 0;
|
||||
m_DamageReduction = 0;
|
||||
m_ComponentId = componentId;
|
||||
|
||||
m_ImmuneToBasicAttackCount = 0;
|
||||
m_ImmuneToDamageOverTimeCount = 0;
|
||||
@@ -73,49 +75,71 @@ DestroyableComponent::DestroyableComponent(Entity* parent) : Component(parent) {
|
||||
m_ImmuneToQuickbuildInterruptCount = 0;
|
||||
m_ImmuneToPullToPointCount = 0;
|
||||
}
|
||||
void DestroyableComponent::Startup() {
|
||||
|
||||
DestroyableComponent::~DestroyableComponent() {
|
||||
}
|
||||
|
||||
void DestroyableComponent::Reinitialize(LOT templateID) {
|
||||
CDComponentsRegistryTable* compRegistryTable = CDClientManager::Instance().GetTable<CDComponentsRegistryTable>();
|
||||
|
||||
int32_t buffComponentID = compRegistryTable->GetByIDAndType(templateID, eReplicaComponentType::BUFF);
|
||||
int32_t collectibleComponentID = compRegistryTable->GetByIDAndType(templateID, eReplicaComponentType::COLLECTIBLE);
|
||||
int32_t rebuildComponentID = compRegistryTable->GetByIDAndType(templateID, eReplicaComponentType::QUICK_BUILD);
|
||||
|
||||
int32_t componentID = 0;
|
||||
if (collectibleComponentID > 0) componentID = collectibleComponentID;
|
||||
if (rebuildComponentID > 0) componentID = rebuildComponentID;
|
||||
if (buffComponentID > 0) componentID = buffComponentID;
|
||||
|
||||
CDDestructibleComponentTable* destCompTable = CDClientManager::Instance().GetTable<CDDestructibleComponentTable>();
|
||||
std::vector<CDDestructibleComponent> destCompData = destCompTable->Query([=](CDDestructibleComponent entry) { return (entry.id == componentID); });
|
||||
|
||||
if (componentID > 0) {
|
||||
std::vector<CDDestructibleComponent> destCompData = destCompTable->Query([=](CDDestructibleComponent entry) { return (entry.id == componentID); });
|
||||
|
||||
if (destCompData.size() > 0) {
|
||||
SetHealth(destCompData[0].life);
|
||||
SetImagination(destCompData[0].imagination);
|
||||
SetArmor(destCompData[0].armor);
|
||||
|
||||
SetMaxHealth(destCompData[0].life);
|
||||
SetMaxImagination(destCompData[0].imagination);
|
||||
SetMaxArmor(destCompData[0].armor);
|
||||
|
||||
SetIsSmashable(destCompData[0].isSmashable);
|
||||
}
|
||||
} else {
|
||||
void DestroyableComponent::LoadConfigData() {
|
||||
SetIsSmashable(GetIsSmashable() | (m_ParentEntity->GetVarAs<int32_t>(u"is_smashable") != 0));
|
||||
}
|
||||
void DestroyableComponent::LoadTemplateData() {
|
||||
if (m_ParentEntity->IsPlayer()) return;
|
||||
auto* destroyableComponentTable = CDClientManager::Instance().GetTable<CDDestructibleComponentTable>();
|
||||
auto destroyableDataLookup = destroyableComponentTable->Query([this](CDDestructibleComponent entry) { return (entry.id == this->m_ComponentId); });
|
||||
if (m_ComponentId == -1 || destroyableDataLookup.empty()) {
|
||||
SetHealth(1);
|
||||
SetImagination(0);
|
||||
SetArmor(0);
|
||||
|
||||
SetMaxHealth(1);
|
||||
SetMaxImagination(0);
|
||||
SetMaxArmor(0);
|
||||
|
||||
SetIsSmashable(true);
|
||||
AddFaction(-1);
|
||||
AddFaction(6); //Smashables
|
||||
|
||||
// A race car has 60 imagination, other entities defaults to 0.
|
||||
SetImagination(m_ParentEntity->HasComponent(eReplicaComponentType::RACING_STATS) ? 60 : 0);
|
||||
SetMaxImagination(m_ParentEntity->HasComponent(eReplicaComponentType::RACING_STATS) ? 60 : 0);
|
||||
return;
|
||||
}
|
||||
|
||||
auto destroyableData = destroyableDataLookup.at(0);
|
||||
if (m_ParentEntity->HasComponent(eReplicaComponentType::RACING_STATS)) {
|
||||
destroyableData.imagination = 60;
|
||||
}
|
||||
|
||||
SetHealth(destroyableData.life);
|
||||
SetImagination(destroyableData.imagination);
|
||||
SetArmor(destroyableData.armor);
|
||||
|
||||
SetMaxHealth(destroyableData.life);
|
||||
SetMaxImagination(destroyableData.imagination);
|
||||
SetMaxArmor(destroyableData.armor);
|
||||
|
||||
SetIsSmashable(destroyableData.isSmashable);
|
||||
|
||||
SetLootMatrixID(destroyableData.LootMatrixIndex);
|
||||
|
||||
// Now get currency information
|
||||
uint32_t npcMinLevel = destroyableData.level;
|
||||
uint32_t currencyIndex = destroyableData.CurrencyIndex;
|
||||
|
||||
auto* currencyTable = CDClientManager::Instance().GetTable<CDCurrencyTableTable>();
|
||||
auto currencyValues = currencyTable->Query([=](CDCurrencyTable entry) { return (entry.currencyIndex == currencyIndex && entry.npcminlevel == npcMinLevel); });
|
||||
|
||||
if (currencyValues.size() > 0) {
|
||||
// Set the coins
|
||||
SetMinCoins(currencyValues.at(0).minvalue);
|
||||
SetMaxCoins(currencyValues.at(0).maxvalue);
|
||||
}
|
||||
AddFaction(destroyableData.faction);
|
||||
std::stringstream ss(destroyableData.factionList);
|
||||
std::string tokenStr;
|
||||
|
||||
while (std::getline(ss, tokenStr, ',')) {
|
||||
int32_t factionToAdd = -2;
|
||||
if (!GeneralUtils::TryParse(tokenStr, factionToAdd) || factionToAdd == -2 || factionToAdd == destroyableData.faction) continue;
|
||||
|
||||
AddFaction(factionToAdd);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -185,7 +209,7 @@ void DestroyableComponent::LoadFromXml(tinyxml2::XMLDocument* doc) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto* buffComponent = m_Parent->GetComponent<BuffComponent>();
|
||||
auto* buffComponent = m_ParentEntity->GetComponent<BuffComponent>();
|
||||
|
||||
if (buffComponent != nullptr) {
|
||||
buffComponent->LoadFromXml(doc);
|
||||
@@ -207,7 +231,7 @@ void DestroyableComponent::UpdateXml(tinyxml2::XMLDocument* doc) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto* buffComponent = m_Parent->GetComponent<BuffComponent>();
|
||||
auto* buffComponent = m_ParentEntity->GetComponent<BuffComponent>();
|
||||
|
||||
if (buffComponent != nullptr) {
|
||||
buffComponent->UpdateXml(doc);
|
||||
@@ -224,7 +248,7 @@ void DestroyableComponent::UpdateXml(tinyxml2::XMLDocument* doc) {
|
||||
void DestroyableComponent::SetHealth(int32_t value) {
|
||||
m_DirtyHealth = true;
|
||||
|
||||
auto* characterComponent = m_Parent->GetComponent<CharacterComponent>();
|
||||
auto* characterComponent = m_ParentEntity->GetComponent<CharacterComponent>();
|
||||
if (characterComponent != nullptr) {
|
||||
characterComponent->TrackHealthDelta(value - m_iHealth);
|
||||
}
|
||||
@@ -244,16 +268,16 @@ void DestroyableComponent::SetMaxHealth(float value, bool playAnim) {
|
||||
|
||||
if (playAnim) {
|
||||
// Now update the player bar
|
||||
if (!m_Parent->GetParentUser()) return;
|
||||
if (!m_ParentEntity->GetParentUser()) return;
|
||||
|
||||
AMFArrayValue args;
|
||||
args.Insert("amount", std::to_string(difference));
|
||||
args.Insert("type", "health");
|
||||
|
||||
GameMessages::SendUIMessageServerToSingleClient(m_Parent, m_Parent->GetParentUser()->GetSystemAddress(), "MaxPlayerBarUpdate", args);
|
||||
GameMessages::SendUIMessageServerToSingleClient(m_ParentEntity, m_ParentEntity->GetParentUser()->GetSystemAddress(), "MaxPlayerBarUpdate", args);
|
||||
}
|
||||
|
||||
Game::entityManager->SerializeEntity(m_Parent);
|
||||
EntityManager::Instance()->SerializeEntity(m_ParentEntity);
|
||||
}
|
||||
|
||||
void DestroyableComponent::SetArmor(int32_t value) {
|
||||
@@ -262,14 +286,14 @@ void DestroyableComponent::SetArmor(int32_t value) {
|
||||
// If Destroyable Component already has zero armor do not trigger the passive ability again.
|
||||
bool hadArmor = m_iArmor > 0;
|
||||
|
||||
auto* characterComponent = m_Parent->GetComponent<CharacterComponent>();
|
||||
auto* characterComponent = m_ParentEntity->GetComponent<CharacterComponent>();
|
||||
if (characterComponent != nullptr) {
|
||||
characterComponent->TrackArmorDelta(value - m_iArmor);
|
||||
}
|
||||
|
||||
m_iArmor = value;
|
||||
|
||||
auto* inventroyComponent = m_Parent->GetComponent<InventoryComponent>();
|
||||
auto* inventroyComponent = m_ParentEntity->GetComponent<InventoryComponent>();
|
||||
if (m_iArmor == 0 && inventroyComponent != nullptr && hadArmor) {
|
||||
inventroyComponent->TriggerPassiveAbility(PassiveAbilityTrigger::SentinelArmor);
|
||||
}
|
||||
@@ -285,29 +309,29 @@ void DestroyableComponent::SetMaxArmor(float value, bool playAnim) {
|
||||
|
||||
if (playAnim) {
|
||||
// Now update the player bar
|
||||
if (!m_Parent->GetParentUser()) return;
|
||||
if (!m_ParentEntity->GetParentUser()) return;
|
||||
|
||||
AMFArrayValue args;
|
||||
args.Insert("amount", std::to_string(value));
|
||||
args.Insert("type", "armor");
|
||||
|
||||
GameMessages::SendUIMessageServerToSingleClient(m_Parent, m_Parent->GetParentUser()->GetSystemAddress(), "MaxPlayerBarUpdate", args);
|
||||
GameMessages::SendUIMessageServerToSingleClient(m_ParentEntity, m_ParentEntity->GetParentUser()->GetSystemAddress(), "MaxPlayerBarUpdate", args);
|
||||
}
|
||||
|
||||
Game::entityManager->SerializeEntity(m_Parent);
|
||||
EntityManager::Instance()->SerializeEntity(m_ParentEntity);
|
||||
}
|
||||
|
||||
void DestroyableComponent::SetImagination(int32_t value) {
|
||||
m_DirtyHealth = true;
|
||||
|
||||
auto* characterComponent = m_Parent->GetComponent<CharacterComponent>();
|
||||
auto* characterComponent = m_ParentEntity->GetComponent<CharacterComponent>();
|
||||
if (characterComponent != nullptr) {
|
||||
characterComponent->TrackImaginationDelta(value - m_iImagination);
|
||||
}
|
||||
|
||||
m_iImagination = value;
|
||||
|
||||
auto* inventroyComponent = m_Parent->GetComponent<InventoryComponent>();
|
||||
auto* inventroyComponent = m_ParentEntity->GetComponent<InventoryComponent>();
|
||||
if (m_iImagination == 0 && inventroyComponent != nullptr) {
|
||||
inventroyComponent->TriggerPassiveAbility(PassiveAbilityTrigger::AssemblyImagination);
|
||||
}
|
||||
@@ -325,15 +349,15 @@ void DestroyableComponent::SetMaxImagination(float value, bool playAnim) {
|
||||
|
||||
if (playAnim) {
|
||||
// Now update the player bar
|
||||
if (!m_Parent->GetParentUser()) return;
|
||||
if (!m_ParentEntity->GetParentUser()) return;
|
||||
|
||||
AMFArrayValue args;
|
||||
args.Insert("amount", std::to_string(difference));
|
||||
args.Insert("type", "imagination");
|
||||
|
||||
GameMessages::SendUIMessageServerToSingleClient(m_Parent, m_Parent->GetParentUser()->GetSystemAddress(), "MaxPlayerBarUpdate", args);
|
||||
GameMessages::SendUIMessageServerToSingleClient(m_ParentEntity, m_ParentEntity->GetParentUser()->GetSystemAddress(), "MaxPlayerBarUpdate", args);
|
||||
}
|
||||
Game::entityManager->SerializeEntity(m_Parent);
|
||||
EntityManager::Instance()->SerializeEntity(m_ParentEntity);
|
||||
}
|
||||
|
||||
void DestroyableComponent::SetDamageToAbsorb(int32_t value) {
|
||||
@@ -455,8 +479,8 @@ bool DestroyableComponent::IsImmune() const {
|
||||
}
|
||||
|
||||
bool DestroyableComponent::IsKnockbackImmune() const {
|
||||
auto* characterComponent = m_Parent->GetComponent<CharacterComponent>();
|
||||
auto* inventoryComponent = m_Parent->GetComponent<InventoryComponent>();
|
||||
auto* characterComponent = m_ParentEntity->GetComponent<CharacterComponent>();
|
||||
auto* inventoryComponent = m_ParentEntity->GetComponent<InventoryComponent>();
|
||||
|
||||
if (characterComponent != nullptr && inventoryComponent != nullptr && characterComponent->GetCurrentActivity() == eGameActivity::QUICKBUILDING) {
|
||||
const auto hasPassive = inventoryComponent->HasAnyPassive({
|
||||
@@ -482,11 +506,11 @@ LWOOBJID DestroyableComponent::GetKillerID() const {
|
||||
}
|
||||
|
||||
Entity* DestroyableComponent::GetKiller() const {
|
||||
return Game::entityManager->GetEntity(m_KillerID);
|
||||
return EntityManager::Instance()->GetEntity(m_KillerID);
|
||||
}
|
||||
|
||||
bool DestroyableComponent::CheckValidity(const LWOOBJID target, const bool ignoreFactions, const bool targetEnemy, const bool targetFriend) const {
|
||||
auto* targetEntity = Game::entityManager->GetEntity(target);
|
||||
auto* targetEntity = EntityManager::Instance()->GetEntity(target);
|
||||
|
||||
if (targetEntity == nullptr) {
|
||||
Game::logger->Log("DestroyableComponent", "Invalid entity for checking validity (%llu)!", target);
|
||||
@@ -499,7 +523,7 @@ bool DestroyableComponent::CheckValidity(const LWOOBJID target, const bool ignor
|
||||
return false;
|
||||
}
|
||||
|
||||
auto* targetQuickbuild = targetEntity->GetComponent<RebuildComponent>();
|
||||
auto* targetQuickbuild = targetEntity->GetComponent<QuickBuildComponent>();
|
||||
|
||||
if (targetQuickbuild != nullptr) {
|
||||
const auto state = targetQuickbuild->GetState();
|
||||
@@ -532,7 +556,7 @@ void DestroyableComponent::Heal(const uint32_t health) {
|
||||
|
||||
SetHealth(current);
|
||||
|
||||
Game::entityManager->SerializeEntity(m_Parent);
|
||||
EntityManager::Instance()->SerializeEntity(m_ParentEntity);
|
||||
}
|
||||
|
||||
|
||||
@@ -550,7 +574,7 @@ void DestroyableComponent::Imagine(const int32_t deltaImagination) {
|
||||
|
||||
SetImagination(current);
|
||||
|
||||
Game::entityManager->SerializeEntity(m_Parent);
|
||||
EntityManager::Instance()->SerializeEntity(m_ParentEntity);
|
||||
}
|
||||
|
||||
|
||||
@@ -564,7 +588,7 @@ void DestroyableComponent::Repair(const uint32_t armor) {
|
||||
|
||||
SetArmor(current);
|
||||
|
||||
Game::entityManager->SerializeEntity(m_Parent);
|
||||
EntityManager::Instance()->SerializeEntity(m_ParentEntity);
|
||||
}
|
||||
|
||||
|
||||
@@ -616,34 +640,34 @@ void DestroyableComponent::Damage(uint32_t damage, const LWOOBJID source, uint32
|
||||
SetIsShielded(absorb > 0);
|
||||
|
||||
// Dismount on the possessable hit
|
||||
auto possessable = m_Parent->GetComponent<PossessableComponent>();
|
||||
auto* possessable = m_ParentEntity->GetComponent<PossessableComponent>();
|
||||
if (possessable && possessable->GetDepossessOnHit()) {
|
||||
possessable->Dismount();
|
||||
}
|
||||
|
||||
// Dismount on the possessor hit
|
||||
auto possessor = m_Parent->GetComponent<PossessorComponent>();
|
||||
auto* possessor = m_ParentEntity->GetComponent<PossessionComponent>();
|
||||
if (possessor) {
|
||||
auto possessableId = possessor->GetPossessable();
|
||||
if (possessableId != LWOOBJID_EMPTY) {
|
||||
auto possessable = Game::entityManager->GetEntity(possessableId);
|
||||
auto possessable = EntityManager::Instance()->GetEntity(possessableId);
|
||||
if (possessable) {
|
||||
possessor->Dismount(possessable);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (m_Parent->GetLOT() != 1) {
|
||||
if (m_ParentEntity->GetLOT() != 1) {
|
||||
echo = true;
|
||||
}
|
||||
|
||||
if (echo) {
|
||||
Game::entityManager->SerializeEntity(m_Parent);
|
||||
EntityManager::Instance()->SerializeEntity(m_ParentEntity);
|
||||
}
|
||||
|
||||
auto* attacker = Game::entityManager->GetEntity(source);
|
||||
m_Parent->OnHit(attacker);
|
||||
m_Parent->OnHitOrHealResult(attacker, sourceDamage);
|
||||
auto* attacker = EntityManager::Instance()->GetEntity(source);
|
||||
m_ParentEntity->OnHit(attacker);
|
||||
m_ParentEntity->OnHitOrHealResult(attacker, sourceDamage);
|
||||
NotifySubscribers(attacker, sourceDamage);
|
||||
|
||||
for (const auto& cb : m_OnHitCallbacks) {
|
||||
@@ -651,7 +675,7 @@ void DestroyableComponent::Damage(uint32_t damage, const LWOOBJID source, uint32
|
||||
}
|
||||
|
||||
if (health != 0) {
|
||||
auto* combatComponent = m_Parent->GetComponent<BaseCombatAIComponent>();
|
||||
auto* combatComponent = m_ParentEntity->GetComponent<BaseCombatAIComponent>();
|
||||
|
||||
if (combatComponent != nullptr) {
|
||||
combatComponent->Taunt(source, sourceDamage * 10); // * 10 is arbatrary
|
||||
@@ -661,33 +685,39 @@ void DestroyableComponent::Damage(uint32_t damage, const LWOOBJID source, uint32
|
||||
}
|
||||
|
||||
//check if hardcore mode is enabled
|
||||
if (Game::entityManager->GetHardcoreMode()) {
|
||||
if (EntityManager::Instance()->GetHardcoreMode()) {
|
||||
DoHardcoreModeDrops(source);
|
||||
}
|
||||
}
|
||||
|
||||
Smash(source, eKillType::VIOLENT, u"", skillID);
|
||||
}
|
||||
|
||||
void DestroyableComponent::Subscribe(LWOOBJID scriptObjId, CppScripts::Script* scriptToAdd) {
|
||||
m_SubscribedScripts.insert(std::make_pair(scriptObjId, scriptToAdd));
|
||||
Game::logger->LogDebug("DestroyableComponent", "Added script %llu to entity %llu", scriptObjId, m_Parent->GetObjectID());
|
||||
void DestroyableComponent::Subscribe(CppScripts::Script* scriptToAdd) {
|
||||
auto foundScript = std::find(m_SubscribedScripts.begin(), m_SubscribedScripts.end(), scriptToAdd);
|
||||
if (foundScript != m_SubscribedScripts.end()) {
|
||||
Game::logger->LogDebug("DestroyableComponent", "WARNING: Tried to add a script for Entity %llu but the script was already subscribed", m_ParentEntity->GetObjectID());
|
||||
return;
|
||||
}
|
||||
m_SubscribedScripts.push_back(scriptToAdd);
|
||||
Game::logger->LogDebug("DestroyableComponent", "A script has subscribed to entity %llu", m_ParentEntity->GetObjectID());
|
||||
Game::logger->LogDebug("DestroyableComponent", "Number of subscribed scripts %i", m_SubscribedScripts.size());
|
||||
}
|
||||
|
||||
void DestroyableComponent::Unsubscribe(LWOOBJID scriptObjId) {
|
||||
auto foundScript = m_SubscribedScripts.find(scriptObjId);
|
||||
void DestroyableComponent::Unsubscribe(CppScripts::Script* scriptToRemove) {
|
||||
auto foundScript = std::find(m_SubscribedScripts.begin(), m_SubscribedScripts.end(), scriptToRemove);
|
||||
if (foundScript != m_SubscribedScripts.end()) {
|
||||
m_SubscribedScripts.erase(foundScript);
|
||||
Game::logger->LogDebug("DestroyableComponent", "Removed script %llu from entity %llu", scriptObjId, m_Parent->GetObjectID());
|
||||
} else {
|
||||
Game::logger->LogDebug("DestroyableComponent", "Tried to remove a script for Entity %llu but script %llu didnt exist", m_Parent->GetObjectID(), scriptObjId);
|
||||
Game::logger->LogDebug("DestroyableComponent", "Unsubscribed a script from entity %llu", m_ParentEntity->GetObjectID());
|
||||
Game::logger->LogDebug("DestroyableComponent", "Number of subscribed scripts %i", m_SubscribedScripts.size());
|
||||
return;
|
||||
}
|
||||
Game::logger->LogDebug("DestroyableComponent", "Number of subscribed scripts %i", m_SubscribedScripts.size());
|
||||
Game::logger->LogDebug("DestroyableComponent", "WARNING: Tried to remove a script for Entity %llu but the script was not subscribed", m_ParentEntity->GetObjectID());
|
||||
}
|
||||
|
||||
void DestroyableComponent::NotifySubscribers(Entity* attacker, uint32_t damage) {
|
||||
for (auto script : m_SubscribedScripts) {
|
||||
script.second->NotifyHitOrHealResult(m_Parent, attacker, damage);
|
||||
for (auto* script : m_SubscribedScripts) {
|
||||
DluAssert(script != nullptr);
|
||||
script->NotifyHitOrHealResult(m_ParentEntity, attacker, damage);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -696,24 +726,24 @@ void DestroyableComponent::Smash(const LWOOBJID source, const eKillType killType
|
||||
SetArmor(0);
|
||||
SetHealth(0);
|
||||
|
||||
Game::entityManager->SerializeEntity(m_Parent);
|
||||
EntityManager::Instance()->SerializeEntity(m_ParentEntity);
|
||||
}
|
||||
|
||||
m_KillerID = source;
|
||||
|
||||
auto* owner = Game::entityManager->GetEntity(source);
|
||||
auto* owner = EntityManager::Instance()->GetEntity(source);
|
||||
|
||||
if (owner != nullptr) {
|
||||
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;
|
||||
const auto isEnemy = m_ParentEntity->GetComponent<BaseCombatAIComponent>() != nullptr;
|
||||
|
||||
auto* inventoryComponent = owner->GetComponent<InventoryComponent>();
|
||||
|
||||
if (inventoryComponent != nullptr && isEnemy) {
|
||||
inventoryComponent->TriggerPassiveAbility(PassiveAbilityTrigger::EnemySmashed, m_Parent);
|
||||
inventoryComponent->TriggerPassiveAbility(PassiveAbilityTrigger::EnemySmashed, m_ParentEntity);
|
||||
}
|
||||
|
||||
auto* missions = owner->GetComponent<MissionComponent>();
|
||||
@@ -721,7 +751,7 @@ void DestroyableComponent::Smash(const LWOOBJID source, const eKillType killType
|
||||
if (missions != nullptr) {
|
||||
if (team != nullptr) {
|
||||
for (const auto memberId : team->members) {
|
||||
auto* member = Game::entityManager->GetEntity(memberId);
|
||||
auto* member = EntityManager::Instance()->GetEntity(memberId);
|
||||
|
||||
if (member == nullptr) continue;
|
||||
|
||||
@@ -729,28 +759,28 @@ void DestroyableComponent::Smash(const LWOOBJID source, const eKillType killType
|
||||
|
||||
if (memberMissions == nullptr) continue;
|
||||
|
||||
memberMissions->Progress(eMissionTaskType::SMASH, m_Parent->GetLOT());
|
||||
memberMissions->Progress(eMissionTaskType::USE_SKILL, m_Parent->GetLOT(), skillID);
|
||||
memberMissions->Progress(eMissionTaskType::SMASH, m_ParentEntity->GetLOT());
|
||||
memberMissions->Progress(eMissionTaskType::USE_SKILL, m_ParentEntity->GetLOT(), skillID);
|
||||
}
|
||||
} else {
|
||||
missions->Progress(eMissionTaskType::SMASH, m_Parent->GetLOT());
|
||||
missions->Progress(eMissionTaskType::USE_SKILL, m_Parent->GetLOT(), skillID);
|
||||
missions->Progress(eMissionTaskType::SMASH, m_ParentEntity->GetLOT());
|
||||
missions->Progress(eMissionTaskType::USE_SKILL, m_ParentEntity->GetLOT(), skillID);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const auto isPlayer = m_Parent->IsPlayer();
|
||||
const auto isPlayer = m_ParentEntity->IsPlayer();
|
||||
|
||||
GameMessages::SendDie(m_Parent, source, source, true, killType, deathType, 0, 0, 0, isPlayer, false, 1);
|
||||
GameMessages::SendDie(m_ParentEntity, source, source, true, killType, deathType, 0, 0, 0, isPlayer, false, 1);
|
||||
|
||||
//NANI?!
|
||||
if (!isPlayer) {
|
||||
if (owner != nullptr) {
|
||||
auto* team = TeamManager::Instance()->GetTeam(owner->GetObjectID());
|
||||
|
||||
if (team != nullptr && m_Parent->GetComponent<BaseCombatAIComponent>() != nullptr) {
|
||||
if (team != nullptr && m_ParentEntity->GetComponent<BaseCombatAIComponent>() != nullptr) {
|
||||
LWOOBJID specificOwner = LWOOBJID_EMPTY;
|
||||
auto* scriptedActivityComponent = m_Parent->GetComponent<ScriptedActivityComponent>();
|
||||
auto* scriptedActivityComponent = m_ParentEntity->GetComponent<ScriptedActivityComponent>();
|
||||
uint32_t teamSize = team->members.size();
|
||||
uint32_t lootMatrixId = GetLootMatrixID();
|
||||
|
||||
@@ -761,58 +791,54 @@ void DestroyableComponent::Smash(const LWOOBJID source, const eKillType killType
|
||||
if (team->lootOption == 0) { // Round robin
|
||||
specificOwner = TeamManager::Instance()->GetNextLootOwner(team);
|
||||
|
||||
auto* member = Game::entityManager->GetEntity(specificOwner);
|
||||
auto* member = EntityManager::Instance()->GetEntity(specificOwner);
|
||||
|
||||
if (member) LootGenerator::Instance().DropLoot(member, m_Parent, lootMatrixId, GetMinCoins(), GetMaxCoins());
|
||||
if (member) LootGenerator::Instance().DropLoot(member, m_ParentEntity, lootMatrixId, GetMinCoins(), GetMaxCoins());
|
||||
} else {
|
||||
for (const auto memberId : team->members) { // Free for all
|
||||
auto* member = Game::entityManager->GetEntity(memberId);
|
||||
auto* member = EntityManager::Instance()->GetEntity(memberId);
|
||||
|
||||
if (member == nullptr) continue;
|
||||
|
||||
LootGenerator::Instance().DropLoot(member, m_Parent, lootMatrixId, GetMinCoins(), GetMaxCoins());
|
||||
LootGenerator::Instance().DropLoot(member, m_ParentEntity, lootMatrixId, GetMinCoins(), GetMaxCoins());
|
||||
}
|
||||
}
|
||||
} else { // drop loot for non team user
|
||||
LootGenerator::Instance().DropLoot(owner, m_Parent, GetLootMatrixID(), GetMinCoins(), GetMaxCoins());
|
||||
LootGenerator::Instance().DropLoot(owner, m_ParentEntity, GetLootMatrixID(), GetMinCoins(), GetMaxCoins());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
//Check if this zone allows coin drops
|
||||
if (Game::zoneManager->GetPlayerLoseCoinOnDeath()) {
|
||||
auto* character = m_Parent->GetCharacter();
|
||||
if (dZoneManager::Instance()->GetPlayerLoseCoinOnDeath()) {
|
||||
auto* character = m_ParentEntity->GetCharacter();
|
||||
uint64_t coinsTotal = character->GetCoins();
|
||||
const uint64_t minCoinsToLose = Game::zoneManager->GetWorldConfig()->coinsLostOnDeathMin;
|
||||
const uint64_t minCoinsToLose = dZoneManager::Instance()->GetWorldConfig()->coinsLostOnDeathMin;
|
||||
if (coinsTotal >= minCoinsToLose) {
|
||||
const uint64_t maxCoinsToLose = Game::zoneManager->GetWorldConfig()->coinsLostOnDeathMax;
|
||||
const float coinPercentageToLose = Game::zoneManager->GetWorldConfig()->coinsLostOnDeathPercent;
|
||||
const uint64_t maxCoinsToLose = dZoneManager::Instance()->GetWorldConfig()->coinsLostOnDeathMax;
|
||||
const float coinPercentageToLose = dZoneManager::Instance()->GetWorldConfig()->coinsLostOnDeathPercent;
|
||||
|
||||
uint64_t coinsToLose = std::max(static_cast<uint64_t>(coinsTotal * coinPercentageToLose), minCoinsToLose);
|
||||
coinsToLose = std::min(maxCoinsToLose, coinsToLose);
|
||||
|
||||
coinsTotal -= coinsToLose;
|
||||
|
||||
LootGenerator::Instance().DropLoot(m_Parent, m_Parent, -1, coinsToLose, coinsToLose);
|
||||
LootGenerator::Instance().DropLoot(m_ParentEntity, m_ParentEntity, -1, coinsToLose, coinsToLose);
|
||||
character->SetCoins(coinsTotal, eLootSourceType::PICKUP);
|
||||
}
|
||||
}
|
||||
|
||||
Entity* zoneControl = Game::entityManager->GetZoneControlEntity();
|
||||
for (CppScripts::Script* script : CppScripts::GetEntityScripts(zoneControl)) {
|
||||
script->OnPlayerDied(zoneControl, m_Parent);
|
||||
}
|
||||
Entity* zoneControl = EntityManager::Instance()->GetZoneControlEntity();
|
||||
zoneControl->GetScript()->OnPlayerDied(zoneControl, m_ParentEntity);
|
||||
|
||||
std::vector<Entity*> scriptedActs = Game::entityManager->GetEntitiesByComponent(eReplicaComponentType::SCRIPTED_ACTIVITY);
|
||||
std::vector<Entity*> scriptedActs = EntityManager::Instance()->GetEntitiesByComponent(eReplicaComponentType::SCRIPTED_ACTIVITY);
|
||||
for (Entity* scriptEntity : scriptedActs) {
|
||||
if (scriptEntity->GetObjectID() != zoneControl->GetObjectID()) { // Don't want to trigger twice on instance worlds
|
||||
for (CppScripts::Script* script : CppScripts::GetEntityScripts(scriptEntity)) {
|
||||
script->OnPlayerDied(scriptEntity, m_Parent);
|
||||
}
|
||||
scriptEntity->GetScript()->OnPlayerDied(scriptEntity, m_ParentEntity);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m_Parent->Kill(owner);
|
||||
m_ParentEntity->Kill(owner);
|
||||
}
|
||||
|
||||
void DestroyableComponent::SetFaction(int32_t factionID, bool ignoreChecks) {
|
||||
@@ -823,16 +849,16 @@ void DestroyableComponent::SetFaction(int32_t factionID, bool ignoreChecks) {
|
||||
}
|
||||
|
||||
void DestroyableComponent::SetStatusImmunity(
|
||||
const eStateChangeType state,
|
||||
const bool bImmuneToBasicAttack,
|
||||
const bool bImmuneToDamageOverTime,
|
||||
const bool bImmuneToKnockback,
|
||||
const bool bImmuneToInterrupt,
|
||||
const bool bImmuneToSpeed,
|
||||
const bool bImmuneToImaginationGain,
|
||||
const bool bImmuneToImaginationLoss,
|
||||
const bool bImmuneToQuickbuildInterrupt,
|
||||
const bool bImmuneToPullToPoint) {
|
||||
const eStateChangeType state,
|
||||
const bool bImmuneToBasicAttack,
|
||||
const bool bImmuneToDamageOverTime,
|
||||
const bool bImmuneToKnockback,
|
||||
const bool bImmuneToInterrupt,
|
||||
const bool bImmuneToSpeed,
|
||||
const bool bImmuneToImaginationGain,
|
||||
const bool bImmuneToImaginationLoss,
|
||||
const bool bImmuneToQuickbuildInterrupt,
|
||||
const bool bImmuneToPullToPoint) {
|
||||
|
||||
if (state == eStateChangeType::POP) {
|
||||
if (bImmuneToBasicAttack && m_ImmuneToBasicAttackCount > 0) m_ImmuneToBasicAttackCount -= 1;
|
||||
@@ -845,7 +871,7 @@ void DestroyableComponent::SetStatusImmunity(
|
||||
if (bImmuneToQuickbuildInterrupt && m_ImmuneToQuickbuildInterruptCount > 0) m_ImmuneToQuickbuildInterruptCount -= 1;
|
||||
if (bImmuneToPullToPoint && m_ImmuneToPullToPointCount > 0) m_ImmuneToPullToPointCount -= 1;
|
||||
|
||||
} else if (state == eStateChangeType::PUSH){
|
||||
} else if (state == eStateChangeType::PUSH) {
|
||||
if (bImmuneToBasicAttack) m_ImmuneToBasicAttackCount += 1;
|
||||
if (bImmuneToDamageOverTime) m_ImmuneToDamageOverTimeCount += 1;
|
||||
if (bImmuneToKnockback) m_ImmuneToKnockbackCount += 1;
|
||||
@@ -858,7 +884,7 @@ void DestroyableComponent::SetStatusImmunity(
|
||||
}
|
||||
|
||||
GameMessages::SendSetStatusImmunity(
|
||||
m_Parent->GetObjectID(), state, m_Parent->GetSystemAddress(),
|
||||
m_ParentEntity->GetObjectID(), state, m_ParentEntity->GetSystemAddress(),
|
||||
bImmuneToBasicAttack,
|
||||
bImmuneToDamageOverTime,
|
||||
bImmuneToKnockback,
|
||||
@@ -872,7 +898,7 @@ void DestroyableComponent::SetStatusImmunity(
|
||||
}
|
||||
|
||||
void DestroyableComponent::FixStats() {
|
||||
auto* entity = GetParent();
|
||||
auto* entity = GetParentEntity();
|
||||
|
||||
if (entity == nullptr) return;
|
||||
|
||||
@@ -965,50 +991,50 @@ void DestroyableComponent::FixStats() {
|
||||
destroyableComponent->SetImagination(currentImagination);
|
||||
|
||||
// Serialize the entity
|
||||
Game::entityManager->SerializeEntity(entity);
|
||||
EntityManager::Instance()->SerializeEntity(entity);
|
||||
}
|
||||
|
||||
void DestroyableComponent::AddOnHitCallback(const std::function<void(Entity*)>& callback) {
|
||||
m_OnHitCallbacks.push_back(callback);
|
||||
}
|
||||
|
||||
void DestroyableComponent::DoHardcoreModeDrops(const LWOOBJID source){
|
||||
void DestroyableComponent::DoHardcoreModeDrops(const LWOOBJID source) {
|
||||
//check if this is a player:
|
||||
if (m_Parent->IsPlayer()) {
|
||||
if (m_ParentEntity->IsPlayer()) {
|
||||
//remove hardcore_lose_uscore_on_death_percent from the player's uscore:
|
||||
auto* character = m_Parent->GetComponent<CharacterComponent>();
|
||||
auto* character = m_ParentEntity->GetComponent<CharacterComponent>();
|
||||
auto uscore = character->GetUScore();
|
||||
|
||||
auto uscoreToLose = uscore * (Game::entityManager->GetHardcoreLoseUscoreOnDeathPercent() / 100);
|
||||
auto uscoreToLose = uscore * (EntityManager::Instance()->GetHardcoreLoseUscoreOnDeathPercent() / 100);
|
||||
character->SetUScore(uscore - uscoreToLose);
|
||||
|
||||
GameMessages::SendModifyLEGOScore(m_Parent, m_Parent->GetSystemAddress(), -uscoreToLose, eLootSourceType::MISSION);
|
||||
GameMessages::SendModifyLEGOScore(m_ParentEntity, m_ParentEntity->GetSystemAddress(), -uscoreToLose, eLootSourceType::MISSION);
|
||||
|
||||
if (Game::entityManager->GetHardcoreDropinventoryOnDeath()) {
|
||||
if (EntityManager::Instance()->GetHardcoreDropinventoryOnDeath()) {
|
||||
//drop all items from inventory:
|
||||
auto* inventory = m_Parent->GetComponent<InventoryComponent>();
|
||||
auto* inventory = m_ParentEntity->GetComponent<InventoryComponent>();
|
||||
if (inventory) {
|
||||
//get the items inventory:
|
||||
auto items = inventory->GetInventory(eInventoryType::ITEMS);
|
||||
if (items){
|
||||
if (items) {
|
||||
auto itemMap = items->GetItems();
|
||||
if (!itemMap.empty()){
|
||||
if (!itemMap.empty()) {
|
||||
for (const auto& item : itemMap) {
|
||||
//drop the item:
|
||||
if (!item.second) continue;
|
||||
// don't drop the thinkng cap
|
||||
if (item.second->GetLot() == 6086) continue;
|
||||
GameMessages::SendDropClientLoot(m_Parent, source, item.second->GetLot(), 0, m_Parent->GetPosition(), item.second->GetCount());
|
||||
GameMessages::SendDropClientLoot(m_ParentEntity, source, item.second->GetLot(), 0, m_ParentEntity->GetPosition(), item.second->GetCount());
|
||||
item.second->SetCount(0, false, false);
|
||||
}
|
||||
Game::entityManager->SerializeEntity(m_Parent);
|
||||
EntityManager::Instance()->SerializeEntity(m_ParentEntity);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//get character:
|
||||
auto* chars = m_Parent->GetCharacter();
|
||||
auto* chars = m_ParentEntity->GetCharacter();
|
||||
if (chars) {
|
||||
auto coins = chars->GetCoins();
|
||||
|
||||
@@ -1016,30 +1042,30 @@ void DestroyableComponent::DoHardcoreModeDrops(const LWOOBJID source){
|
||||
chars->SetCoins(0, eLootSourceType::NONE);
|
||||
|
||||
//drop all coins:
|
||||
GameMessages::SendDropClientLoot(m_Parent, source, LOT_NULL, coins, m_Parent->GetPosition());
|
||||
GameMessages::SendDropClientLoot(m_ParentEntity, source, LOT_NULL, coins, m_ParentEntity->GetPosition());
|
||||
}
|
||||
|
||||
// Reload the player since we can't normally reduce uscore from the server and we want the UI to update
|
||||
// do this last so we don't get killed.... again
|
||||
Game::entityManager->DestructEntity(m_Parent);
|
||||
Game::entityManager->ConstructEntity(m_Parent);
|
||||
EntityManager::Instance()->DestructEntity(m_ParentEntity);
|
||||
EntityManager::Instance()->ConstructEntity(m_ParentEntity);
|
||||
return;
|
||||
}
|
||||
|
||||
//award the player some u-score:
|
||||
auto* player = Game::entityManager->GetEntity(source);
|
||||
auto* player = EntityManager::Instance()->GetEntity(source);
|
||||
if (player && player->IsPlayer()) {
|
||||
auto* playerStats = player->GetComponent<CharacterComponent>();
|
||||
if (playerStats) {
|
||||
//get the maximum health from this enemy:
|
||||
auto maxHealth = GetMaxHealth();
|
||||
|
||||
int uscore = maxHealth * Game::entityManager->GetHardcoreUscoreEnemiesMultiplier();
|
||||
int uscore = maxHealth * EntityManager::Instance()->GetHardcoreUscoreEnemiesMultiplier();
|
||||
|
||||
playerStats->SetUScore(playerStats->GetUScore() + uscore);
|
||||
GameMessages::SendModifyLEGOScore(player, player->GetSystemAddress(), uscore, eLootSourceType::MISSION);
|
||||
|
||||
Game::entityManager->SerializeEntity(m_Parent);
|
||||
EntityManager::Instance()->SerializeEntity(m_ParentEntity);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,23 +17,19 @@ enum class eStateChangeType : uint32_t;
|
||||
* Represents the stats of an entity, for example its health, imagination and armor. Also handles factions, which
|
||||
* indicate which enemies this entity has.
|
||||
*/
|
||||
class DestroyableComponent : public Component {
|
||||
class DestroyableComponent final : public Component {
|
||||
public:
|
||||
static const eReplicaComponentType ComponentType = eReplicaComponentType::DESTROYABLE;
|
||||
inline static const eReplicaComponentType ComponentType = eReplicaComponentType::DESTROYABLE;
|
||||
|
||||
DestroyableComponent(Entity* parentEntity);
|
||||
~DestroyableComponent() override;
|
||||
DestroyableComponent(Entity* parentEntity, int32_t componentId = -1);
|
||||
|
||||
void Startup() override;
|
||||
void LoadConfigData() override;
|
||||
void LoadTemplateData() override;
|
||||
void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, uint32_t& flags);
|
||||
void LoadFromXml(tinyxml2::XMLDocument* doc) override;
|
||||
void UpdateXml(tinyxml2::XMLDocument* doc) override;
|
||||
|
||||
/**
|
||||
* Initializes the component using a different LOT
|
||||
* @param templateID the ID to use for initialization
|
||||
*/
|
||||
void Reinitialize(LOT templateID);
|
||||
|
||||
/**
|
||||
* Sets the health of this entity. Makes sure this is serialized on the next tick and if this is a character its
|
||||
* stats will also update.
|
||||
@@ -451,13 +447,15 @@ public:
|
||||
*/
|
||||
void NotifySubscribers(Entity* attacker, uint32_t damage);
|
||||
|
||||
void Subscribe(LWOOBJID scriptObjId, CppScripts::Script* scriptToAdd);
|
||||
void Unsubscribe(LWOOBJID scriptObjId);
|
||||
void Subscribe(CppScripts::Script* scriptToAdd);
|
||||
void Unsubscribe(CppScripts::Script* scriptToRemove);
|
||||
|
||||
// handle hardcode mode drops
|
||||
void DoHardcoreModeDrops(const LWOOBJID source);
|
||||
|
||||
private:
|
||||
// The ID of this component
|
||||
int32_t m_ComponentId;
|
||||
/**
|
||||
* Whether or not the health should be serialized
|
||||
*/
|
||||
@@ -589,9 +587,9 @@ private:
|
||||
std::vector<std::function<void(Entity*)>> m_OnHitCallbacks;
|
||||
|
||||
/**
|
||||
* The list of scripts subscribed to this components actions
|
||||
* Scripts that are subscribed to this component
|
||||
*/
|
||||
std::map<LWOOBJID, CppScripts::Script*> m_SubscribedScripts;
|
||||
std::vector<CppScripts::Script*> m_SubscribedScripts;
|
||||
|
||||
/**
|
||||
* status immunity counters
|
||||
|
||||
@@ -1,41 +1,9 @@
|
||||
#include "DonationVendorComponent.h"
|
||||
#include "Database.h"
|
||||
|
||||
DonationVendorComponent::DonationVendorComponent(Entity* parent) : VendorComponent(parent) {
|
||||
//LoadConfigData
|
||||
m_PercentComplete = 0.0;
|
||||
m_TotalDonated = 0;
|
||||
m_TotalRemaining = 0;
|
||||
|
||||
// custom attribute to calculate other values
|
||||
m_Goal = m_Parent->GetVar<int32_t>(u"donationGoal");
|
||||
if (m_Goal == 0) m_Goal = INT32_MAX;
|
||||
|
||||
// Default to the nexus tower jawbox activity and setup settings
|
||||
m_ActivityId = m_Parent->GetVar<uint32_t>(u"activityID");
|
||||
if ((m_ActivityId == 0) || (m_ActivityId == 117)) {
|
||||
m_ActivityId = 117;
|
||||
m_PercentComplete = 1.0;
|
||||
m_TotalDonated = INT32_MAX;
|
||||
m_TotalRemaining = 0;
|
||||
m_Goal = INT32_MAX;
|
||||
return;
|
||||
}
|
||||
|
||||
std::unique_ptr<sql::PreparedStatement> query(Database::CreatePreppedStmt("SELECT SUM(primaryScore) as donation_total FROM leaderboard WHERE game_id = ?;"));
|
||||
query->setInt(1, m_ActivityId);
|
||||
std::unique_ptr<sql::ResultSet> donation_total(query->executeQuery());
|
||||
if (donation_total->next()) m_TotalDonated = donation_total->getInt("donation_total");
|
||||
m_TotalRemaining = m_Goal - m_TotalDonated;
|
||||
m_PercentComplete = m_TotalDonated/static_cast<float>(m_Goal);
|
||||
}
|
||||
|
||||
void DonationVendorComponent::SubmitDonation(uint32_t count) {
|
||||
if (count <= 0 && ((m_TotalDonated + count) > 0)) return;
|
||||
m_TotalDonated += count;
|
||||
m_TotalRemaining = m_Goal - m_TotalDonated;
|
||||
m_PercentComplete = m_TotalDonated/static_cast<float>(m_Goal);
|
||||
m_DirtyDonationVendor = true;
|
||||
}
|
||||
|
||||
void DonationVendorComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags) {
|
||||
@@ -48,3 +16,8 @@ void DonationVendorComponent::Serialize(RakNet::BitStream* outBitStream, bool bI
|
||||
if (!bIsInitialUpdate) m_DirtyDonationVendor = false;
|
||||
}
|
||||
}
|
||||
|
||||
void DonationVendorComponent::LoadConfigData() {
|
||||
m_ActivityId = m_ParentEntity->GetVar<uint32_t>(u"activityID");
|
||||
VendorComponent::LoadConfigData();
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user