Compare commits

..

122 Commits

Author SHA1 Message Date
David Markowitz
6240eefc7e local work 2023-07-11 00:26:27 -07:00
David Markowitz
949a6db4bc RailActivatorComponent pass 2023-07-11 00:23:27 -07:00
David Markowitz
49f3d757e5 ProximityMonitorComponent re-pass 2023-07-11 00:15:05 -07:00
David Markowitz
c204ea4009 PropertyVendorComponent pass 2023-07-11 00:07:08 -07:00
David Markowitz
a37ec326b9 idek if ill keep this but yeah 2023-07-11 00:02:59 -07:00
EmosewaMC
8678ed03c3 push no pass 2023-07-10 22:18:42 -07:00
David Markowitz
37bcc81694 PropertyComponent pass 2023-07-09 22:41:22 -07:00
David Markowitz
4d88f63338 PossessionComponent pass 2023-07-09 22:37:28 -07:00
David Markowitz
59831fc15d PhantomPhysicsComponent pass 2023-07-09 22:34:24 -07:00
David Markowitz
fe6b279ebb PossessableComponent pass 2023-07-09 22:34:18 -07:00
David Markowitz
598d88b307 PlayerForcedMovementComponent pass 2023-07-09 22:20:51 -07:00
David Markowitz
28637a206d PhantomPhysicsComponent pass 2023-07-09 22:17:57 -07:00
David Markowitz
87675aa62d PetComponent pass 2023-07-09 21:54:11 -07:00
David Markowitz
83780affbd MutableModelBehaviorComponent pass 2023-07-09 20:56:04 -07:00
David Markowitz
7ca9e59ef2 MultiZoneEntranceComponent pass 2023-07-09 20:52:48 -07:00
David Markowitz
d2a7e147cc I am throwing this one out later. 2023-07-09 20:44:08 -07:00
David Markowitz
8a512e5c76 MovementAiComponent pass 2023-07-09 20:38:47 -07:00
David Markowitz
2528e02b98 add final 2023-07-09 19:48:23 -07:00
David Markowitz
1b7be5d7db Merge branch 'components-wheeeee' of https://github.com/DarkflameUniverse/DarkflameServer into components-wheeeee 2023-07-09 19:45:35 -07:00
EmosewaMC
790bd6c03b Merge branch 'components-wheeeee' of https://github.com/DarkflameUniverse/DarkflameServer into components-wheeeee 2023-07-08 01:31:52 -07:00
EmosewaMC
28fbe20b97 ModuleAssemblyComponent pass 2023-07-08 01:31:26 -07:00
David Markowitz
cf53e35af5 SwitchComponent pass 2023-07-07 12:13:26 -07:00
EmosewaMC
5301346ed5 ModelBehaviorComponent pass 2023-07-05 20:37:16 -07:00
EmosewaMC
001f6a75eb MissionOfferComponent pass 2023-07-05 20:30:52 -07:00
EmosewaMC
950a1fe423 MissionComponent pass 2023-07-05 19:32:28 -07:00
197d1bcdee Merge branch 'main' into components-wheeeee 2023-07-05 07:59:52 -05:00
EmosewaMC
68a2a04e5d LUPExhibitComponent cleanup 2023-07-04 22:52:51 -07:00
EmosewaMC
cfec9801a8 LevelProgressionComponent cleanup 2023-07-04 22:38:46 -07:00
EmosewaMC
8ede5b87ca Fix compilation error; add final specifiers 2023-07-04 22:38:35 -07:00
EmosewaMC
c22040c6eb ItemComponent Pass
make use of Writing a length
add C include guard
2023-07-04 22:17:44 -07:00
6fb1786cf1 Add component order array
fix loop in inven comp
2023-06-29 12:44:06 -05:00
David Markowitz
81404d9671 InventoryComponent pass
- reduce scope usage
- bouncing returns
- std::for_each in some contexts
- extra nullptr checks
- constiness
- extra logs
- scoped enum type for eItemType
- lol serialization.
2023-06-28 01:20:41 -07:00
David Markowitz
0544eeba1f Add default constructor for EquipmentItem
Change struct constructors to proper constructors.
2023-06-27 02:01:43 -07:00
David Markowitz
c2fe7f6205 HavokVehicleComponent improvements
serialization, moving stuff to header, removal of update as we serialize dirty position so often this check is pointless.
2023-06-26 23:28:23 -07:00
David Markowitz
f55bec026d Donation Component serialization 2023-06-26 23:17:59 -07:00
David Markowitz
478b6ff6a9 Move empty definitions to header 2023-06-26 23:01:46 -07:00
David Markowitz
7c1265911c move ctor to header for collectible 2023-06-26 22:59:57 -07:00
David Markowitz
c6063aac66 Component serialization updates
- Fix serialization in multiple components so they don't get dirty flags reset when it was not intentional
2023-06-26 22:58:35 -07:00
David Markowitz
2abcb142ad Character fixes - get it compiling again
- Pass componentID to activity component constructor
- use int componentid so -1 can denote no component
2023-06-26 22:39:15 -07:00
David Markowitz
d9a3bea6d5 Merge branch 'components-wheeeee' of https://github.com/DarkflameUniverse/DarkflameServer into components-wheeeee 2023-06-26 21:59:02 -07:00
David Markowitz
fdcfbdee85 BuildBorderCleanup 2023-06-26 21:58:56 -07:00
fd182d222f Update activing and racing components
Hopefully not going to rename them for a third time, no way
2023-06-26 15:06:33 -05:00
68f90b7136 rename possessor to possession 2023-06-26 12:36:36 -05:00
David Markowitz
d29287f9d9 Buff Component fixes 2023-06-26 01:49:56 -07:00
David Markowitz
06acd23cb7 Use of final 2023-06-26 01:23:22 -07:00
David Markowitz
a5611e9c7f Bouncer cleanup
- Correct variable names
- Make serialization more efficient and make sense
- remove unneeded members
- more descriptive logs
- Move work to Startup
2023-06-26 01:19:49 -07:00
David Markowitz
34cfd45d40 CombatAI and Vendor 2023-06-26 00:15:25 -07:00
David Markowitz
ec9278286b Use better naming
- Remove use of Base.  It is implied if you inherit that the class inherited from is a Base.
- Fix compilation errors from said change.
2023-06-25 22:00:01 -07:00
David Markowitz
9121bf41c5 Entity work
- Add in bool cheks
- Fix component class files so they compile and link
- Fin inheritance
2023-06-25 21:47:35 -07:00
David Markowitz
fee1025982 Remove commented out logic, add ghosting exclusion
We dont create items that are in an inventory, so that path is pointless for us.
2023-06-25 21:05:25 -07:00
David Markowitz
3f328a18be Merge upstream ghosting candidate changes 2023-06-25 21:01:10 -07:00
485a88dfd4 move vars to be local 2023-06-24 01:56:13 -05:00
c237c16c33 group physicstogether in heirarchy 2023-06-23 16:56:56 -05:00
d44b18e38f rough logic around loading physics
as well as modelbehaviors
2023-06-23 12:01:41 -05:00
d153d66e26 Merge branch 'item-component' into components-wheeeee 2023-06-23 11:01:16 -05:00
34d22d2d0d AchievementVendor scaffold 2023-06-23 10:56:05 -05:00
bcbc5882dc Merge branch 'main' into components-wheeeee 2023-06-23 10:36:21 -05:00
f27e0400e7 scaffold activity based components 2023-06-23 10:30:03 -05:00
c78760db59 make inheritence clearer 2023-06-23 09:21:19 -05:00
907e045904 some tweaks 2023-06-23 01:10:06 -05:00
dc96fcba85 Vendor cleanup and start Donation Vendor impl 2023-06-23 00:56:25 -05:00
David Markowitz
e180430ede Finish header re-ordering 2023-06-17 18:41:51 -07:00
David Markowitz
1bdec00a61 More organization of header 2023-06-17 02:39:33 -07:00
David Markowitz
68a5cc1d89 Use better API terminology for radii
- SetProximityRadius just calls AddProximityRadius so its clear what is going on.
- created struct BoxDimensions for clear reading of what the floats are
2023-06-17 02:01:42 -07:00
David Markowitz
be17d1a467 Update DestroyableComponent.cpp 2023-06-16 02:02:33 -07:00
David Markowitz
a992a28088 Merge from upstream 2023-06-16 02:01:01 -07:00
David Markowitz
891648288a Organize Entity header
Probably the third or fourth pass of this darn header...  Just keep making it better every time
Rename some functions to make more sense to a reader
Use different method for Observing/subscribing to component events
Get rid of abomination of overloading GetParentUser
2023-06-16 01:56:02 -07:00
David Markowitz
92006123b8 Another consistency pass
- change NotifyObject to use u16 string
- move stuff to header that is inline
- use u16strings instead of converting to u16 string
- move entity to dEntity
2023-06-16 01:01:13 -07:00
David Markowitz
9a9b9aa813 Merge branch 'components-wheeeee' of https://github.com/DarkflameUniverse/DarkflameServer into components-wheeeee 2023-06-15 23:58:59 -07:00
David Markowitz
ea975965ca Fix typo 2023-06-15 23:58:39 -07:00
EmosewaMC
35e5d8497b Remove empty destructors 2023-06-15 02:32:30 -07:00
EmosewaMC
4d57eff946 Update includes 2023-06-15 02:28:27 -07:00
EmosewaMC
2a8f40f8e8 Finish file 2023-06-15 02:13:25 -07:00
EmosewaMC
355f4f4df8 Update Entity.cpp 2023-06-14 23:17:30 -07:00
EmosewaMC
451f7e76d7 switch to unique_ptrs for callback timers 2023-06-14 23:16:31 -07:00
EmosewaMC
83065dfb6f I havent checked if this compiled 2023-06-14 19:01:31 -07:00
fdd98ab825 fix other script calls 2023-06-13 22:01:51 -05:00
David Markowitz
31be1fbe4c remove script stuff 2023-06-13 19:55:27 -07:00
David Markowitz
d8e2e92428 use get 2023-06-13 19:47:14 -07:00
e389a619ad update heirarchy 2023-06-13 10:31:39 -05:00
David Markowitz
45bcc80a1b doesnt compile 2023-06-12 15:32:46 -07:00
David Markowitz
b2fee29ee0 Better log and comment 2023-06-12 04:37:38 -07:00
David Markowitz
326c495776 Use more clear control paths 2023-06-12 04:27:14 -07:00
David Markowitz
d224a86e93 Use only 1 script component per Entity
Serialization won't make sense if we allow two of the same component like that
2023-06-12 04:13:06 -07:00
David Markowitz
f9ac0a9dec few more pointer specifiers 2023-06-12 04:04:45 -07:00
David Markowitz
3f3810519d Extra * on one auto 2023-06-12 04:01:45 -07:00
David Markowitz
5be9146662 Specify auto ptr 2023-06-12 04:00:44 -07:00
David Markowitz
262b6ebb58 Remove old Entity Initialize 2023-06-12 03:55:07 -07:00
David Markowitz
6f38a150d3 Minor formatting change 2023-06-12 03:47:57 -07:00
David Markowitz
36c44ecc83 Fully re-implemented initialize 2023-06-12 01:29:43 -07:00
David Markowitz
fc719cbb0a Comment out done code 2023-06-11 04:46:22 -07:00
David Markowitz
f78ea1bbc9 whitespace and comments in quickbuild 2023-06-11 04:40:19 -07:00
David Markowitz
b43e5c2165 doesnt have a component 2023-06-11 04:39:28 -07:00
David Markowitz
5f139c75e0 Quickbuild and Destroyable reintegration 2023-06-11 04:37:53 -07:00
David Markowitz
77dc6ff312 Continued re-integration of Entity::Initialize 2023-06-11 03:06:18 -07:00
David Markowitz
0b5df9f0b1 Destroyable 2023-06-10 05:10:00 -07:00
David Markowitz
b91f84d884 Collectible, Item, further re-implement initialize 2023-06-10 04:46:48 -07:00
David Markowitz
cebe3c732a Update to actually work with component list 2023-06-10 00:14:20 -07:00
David Markowitz
5714ac558e Use std algorithms 2023-06-10 00:03:07 -07:00
David Markowitz
2a2799793d More robust tests 2023-06-09 23:05:44 -07:00
David Markowitz
1c23f3c030 Add test for component Whitelists 2023-06-09 23:02:28 -07:00
a68fa69e7a Rename RebuildComponent to QuickbuildComponent 2023-06-09 17:12:57 -05:00
David Markowitz
ddc5f0e117 Merge branch 'main' into components-wheeeee 2023-06-09 04:05:55 -07:00
David Markowitz
e3a716a9cf Further re-implement Entity::Initialize
wheee
2023-06-09 04:05:04 -07:00
David Markowitz
0b37dc1e4d Update includes for propertyEnteranceComponent
Class must be complete to be used in a MoveAssignment
2023-06-09 04:03:58 -07:00
David Markowitz
f2d28cc0bd Update CMakeLists.txt 2023-06-09 04:02:45 -07:00
David Markowitz
5da776a084 Add blank classes for some Components
Racing Stats

Minigame Control
2023-06-09 04:02:40 -07:00
David Markowitz
6f057204be Rename some variables
- Add order for loading Components
- Enforce all components have Entity* in the first argument
2023-06-09 02:46:01 -07:00
David Markowitz
f555ba8c25 Rename from GetOwningEntity to GetParentEntity 2023-06-09 01:28:01 -07:00
David Markowitz
e2dfa1809d Replace all auto with auto*
For components
2023-06-09 01:27:05 -07:00
David Markowitz
62aa863997 Remove shared pointer, ODR of componentType variable 2023-06-09 01:22:45 -07:00
EmosewaMC
ec00f5fd9d holy mother of const
Use const everywhere that makes sense
return const variables when it makes sense
const functions and variables again, where it makes sense
No raw access and modifications to protected members
Move template definitions to tcc file

idk how I feel about this one
2023-06-09 01:04:42 -07:00
d11e2db887 update component names, document heirarchy 2023-06-08 10:29:17 -05:00
David Markowitz
9e9e4dc087 Move to shared pointer 2023-06-07 00:23:50 -07:00
David Markowitz
ea9d0d8592 I hope this works 2023-06-06 20:48:30 -07:00
David Markowitz
716a5fcf37 Rename some variables in Component
And add more virtuals
2023-06-06 18:59:53 -07:00
15988afb4c hey it compiles 2023-03-20 21:14:52 -05:00
5e9a956bed Merge branch 'main' into item-component 2023-03-20 21:05:58 -05:00
c2d5be0e5d include 2023-03-20 20:59:11 -05:00
2ae9a92b55 fix cmake 2023-02-10 21:43:39 -06:00
50978620d8 Merge branch 'main' into item-component 2023-02-10 21:35:53 -06:00
c168f6c970 Break out the model component
into the model, item, and mutable model behaviors
2023-02-10 21:33:30 -06:00
422 changed files with 9922 additions and 12755 deletions

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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.

View File

@@ -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.

View File

@@ -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.

View File

@@ -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();

View File

@@ -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;
}

View File

@@ -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());
}
}

View File

@@ -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
/*!

View File

@@ -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);

View File

@@ -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;

View File

@@ -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

View 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__

View File

@@ -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__

View File

@@ -0,0 +1,13 @@
#ifndef __EUGCMODERATIONSTATUS__H__
#define __EUGCMODERATIONSTATUS__H__
#include <cstdint>
enum class eUgcModerationStatus : uint32_t {
NoStatus,
Approved,
Rejected,
};
#endif //!__EUGCMODERATIONSTATUS__H__

View File

@@ -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;
}

View File

@@ -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);

View File

@@ -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;
}

View File

@@ -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);
};

View File

@@ -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;
}

View File

@@ -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;
};

View File

@@ -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;
}

View File

@@ -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);

View File

@@ -1,5 +1,4 @@
set(DGAME_SOURCES "Character.cpp"
"Entity.cpp"
"EntityManager.cpp"
"LeaderboardManager.cpp"
"Player.cpp"

View File

@@ -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 {

File diff suppressed because it is too large Load Diff

View File

@@ -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;
}

View File

@@ -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);
}

View File

@@ -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;

View File

@@ -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;";

View File

@@ -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__

View File

@@ -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);
}
}
}

View File

@@ -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);

View File

@@ -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;

View File

@@ -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();

View File

@@ -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;
}

View File

@@ -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;

View File

@@ -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);

View File

@@ -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);

View File

@@ -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;

View File

@@ -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();

View File

@@ -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);

View File

@@ -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) {

View File

@@ -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>();

View File

@@ -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;
}

View File

@@ -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);

View File

@@ -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);

View File

@@ -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);

View File

@@ -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(){

View File

@@ -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);
}
}

View File

@@ -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);

View File

@@ -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;

View File

@@ -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);

View File

@@ -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;

View File

@@ -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);

View File

@@ -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>();

View File

@@ -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) {

View File

@@ -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;

View File

@@ -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);

View File

@@ -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();

View File

@@ -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;

View File

@@ -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>();

View File

@@ -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);

View File

@@ -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);
}
}

View File

@@ -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;

View File

@@ -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() {

View File

@@ -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;

View File

@@ -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;

View File

@@ -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();

View File

@@ -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);

View File

@@ -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);

View File

@@ -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);

View File

@@ -1,8 +1,9 @@
#include "eMissionTaskType.h"
#ifndef __ACHIEVEMENTCACHEKEY__H__
#define __ACHIEVEMENTCACHEKEY__H__
#include "eMissionTaskType.h"
#include "GeneralUtils.h"
class AchievementCacheKey {
public:
AchievementCacheKey() {

View File

@@ -0,0 +1,5 @@
#include "AchievementVendorComponent.h"
AchievementVendorComponent::AchievementVendorComponent(Entity* parent) : VendorComponent(parent) {
}

View 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__

View 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);
}

View 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

View File

@@ -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() {

View File

@@ -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

View File

@@ -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();
});
}

View File

@@ -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

View File

@@ -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);
}
}

View File

@@ -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

View File

@@ -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());
}
}

View File

@@ -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

View File

@@ -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)

View File

@@ -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);
}

View File

@@ -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

View File

@@ -0,0 +1,7 @@
#include "CollectibleComponent.h"
#include "Entity.h"
void CollectibleComponent::Startup() {
m_CollectibleId = GetParentEntity()->GetVarAs<int32_t>(u"collectible_id");
}

View 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__

View File

@@ -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;
}

View File

@@ -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;
};

View 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
```

View File

@@ -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,

View File

@@ -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

View File

@@ -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);
}
}
}

View File

@@ -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

View File

@@ -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