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
270 changed files with 7206 additions and 7955 deletions

View File

@@ -10,7 +10,7 @@ trim_trailing_whitespace=true
end_of_line=lf end_of_line=lf
insert_final_newline=true 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 vc_generate_documentation_comments=doxygen_slash_star

View File

@@ -305,7 +305,7 @@ file(
file( file(
GLOB HEADERS_DGAME GLOB HEADERS_DGAME
LIST_DIRECTORIES false 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/dGameMessages/GameMessages.h
${PROJECT_SOURCE_DIR}/dGame/EntityManager.h ${PROJECT_SOURCE_DIR}/dGame/EntityManager.h
${PROJECT_SOURCE_DIR}/dScripts/CppScripts.h ${PROJECT_SOURCE_DIR}/dScripts/CppScripts.h

View File

@@ -17,7 +17,7 @@ __dynamic=1
# Set __compile_backtrace__ to 1 to compile the backtrace library instead of using system libraries. # Set __compile_backtrace__ to 1 to compile the backtrace library instead of using system libraries.
# __compile_backtrace__=1 # __compile_backtrace__=1
# Set to the number of jobs (make -j equivalent) to compile the mariadbconn files with. # 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. # When set to 1 and uncommented, compiling and linking testing folders and libraries will be done.
__enable_testing__=1 __enable_testing__=1
# The path to OpenSSL. Change this if your OpenSSL install path is different than the default. # The path to OpenSSL. Change this if your OpenSSL install path is different than the default.

View File

@@ -33,7 +33,7 @@ public:
virtual void WriteToPacket(RakNet::BitStream* packet) = 0; virtual void WriteToPacket(RakNet::BitStream* packet) = 0;
virtual const std::u16string& GetKey() = 0; virtual const std::u16string& GetKey() const = 0;
virtual eLDFType GetValueType() = 0; virtual eLDFType GetValueType() = 0;
@@ -117,7 +117,7 @@ public:
/*! /*!
\return The key \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 //! Gets the LDF Type
/*! /*!

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 LWOINSTANCEID_INVALID = -1; //!< Invalid LWOINSTANCEID
const uint16_t LWOMAPID_INVALID = -1; //!< Invalid LWOMAPID const uint16_t LWOMAPID_INVALID = -1; //!< Invalid LWOMAPID
const uint64_t LWOZONEID_INVALID = 0; //!< Invalid LWOZONEID 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; const float PI = 3.14159f;

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, CHARACTER,
SCRIPT, SCRIPT,
BOUNCER, BOUNCER,
BUFF, // buff is really 98, this is DESTROYABLE DESTROYABLE,
GHOST, GHOST,
SKILL, SKILL,
SPAWNER, SPAWN,
ITEM, ITEM,
REBUILD, MODULAR_BUILD,
REBUILD_START, BUILD_CONTROLLER,
REBUILD_ACTIVATOR, BUILD_ACTIVATOR,
ICON_ONLY, ICON_ONLY,
VENDOR, VENDOR,
INVENTORY, INVENTORY,
@@ -33,8 +33,8 @@ enum class eReplicaComponentType : uint32_t {
PET, PET,
PLATFORM_BOUNDARY, PLATFORM_BOUNDARY,
MODULE, MODULE,
ARCADE, JETPACKPAD,
VEHICLE_PHYSICS, // Havok demo based HAVOK_VEHICLE_PHYSICS,
MOVEMENT_AI, MOVEMENT_AI,
EXHIBIT, EXHIBIT,
OVERHEAD_ICON, OVERHEAD_ICON,
@@ -46,23 +46,23 @@ enum class eReplicaComponentType : uint32_t {
SCRIPTED_ACTIVITY, SCRIPTED_ACTIVITY,
PHANTOM_PHYSICS, PHANTOM_PHYSICS,
SPRINGPAD, SPRINGPAD,
MODEL, MODEL_BEHAVIOR,
PROPERTY_ENTRANCE, PROPERTY_ENTRANCE,
FX, FX,
PROPERTY_MANAGEMENT, PROPERTY_MANAGEMENT,
VEHICLE_PHYSICS_NEW, // internal physics based on havok VEHICLE_PHYSICS,
PHYSICS_SYSTEM, PHYSICS_SYSTEM,
QUICK_BUILD, QUICK_BUILD,
SWITCH, SWITCH,
ZONE_CONTROL, // Minigame MINIGAME_CONTROL,
CHANGLING, CHANGLING_BUILD,
CHOICE_BUILD, CHOICE_BUILD,
PACKAGE, PACKAGE,
SOUND_REPEATER, SOUND_REPEATER,
SOUND_AMBIENT_2D, SOUND_AMBIENT_2D,
SOUND_AMBIENT_3D, SOUND_AMBIENT_3D,
PRECONDITION, PRECONDITION,
PLAYER_FLAG, FLAG,
CUSTOM_BUILD_ASSEMBLY, CUSTOM_BUILD_ASSEMBLY,
BASE_COMBAT_AI, BASE_COMBAT_AI,
MODULE_ASSEMBLY, MODULE_ASSEMBLY,
@@ -71,8 +71,8 @@ enum class eReplicaComponentType : uint32_t {
GENERIC_ACTIVATOR, GENERIC_ACTIVATOR,
PROPERTY_VENDOR, PROPERTY_VENDOR,
HF_LIGHT_DIRECTION_GADGET, HF_LIGHT_DIRECTION_GADGET,
ROCKET_LAUNCH, ROCKET_LAUNCHPAD_CONTROL,
ROCKET_LANDING, ROCKET_ANIMATION_CONTROL,
TRIGGER, TRIGGER,
DROPPED_LOOT, DROPPED_LOOT,
RACING_CONTROL, RACING_CONTROL,
@@ -84,7 +84,7 @@ enum class eReplicaComponentType : uint32_t {
SOUND_TRIGGER, SOUND_TRIGGER,
PROXIMITY_MONITOR, PROXIMITY_MONITOR,
RACING_SOUND_TRIGGER, RACING_SOUND_TRIGGER,
CHAT, CHAT_BUBBLE,
FRIENDS_LIST, FRIENDS_LIST,
GUILD, GUILD,
LOCAL_SYSTEM, LOCAL_SYSTEM,
@@ -101,12 +101,12 @@ enum class eReplicaComponentType : uint32_t {
TRADE, TRADE,
USER_CONTROL, USER_CONTROL,
IGNORE_LIST, IGNORE_LIST,
ROCKET_LAUNCH_LUP, MULTI_ZONE_ENTRANCE,
BUFF_REAL, // the real buff component, should just be name BUFF BUFF,
INTERACTION_MANAGER, INTERACTION_MANAGER,
DONATION_VENDOR, DONATION_VENDOR,
COMBAT_MEDIATOR, COMBAT_MEDIATOR,
COMMENDATION_VENDOR, ACHIEVEMENT_VENDOR,
GATE_RUSH_CONTROL, GATE_RUSH_CONTROL,
RAIL_ACTIVATOR, RAIL_ACTIVATOR,
ROLLER, ROLLER,
@@ -114,14 +114,14 @@ enum class eReplicaComponentType : uint32_t {
CRAFTING, CRAFTING,
POSSESSABLE, POSSESSABLE,
LEVEL_PROGRESSION, LEVEL_PROGRESSION,
POSSESSOR, POSSESSION,
MOUNT_CONTROL, MOUNT_CONTROL,
UNKNOWN_112, UNKNOWN_112,
PROPERTY_PLAQUE, PROPERTY_PLAQUE,
BUILD_BORDER, BUILD_BORDER,
UNKNOWN_115, UNKNOWN_115,
CULLING_PLANE, CULLING_PLANE,
DESTROYABLE = 1000 // Actually 7 NUMBER_OF_COMPONENTS
}; };
#endif //!__EREPLICACOMPONENTTYPE__H__ #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

@@ -90,4 +90,11 @@ int32_t CDComponentsRegistryTable::GetByIDAndType(uint32_t id, eReplicaComponent
return defaultValue; return defaultValue;
#endif #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 // Custom Classes
#include "CDTable.h" #include "CDTable.h"
#include "dCommonVars.h"
enum class eReplicaComponentType : uint32_t; enum class eReplicaComponentType : uint32_t;
struct CDComponentsRegistry { struct CDComponentsRegistry {
@@ -18,4 +19,5 @@ private:
public: public:
CDComponentsRegistryTable(); CDComponentsRegistryTable();
int32_t GetByIDAndType(uint32_t id, eReplicaComponentType componentType, int32_t defaultValue = 0); 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 "CDItemComponentTable.h"
#include "GeneralUtils.h" #include "GeneralUtils.h"
#include "eItemType.h"
CDItemComponent CDItemComponentTable::Default = {}; CDItemComponent CDItemComponentTable::Default = {};
//! Constructor //! Constructor
@@ -74,8 +76,8 @@ CDItemComponentTable::CDItemComponentTable(void) {
#endif #endif
} }
const CDItemComponent& CDItemComponentTable::GetItemComponentByID(unsigned int skillID) { const CDItemComponent& CDItemComponentTable::GetItemComponentByID(unsigned int id) {
const auto& it = this->entries.find(skillID); const auto& it = this->entries.find(id);
if (it != this->entries.end()) { if (it != this->entries.end()) {
return it->second; return it->second;
} }
@@ -83,11 +85,11 @@ const CDItemComponent& CDItemComponentTable::GetItemComponentByID(unsigned int s
#ifndef CDCLIENT_CACHE_ALL #ifndef CDCLIENT_CACHE_ALL
std::stringstream query; 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()); auto tableData = CDClientDatabase::ExecuteQuery(query.str());
if (tableData.eof()) { if (tableData.eof()) {
entries.insert(std::make_pair(skillID, Default)); entries.insert(std::make_pair(id, Default));
return Default; return Default;
} }
@@ -98,7 +100,7 @@ const CDItemComponent& CDItemComponentTable::GetItemComponentByID(unsigned int s
entry.baseValue = tableData.getIntField("baseValue", -1); entry.baseValue = tableData.getIntField("baseValue", -1);
entry.isKitPiece = tableData.getIntField("isKitPiece", -1) == 1 ? true : false; entry.isKitPiece = tableData.getIntField("isKitPiece", -1) == 1 ? true : false;
entry.rarity = tableData.getIntField("rarity", 0); 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.itemInfo = tableData.getInt64Field("itemInfo", -1);
entry.inLootTable = tableData.getIntField("inLootTable", -1) == 1 ? true : false; entry.inLootTable = tableData.getIntField("inLootTable", -1) == 1 ? true : false;
entry.inVendor = tableData.getIntField("inVendor", -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(); tableData.nextRow();
} }
const auto& it2 = this->entries.find(skillID); const auto& it2 = this->entries.find(id);
if (it2 != this->entries.end()) { if (it2 != this->entries.end()) {
return it2->second; return it2->second;
} }

View File

@@ -4,13 +4,15 @@
#include "CDTable.h" #include "CDTable.h"
#include "dCommonVars.h" #include "dCommonVars.h"
enum class eItemType : int32_t;
struct CDItemComponent { struct CDItemComponent {
unsigned int id; //!< The Component ID unsigned int id; //!< The Component ID
std::string equipLocation; //!< The equip location std::string equipLocation; //!< The equip location
unsigned int baseValue; //!< The monetary base value of the item unsigned int baseValue; //!< The monetary base value of the item
bool isKitPiece; //!< Whether or not the item belongs to a kit bool isKitPiece; //!< Whether or not the item belongs to a kit
unsigned int rarity; //!< The rarity of the item 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 int64_t itemInfo; //!< The item info
bool inLootTable; //!< Whether or not the item is in a loot table bool inLootTable; //!< Whether or not the item is in a loot table
bool inVendor; //!< Whether or not the item is in a vendor inventory 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); static std::map<LOT, uint32_t> ParseCraftingCurrencies(const CDItemComponent& itemComponent);
// Gets an entry by ID // Gets an entry by ID
const CDItemComponent& GetItemComponentByID(unsigned int skillID); const CDItemComponent& GetItemComponentByID(unsigned int id);
static CDItemComponent Default; static CDItemComponent Default;
}; };

View File

@@ -37,10 +37,7 @@ CDPhysicsComponentTable::~CDPhysicsComponentTable() {
} }
CDPhysicsComponent* CDPhysicsComponentTable::GetByID(unsigned int componentID) { CDPhysicsComponent* CDPhysicsComponentTable::GetByID(unsigned int componentID) {
for (auto e : m_entries) { auto itr = m_entries.find(componentID);
if (e.first == componentID) return e.second; return itr != m_entries.end() ? itr->second : nullptr;
}
return nullptr;
} }

View File

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

File diff suppressed because it is too large Load Diff

View File

@@ -1,503 +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;
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,7 @@
#include "eGameMasterLevel.h" #include "eGameMasterLevel.h"
#include "eReplicaComponentType.h" #include "eReplicaComponentType.h"
#include "eReplicaPacketType.h" #include "eReplicaPacketType.h"
#include "CollectibleComponent.h"
EntityManager* EntityManager::m_Address = nullptr; EntityManager* EntityManager::m_Address = nullptr;
@@ -518,16 +519,18 @@ void EntityManager::UpdateGhosting(Player* player) {
entity->SetObservers(entity->GetObservers() - 1); entity->SetObservers(entity->GetObservers() - 1);
} else if (!observed && ghostingDistanceMin > distance) { } else if (!observed && ghostingDistanceMin > distance) {
// Check collectables, don't construct if it has been collected // 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) { if (collectionId != 0) {
collectionId = static_cast<uint32_t>(collectionId) + static_cast<uint32_t>(Game::server->GetZoneID() << 8); collectionId = static_cast<uint32_t>(collectionId) + static_cast<uint32_t>(Game::server->GetZoneID() << 8);
if (missionComponent->HasCollectible(collectionId)) { if (missionComponent->HasCollectible(collectionId)) {
continue; continue;
}
} }
} }
player->ObserveEntity(id); player->ObserveEntity(id);
ConstructEntity(entity, player->GetSystemAddress()); ConstructEntity(entity, player->GetSystemAddress());
@@ -599,7 +602,7 @@ void EntityManager::ScheduleForKill(Entity* entity) {
if (!entity) if (!entity)
return; return;
SwitchComponent* switchComp = entity->GetComponent<SwitchComponent>(); auto* switchComp = entity->GetComponent<SwitchComponent>();
if (switchComp) { if (switchComp) {
entity->TriggerEvent(eTriggerEventType::DEACTIVATED, entity); entity->TriggerEvent(eTriggerEventType::DEACTIVATED, entity);
} }

View File

@@ -51,21 +51,17 @@ User* Player::GetParentUser() const {
return m_ParentUser; return m_ParentUser;
} }
SystemAddress Player::GetSystemAddress() const {
return m_SystemAddress;
}
void Player::SetSystemAddress(const SystemAddress& value) { void Player::SetSystemAddress(const SystemAddress& value) {
m_SystemAddress = value; m_SystemAddress = value;
} }
void Player::SetRespawnPos(const NiPoint3 position) { void Player::SetRespawnPosition(const NiPoint3& position) {
m_respawnPos = position; m_respawnPos = position;
m_Character->SetRespawnPoint(dZoneManager::Instance()->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; m_respawnRot = rotation;
} }
@@ -251,7 +247,7 @@ const std::vector<Player*>& Player::GetAllPlayers() {
return m_Players; return m_Players;
} }
uint64_t Player::GetDroppedCoins() { uint64_t Player::GetDroppedCoins() const {
return m_DroppedCoins; return m_DroppedCoins;
} }
@@ -286,16 +282,12 @@ Player::~Player() {
if (IsPlayer()) { if (IsPlayer()) {
Entity* zoneControl = EntityManager::Instance()->GetZoneControlEntity(); Entity* zoneControl = EntityManager::Instance()->GetZoneControlEntity();
for (CppScripts::Script* script : CppScripts::GetEntityScripts(zoneControl)) { zoneControl->GetScript()->OnPlayerExit(zoneControl, this);
script->OnPlayerExit(zoneControl, this);
}
std::vector<Entity*> scriptedActs = EntityManager::Instance()->GetEntitiesByComponent(eReplicaComponentType::SCRIPTED_ACTIVITY); std::vector<Entity*> scriptedActs = EntityManager::Instance()->GetEntitiesByComponent(eReplicaComponentType::SCRIPTED_ACTIVITY);
for (Entity* scriptEntity : scriptedActs) { for (Entity* scriptEntity : scriptedActs) {
if (scriptEntity->GetObjectID() != zoneControl->GetObjectID()) { // Don't want to trigger twice on instance worlds if (scriptEntity->GetObjectID() != zoneControl->GetObjectID()) { // Don't want to trigger twice on instance worlds
for (CppScripts::Script* script : CppScripts::GetEntityScripts(scriptEntity)) { scriptEntity->GetScript()->OnPlayerExit(scriptEntity, this);
script->OnPlayerExit(scriptEntity, this);
}
} }
} }
} }

View File

@@ -20,7 +20,7 @@ public:
User* GetParentUser() const override; User* GetParentUser() const override;
SystemAddress GetSystemAddress() const override; const SystemAddress GetSystemAddress() const override { return m_SystemAddress; }
NiPoint3 GetRespawnPosition() const override; NiPoint3 GetRespawnPosition() const override;
@@ -36,7 +36,7 @@ public:
std::map<LWOOBJID, Loot::Info>& GetDroppedLoot(); std::map<LWOOBJID, Loot::Info>& GetDroppedLoot();
uint64_t GetDroppedCoins(); uint64_t GetDroppedCoins() const;
/** /**
* Setters * Setters
@@ -44,9 +44,9 @@ public:
void SetSystemAddress(const SystemAddress& value) override; 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); void SetGhostReferencePoint(const NiPoint3& value);

View File

@@ -219,7 +219,7 @@ void Trade::SendUpdateToOther(LWOOBJID participant) {
if (inventoryComponent == nullptr) return; if (inventoryComponent == nullptr) return;
for (const auto tradeItem : itemIds) { for (const auto tradeItem : itemIds) {
auto* item = inventoryComponent->FindItemById(tradeItem.itemId); auto item = inventoryComponent->FindItemById(tradeItem.itemId);
if (item == nullptr) return; if (item == nullptr) return;

View File

@@ -4,7 +4,6 @@
#include "BehaviorBranchContext.h" #include "BehaviorBranchContext.h"
#include "BuffComponent.h" #include "BuffComponent.h"
void ApplyBuffBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) { void ApplyBuffBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) {
auto* entity = EntityManager::Instance()->GetEntity(branch.target == LWOOBJID_EMPTY ? context->originator : branch.target); auto* entity = EntityManager::Instance()->GetEntity(branch.target == LWOOBJID_EMPTY ? context->originator : branch.target);

View File

@@ -7,7 +7,7 @@
#include "dLogger.h" #include "dLogger.h"
#include "BehaviorBranchContext.h" #include "BehaviorBranchContext.h"
#include "BehaviorContext.h" #include "BehaviorContext.h"
#include "RebuildComponent.h" #include "QuickBuildComponent.h"
#include "DestroyableComponent.h" #include "DestroyableComponent.h"
#include "Game.h" #include "Game.h"
#include "dLogger.h" #include "dLogger.h"

View File

@@ -162,7 +162,7 @@ void BasicAttackBehavior::DoBehaviorCalculation(BehaviorContext* context, RakNet
} }
auto* destroyableComponent = targetEntity->GetComponent<DestroyableComponent>(); auto* destroyableComponent = targetEntity->GetComponent<DestroyableComponent>();
if (!destroyableComponent || !destroyableComponent->GetParent()) { if (!destroyableComponent || !destroyableComponent->GetParentEntity()) {
Game::logger->Log("BasicAttackBehavior", "No destroyable component on %llu", branch.target); Game::logger->Log("BasicAttackBehavior", "No destroyable component on %llu", branch.target);
return; return;
} }
@@ -213,7 +213,7 @@ void BasicAttackBehavior::DoBehaviorCalculation(BehaviorContext* context, RakNet
bitStream->Write(armorDamageDealt); bitStream->Write(armorDamageDealt);
bitStream->Write(healthDamageDealt); bitStream->Write(healthDamageDealt);
bitStream->Write(targetEntity->GetIsDead()); bitStream->Write(targetEntity->IsDead());
} }
bitStream->Write(successState); bitStream->Write(successState);

View File

@@ -13,7 +13,7 @@
#include "DestroyableComponent.h" #include "DestroyableComponent.h"
#include "EchoSyncSkill.h" #include "EchoSyncSkill.h"
#include "PhantomPhysicsComponent.h" #include "PhantomPhysicsComponent.h"
#include "RebuildComponent.h" #include "QuickBuildComponent.h"
#include "eReplicaComponentType.h" #include "eReplicaComponentType.h"
#include "eConnectionType.h" #include "eConnectionType.h"
@@ -331,8 +331,8 @@ std::vector<LWOOBJID> BehaviorContext::GetValidTargets(int32_t ignoreFaction, in
} }
if (ignoreFaction || includeFaction || (!entity->HasComponent(eReplicaComponentType::PHANTOM_PHYSICS) && targets.empty())) { if (ignoreFaction || includeFaction || (!entity->HasComponent(eReplicaComponentType::PHANTOM_PHYSICS) && targets.empty())) {
DestroyableComponent* destroyableComponent; auto* destroyableComponent = entity->GetComponent<DestroyableComponent>();
if (!entity->TryGetComponent(eReplicaComponentType::DESTROYABLE, destroyableComponent)) { if (!destroyableComponent) {
return targets; return targets;
} }

View File

@@ -16,7 +16,7 @@ void HealBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bit_strea
return; return;
} }
auto* destroyable = static_cast<DestroyableComponent*>(entity->GetComponent(eReplicaComponentType::DESTROYABLE)); auto* destroyable = entity->GetComponent<DestroyableComponent>();
if (destroyable == nullptr) { if (destroyable == nullptr) {
Game::logger->Log("HealBehavior", "Failed to find destroyable component for %(llu)!", branch.target); Game::logger->Log("HealBehavior", "Failed to find destroyable component for %(llu)!", branch.target);

View File

@@ -4,7 +4,7 @@ void LootBuffBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bitSt
auto target = EntityManager::Instance()->GetEntity(context->caster); auto target = EntityManager::Instance()->GetEntity(context->caster);
if (!target) return; if (!target) return;
auto controllablePhysicsComponent = target->GetComponent<ControllablePhysicsComponent>(); auto* controllablePhysicsComponent = target->GetComponent<ControllablePhysicsComponent>();
if (!controllablePhysicsComponent) return; if (!controllablePhysicsComponent) return;
controllablePhysicsComponent->AddPickupRadiusScale(m_Scale); controllablePhysicsComponent->AddPickupRadiusScale(m_Scale);
@@ -22,7 +22,7 @@ void LootBuffBehavior::UnCast(BehaviorContext* context, BehaviorBranchContext br
auto target = EntityManager::Instance()->GetEntity(context->caster); auto target = EntityManager::Instance()->GetEntity(context->caster);
if (!target) return; if (!target) return;
auto controllablePhysicsComponent = target->GetComponent<ControllablePhysicsComponent>(); auto* controllablePhysicsComponent = target->GetComponent<ControllablePhysicsComponent>();
if (!controllablePhysicsComponent) return; if (!controllablePhysicsComponent) return;
controllablePhysicsComponent->RemovePickupRadiusScale(m_Scale); controllablePhysicsComponent->RemovePickupRadiusScale(m_Scale);

View File

@@ -16,7 +16,7 @@ void RepairBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bit_str
return; return;
} }
auto* destroyable = static_cast<DestroyableComponent*>(entity->GetComponent(eReplicaComponentType::DESTROYABLE)); auto* destroyable = entity->GetComponent<DestroyableComponent>();
if (destroyable == nullptr) { if (destroyable == nullptr) {
Game::logger->Log("RepairBehavior", "Failed to find destroyable component for %(llu)!", branch.target); Game::logger->Log("RepairBehavior", "Failed to find destroyable component for %(llu)!", branch.target);

View File

@@ -9,9 +9,7 @@ void SkillEventBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bit
auto* caster = EntityManager::Instance()->GetEntity(context->originator); auto* caster = EntityManager::Instance()->GetEntity(context->originator);
if (caster != nullptr && target != nullptr && this->m_effectHandle != nullptr && !this->m_effectHandle->empty()) { if (caster != nullptr && target != nullptr && this->m_effectHandle != nullptr && !this->m_effectHandle->empty()) {
for (CppScripts::Script* script : CppScripts::GetEntityScripts(target)) { target->GetScript()->OnSkillEventFired(target, caster, *this->m_effectHandle);
script->OnSkillEventFired(target, caster, *this->m_effectHandle);
}
} }
} }
@@ -21,8 +19,6 @@ SkillEventBehavior::Calculate(BehaviorContext* context, RakNet::BitStream* bitSt
auto* caster = EntityManager::Instance()->GetEntity(context->originator); auto* caster = EntityManager::Instance()->GetEntity(context->originator);
if (caster != nullptr && target != nullptr && this->m_effectHandle != nullptr && !this->m_effectHandle->empty()) { if (caster != nullptr && target != nullptr && this->m_effectHandle != nullptr && !this->m_effectHandle->empty()) {
for (CppScripts::Script* script : CppScripts::GetEntityScripts(target)) { target->GetScript()->OnSkillEventFired(target, caster, *this->m_effectHandle);
script->OnSkillEventFired(target, caster, *this->m_effectHandle);
}
} }
} }

View File

@@ -6,7 +6,7 @@
#include "Game.h" #include "Game.h"
#include "dLogger.h" #include "dLogger.h"
#include "DestroyableComponent.h" #include "DestroyableComponent.h"
#include "RebuildComponent.h" #include "QuickBuildComponent.h"
#include "Entity.h" #include "Entity.h"
#include "EntityInfo.h" #include "EntityInfo.h"
#include "eReplicaComponentType.h" #include "eReplicaComponentType.h"
@@ -53,10 +53,10 @@ void SpawnBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bitStrea
entity->SetOwnerOverride(context->originator); entity->SetOwnerOverride(context->originator);
// Unset the flag to reposition the player, this makes it harder to glitch out of the map // 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) { if (quickBuildComponent != nullptr) {
rebuildComponent->SetRepositionPlayer(false); quickBuildComponent->SetRepositionPlayer(false);
} }
EntityManager::Instance()->ConstructEntity(entity); EntityManager::Instance()->ConstructEntity(entity);
@@ -87,9 +87,9 @@ void SpawnBehavior::Timer(BehaviorContext* context, const BehaviorBranchContext
return; return;
} }
auto* destroyable = static_cast<DestroyableComponent*>(entity->GetComponent(eReplicaComponentType::DESTROYABLE)); auto* destroyable = entity->GetComponent<DestroyableComponent>();
if (destroyable == nullptr) { if (!destroyable) {
entity->Smash(context->originator); entity->Smash(context->originator);
return; return;

View File

@@ -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. * 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) { if (combatAiComponent == nullptr) {
return; return;
@@ -56,7 +56,7 @@ void StunBehavior::Calculate(BehaviorContext* context, RakNet::BitStream* bitStr
* See if we can stun ourselves * 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) { if (combatAiComponent == nullptr) {
return; return;
@@ -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. * 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) { if (combatAiComponent == nullptr) {
return; return;

View File

@@ -6,7 +6,7 @@
#include "BehaviorContext.h" #include "BehaviorContext.h"
#include "BaseCombatAIComponent.h" #include "BaseCombatAIComponent.h"
#include "EntityManager.h" #include "EntityManager.h"
#include "RebuildComponent.h" #include "QuickBuildComponent.h"
#include "DestroyableComponent.h" #include "DestroyableComponent.h"
#include <vector> #include <vector>
@@ -148,7 +148,7 @@ void TacArcBehavior::Calculate(BehaviorContext* context, RakNet::BitStream* bitS
continue; continue;
} }
if (entity->GetIsDead()) continue; if (entity->IsDead()) continue;
const auto otherPosition = entity->GetPosition(); const auto otherPosition = entity->GetPosition();

View File

@@ -8,7 +8,7 @@ void VentureVisionBehavior::Handle(BehaviorContext* context, RakNet::BitStream*
const auto targetEntity = EntityManager::Instance()->GetEntity(branch.target); const auto targetEntity = EntityManager::Instance()->GetEntity(branch.target);
if (targetEntity) { if (targetEntity) {
auto characterComponent = targetEntity->GetComponent<CharacterComponent>(); auto* characterComponent = targetEntity->GetComponent<CharacterComponent>();
if (characterComponent) { if (characterComponent) {
if (m_show_collectibles) characterComponent->AddVentureVisionEffect(m_ShowCollectibles); if (m_show_collectibles) characterComponent->AddVentureVisionEffect(m_ShowCollectibles);
@@ -24,7 +24,7 @@ void VentureVisionBehavior::UnCast(BehaviorContext* context, BehaviorBranchConte
const auto targetEntity = EntityManager::Instance()->GetEntity(branch.target); const auto targetEntity = EntityManager::Instance()->GetEntity(branch.target);
if (targetEntity) { if (targetEntity) {
auto characterComponent = targetEntity->GetComponent<CharacterComponent>(); auto* characterComponent = targetEntity->GetComponent<CharacterComponent>();
if (characterComponent) { if (characterComponent) {
if (m_show_collectibles) characterComponent->RemoveVentureVisionEffect(m_ShowCollectibles); if (m_show_collectibles) characterComponent->RemoveVentureVisionEffect(m_ShowCollectibles);

View File

@@ -1,8 +1,9 @@
#include "eMissionTaskType.h"
#ifndef __ACHIEVEMENTCACHEKEY__H__ #ifndef __ACHIEVEMENTCACHEKEY__H__
#define __ACHIEVEMENTCACHEKEY__H__ #define __ACHIEVEMENTCACHEKEY__H__
#include "eMissionTaskType.h"
#include "GeneralUtils.h"
class AchievementCacheKey { class AchievementCacheKey {
public: public:
AchievementCacheKey() { 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 "BaseCombatAIComponent.h"
#include <BitStream.h>
#include <algorithm>
#include <sstream>
#include <vector>
#include "BitStream.h"
#include "Entity.h" #include "Entity.h"
#include "EntityManager.h" #include "EntityManager.h"
#include "ControllablePhysicsComponent.h" #include "ControllablePhysicsComponent.h"
@@ -15,32 +19,32 @@
#include "CDClientManager.h" #include "CDClientManager.h"
#include "DestroyableComponent.h" #include "DestroyableComponent.h"
#include <algorithm>
#include <sstream>
#include <vector>
#include "SkillComponent.h" #include "SkillComponent.h"
#include "RebuildComponent.h" #include "QuickBuildComponent.h"
#include "DestroyableComponent.h" #include "DestroyableComponent.h"
#include "Metrics.hpp" #include "Metrics.hpp"
#include "CDComponentsRegistryTable.h" #include "CDComponentsRegistryTable.h"
#include "CDPhysicsComponentTable.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; m_Target = LWOOBJID_EMPTY;
SetAiState(AiState::spawn); m_ComponentId = componentId;
SetAiState(AiState::Spawn);
m_Timer = 1.0f; m_Timer = 1.0f;
m_StartPosition = parent->GetPosition(); m_StartPosition = parent->GetPosition();
m_MovementAI = nullptr;
m_Disabled = false; m_Disabled = false;
m_SkillEntries = {}; m_SkillEntries = {};
m_MovementAI = nullptr; m_MovementAI = nullptr;
m_SoftTimer = 5.0f; m_SoftTimer = 5.0f;
m_dpEntity = nullptr;
m_dpEntityEnemy = nullptr;
}
void BaseCombatAIComponent::LoadTemplateData() {
//Grab the aggro information from BaseCombatAI: //Grab the aggro information from BaseCombatAI:
auto componentQuery = CDClientDatabase::CreatePreppedStmt( auto componentQuery = CDClientDatabase::CreatePreppedStmt(
"SELECT aggroRadius, tetherSpeed, pursuitSpeed, softTetherRadius, hardTetherRadius FROM BaseCombatAIComponent WHERE id = ?;"); "SELECT aggroRadius, tetherSpeed, pursuitSpeed, softTetherRadius, hardTetherRadius FROM BaseCombatAIComponent WHERE id = ?;");
componentQuery.bind(1, (int)id); componentQuery.bind(1, m_ComponentId);
auto componentResult = componentQuery.execQuery(); auto componentResult = componentQuery.execQuery();
@@ -63,21 +67,12 @@ BaseCombatAIComponent::BaseCombatAIComponent(Entity* parent, const uint32_t id):
componentResult.finalize(); 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 * Find skills
*/ */
auto skillQuery = CDClientDatabase::CreatePreppedStmt( auto skillQuery = CDClientDatabase::CreatePreppedStmt(
"SELECT skillID, cooldown, behaviorID FROM SkillBehavior WHERE skillID IN (SELECT skillID FROM ObjectSkills WHERE objectTemplate = ?);"); "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(); auto result = skillQuery.execQuery();
@@ -90,44 +85,51 @@ BaseCombatAIComponent::BaseCombatAIComponent(Entity* parent, const uint32_t id):
auto* behavior = Behavior::CreateBehavior(behaviorId); auto* behavior = Behavior::CreateBehavior(behaviorId);
std::stringstream behaviorQuery; m_SkillEntries.push_back(AiSkillEntry(skillId, 0, abilityCooldown, behavior));
AiSkillEntry entry = { skillId, 0, abilityCooldown, behavior };
m_SkillEntries.push_back(entry);
result.nextRow(); 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); Stun(1.0f);
/* // Add physics
* Add physics
*/
int32_t collisionGroup = (COLLISION_GROUP_DYNAMIC | COLLISION_GROUP_ENEMY); int32_t collisionGroup = (COLLISION_GROUP_DYNAMIC | COLLISION_GROUP_ENEMY);
CDComponentsRegistryTable* componentRegistryTable = CDClientManager::Instance().GetTable<CDComponentsRegistryTable>(); auto* componentRegistryTable = CDClientManager::Instance().GetTable<CDComponentsRegistryTable>();
auto componentID = componentRegistryTable->GetByIDAndType(parent->GetLOT(), eReplicaComponentType::CONTROLLABLE_PHYSICS); if (!componentRegistryTable) return;
CDPhysicsComponentTable* physicsComponentTable = CDClientManager::Instance().GetTable<CDPhysicsComponentTable>(); auto componentID = componentRegistryTable->GetByIDAndType(m_ParentEntity->GetLOT(), eReplicaComponentType::CONTROLLABLE_PHYSICS);
if (physicsComponentTable != nullptr) { auto* physicsComponentTable = CDClientManager::Instance().GetTable<CDPhysicsComponentTable>();
auto* info = physicsComponentTable->GetByID(componentID);
if (info != nullptr) {
collisionGroup = info->bStatic ? COLLISION_GROUP_NEUTRAL : info->collisionGroup;
}
}
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. //Create a phantom physics volume so we can detect when we're aggro'd.
m_dpEntity = new dpEntity(m_Parent->GetObjectID(), m_AggroRadius); m_dpEntity = new dpEntity(m_ParentEntity->GetObjectID(), m_AggroRadius);
m_dpEntityEnemy = new dpEntity(m_Parent->GetObjectID(), m_AggroRadius, false); m_dpEntityEnemy = new dpEntity(m_ParentEntity->GetObjectID(), m_AggroRadius, false);
m_dpEntity->SetCollisionGroup(collisionGroup); m_dpEntity->SetCollisionGroup(collisionGroup);
m_dpEntityEnemy->SetCollisionGroup(collisionGroup); m_dpEntityEnemy->SetCollisionGroup(collisionGroup);
m_dpEntity->SetPosition(m_Parent->GetPosition()); m_dpEntity->SetPosition(m_ParentEntity->GetPosition());
m_dpEntityEnemy->SetPosition(m_Parent->GetPosition()); m_dpEntityEnemy->SetPosition(m_ParentEntity->GetPosition());
dpWorld::Instance().AddEntity(m_dpEntity); dpWorld::Instance().AddEntity(m_dpEntity);
dpWorld::Instance().AddEntity(m_dpEntityEnemy); dpWorld::Instance().AddEntity(m_dpEntityEnemy);
@@ -135,28 +137,29 @@ BaseCombatAIComponent::BaseCombatAIComponent(Entity* parent, const uint32_t id):
} }
BaseCombatAIComponent::~BaseCombatAIComponent() { BaseCombatAIComponent::~BaseCombatAIComponent() {
if (m_dpEntity) if (m_dpEntity) dpWorld::Instance().RemoveEntity(m_dpEntity);
dpWorld::Instance().RemoveEntity(m_dpEntity);
if (m_dpEntityEnemy) if (m_dpEntityEnemy) dpWorld::Instance().RemoveEntity(m_dpEntityEnemy);
dpWorld::Instance().RemoveEntity(m_dpEntityEnemy); m_MovementAI = nullptr;
m_dpEntity = nullptr;
m_dpEntityEnemy = nullptr;
} }
void BaseCombatAIComponent::Update(const float deltaTime) { void BaseCombatAIComponent::Update(const float deltaTime) {
//First, we need to process physics: //First, we need to process physics:
if (!m_dpEntity) return; if (!m_dpEntity) return;
m_dpEntity->SetPosition(m_Parent->GetPosition()); //make sure our position is synced with our dpEntity m_dpEntity->SetPosition(m_ParentEntity->GetPosition()); //make sure our position is synced with our dpEntity
m_dpEntityEnemy->SetPosition(m_Parent->GetPosition()); m_dpEntityEnemy->SetPosition(m_ParentEntity->GetPosition());
//Process enter events //Process enter events
for (auto en : m_dpEntity->GetNewObjects()) { for (auto en : m_dpEntity->GetNewObjects()) {
m_Parent->OnCollisionPhantom(en->GetObjectID()); m_ParentEntity->OnCollisionPhantom(en->GetObjectID());
} }
//Process exit events //Process exit events
for (auto en : m_dpEntity->GetRemovedObjects()) { for (auto en : m_dpEntity->GetRemovedObjects()) {
m_Parent->OnCollisionLeavePhantom(en->GetObjectID()); m_ParentEntity->OnCollisionLeavePhantom(en->GetObjectID());
} }
// Check if we should stop the tether effect // Check if we should stop the tether effect
@@ -165,39 +168,35 @@ void BaseCombatAIComponent::Update(const float deltaTime) {
const auto& info = m_MovementAI->GetInfo(); const auto& info = m_MovementAI->GetInfo();
if (m_Target != LWOOBJID_EMPTY || (NiPoint3::DistanceSquared( if (m_Target != LWOOBJID_EMPTY || (NiPoint3::DistanceSquared(
m_StartPosition, 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; m_TetherEffectActive = false;
} }
} }
if (m_SoftTimer <= 0.0f) { if (m_SoftTimer <= 0.0f) {
EntityManager::Instance()->SerializeEntity(m_Parent); EntityManager::Instance()->SerializeEntity(m_ParentEntity);
m_SoftTimer = 5.0f; m_SoftTimer = 5.0f;
} else { } else {
m_SoftTimer -= deltaTime; m_SoftTimer -= deltaTime;
} }
if (m_Disabled || m_Parent->GetIsDead()) if (m_Disabled || m_ParentEntity->IsDead()) return;
return;
bool stunnedThisFrame = m_Stunned; bool stunnedThisFrame = m_Stunned;
CalculateCombat(deltaTime); // Putting this here for now CalculateCombat(deltaTime); // Putting this here for now
if (m_StartPosition == NiPoint3::ZERO) { if (m_StartPosition == NiPoint3::ZERO) {
m_StartPosition = m_Parent->GetPosition(); m_StartPosition = m_ParentEntity->GetPosition();
} }
m_MovementAI = m_Parent->GetComponent<MovementAIComponent>(); if (!m_MovementAI) {
m_MovementAI = m_ParentEntity->GetComponent<MovementAIComponent>();
if (m_MovementAI == nullptr) { if (!m_MovementAI) return;
return;
} }
if (stunnedThisFrame) { if (stunnedThisFrame) {
m_MovementAI->Stop(); m_MovementAI->Stop();
return; return;
} }
@@ -207,24 +206,25 @@ void BaseCombatAIComponent::Update(const float deltaTime) {
} }
switch (m_State) { switch (m_State) {
case AiState::spawn: case AiState::Spawn:
Stun(2.0f); Stun(2.0f);
SetAiState(AiState::idle); SetAiState(AiState::Idle);
break; break;
case AiState::idle: case AiState::Idle:
Wander(); Wander();
break; break;
case AiState::aggro: case AiState::Aggro:
OnAggro(); OnAggro();
break; break;
case AiState::tether: case AiState::Tether:
OnTether(); OnTether();
break; break;
default: default:
Game::logger->Log("BaseCombatAIComponent", "Entity %i is in an invalid state %i", m_ParentEntity->GetLOT(), m_State);
break; break;
} }
} }
@@ -243,7 +243,7 @@ void BaseCombatAIComponent::CalculateCombat(const float deltaTime) {
bool hadRemainingDowntime = m_SkillTime > 0.0f; bool hadRemainingDowntime = m_SkillTime > 0.0f;
if (m_SkillTime > 0.0f) m_SkillTime -= deltaTime; if (m_SkillTime > 0.0f) m_SkillTime -= deltaTime;
auto* rebuild = m_Parent->GetComponent<RebuildComponent>(); auto* rebuild = m_ParentEntity->GetComponent<QuickBuildComponent>();
if (rebuild != nullptr) { if (rebuild != nullptr) {
const auto state = rebuild->GetState(); 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) { if (!skillComponent) return;
return;
}
skillComponent->CalculateUpdate(deltaTime); skillComponent->CalculateUpdate(deltaTime);
@@ -287,7 +285,7 @@ void BaseCombatAIComponent::CalculateCombat(const float deltaTime) {
} }
if (!m_TetherEffectActive && m_OutOfCombat && (m_OutOfCombatTime -= deltaTime) <= 0) { 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)) { if (destroyableComponent != nullptr && destroyableComponent->HasFaction(4)) {
auto serilizationRequired = false; auto serilizationRequired = false;
@@ -305,10 +303,10 @@ void BaseCombatAIComponent::CalculateCombat(const float deltaTime) {
} }
if (serilizationRequired) { if (serilizationRequired) {
EntityManager::Instance()->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; m_TetherEffectActive = true;
@@ -329,19 +327,19 @@ void BaseCombatAIComponent::CalculateCombat(const float deltaTime) {
SetTarget(newTarget); SetTarget(newTarget);
if (m_Target != LWOOBJID_EMPTY) { if (m_Target != LWOOBJID_EMPTY) {
if (m_State == AiState::idle) { if (m_State == AiState::Idle) {
m_Timer = 0; m_Timer = 0;
} }
SetAiState(AiState::aggro); SetAiState(AiState::Aggro);
} else { } else {
SetAiState(AiState::idle); SetAiState(AiState::Idle);
} }
if (!hasSkillToCast) return; if (!hasSkillToCast) return;
if (m_Target == LWOOBJID_EMPTY) { if (m_Target == LWOOBJID_EMPTY) {
SetAiState(AiState::idle); SetAiState(AiState::Idle);
return; return;
} }
@@ -366,7 +364,7 @@ void BaseCombatAIComponent::CalculateCombat(const float deltaTime) {
m_MovementAI->Stop(); m_MovementAI->Stop();
} }
SetAiState(AiState::aggro); SetAiState(AiState::Aggro);
m_Timer = 0; m_Timer = 0;
@@ -382,8 +380,6 @@ void BaseCombatAIComponent::CalculateCombat(const float deltaTime) {
} }
LWOOBJID BaseCombatAIComponent::FindTarget() { LWOOBJID BaseCombatAIComponent::FindTarget() {
//const auto reference = m_MovementAI == nullptr ? m_StartPosition : m_MovementAI->ApproximateLocation();
NiPoint3 reference = m_StartPosition; NiPoint3 reference = m_StartPosition;
if (m_MovementAI) reference = m_MovementAI->ApproximateLocation(); if (m_MovementAI) reference = m_MovementAI->ApproximateLocation();
@@ -496,10 +492,10 @@ LWOOBJID BaseCombatAIComponent::FindTarget() {
std::vector<LWOOBJID> BaseCombatAIComponent::GetTargetWithinAggroRange() const { std::vector<LWOOBJID> BaseCombatAIComponent::GetTargetWithinAggroRange() const {
std::vector<LWOOBJID> targets; std::vector<LWOOBJID> targets;
for (auto id : m_Parent->GetTargetsInPhantom()) { for (auto id : m_ParentEntity->GetTargetsInPhantom()) {
auto* other = EntityManager::Instance()->GetEntity(id); 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; if (distance > m_AggroRadius * m_AggroRadius) continue;
@@ -509,25 +505,12 @@ std::vector<LWOOBJID> BaseCombatAIComponent::GetTargetWithinAggroRange() const {
return targets; 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) { void BaseCombatAIComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags) {
outBitStream->Write(m_DirtyStateOrTarget || bIsInitialUpdate); outBitStream->Write(m_DirtyStateOrTarget);
if (m_DirtyStateOrTarget || bIsInitialUpdate) { if (bIsInitialUpdate || m_DirtyStateOrTarget) {
outBitStream->Write(uint32_t(m_State)); outBitStream->Write(m_State);
outBitStream->Write(m_Target); outBitStream->Write(m_Target);
m_DirtyStateOrTarget = false; if (!bIsInitialUpdate) m_DirtyStateOrTarget = false;
} }
} }
@@ -535,13 +518,13 @@ void BaseCombatAIComponent::SetAiState(AiState newState) {
if (newState == this->m_State) return; if (newState == this->m_State) return;
this->m_State = newState; this->m_State = newState;
m_DirtyStateOrTarget = true; m_DirtyStateOrTarget = true;
EntityManager::Instance()->SerializeEntity(m_Parent); EntityManager::Instance()->SerializeEntity(m_ParentEntity);
} }
bool BaseCombatAIComponent::IsEnemy(LWOOBJID target) const { bool BaseCombatAIComponent::IsEnemy(LWOOBJID target) const {
auto* entity = EntityManager::Instance()->GetEntity(target); auto* entity = EntityManager::Instance()->GetEntity(target);
if (entity == nullptr) { if (!entity) {
Game::logger->Log("BaseCombatAIComponent", "Invalid entity for checking validity (%llu)!", target); Game::logger->Log("BaseCombatAIComponent", "Invalid entity for checking validity (%llu)!", target);
return false; return false;
@@ -549,21 +532,19 @@ bool BaseCombatAIComponent::IsEnemy(LWOOBJID target) const {
auto* destroyable = entity->GetComponent<DestroyableComponent>(); auto* destroyable = entity->GetComponent<DestroyableComponent>();
if (destroyable == nullptr) { if (!destroyable) return false;
return false;
}
auto* referenceDestroyable = m_Parent->GetComponent<DestroyableComponent>(); auto* referenceDestroyable = m_ParentEntity->GetComponent<DestroyableComponent>();
if (referenceDestroyable == nullptr) { if (!referenceDestroyable) {
Game::logger->Log("BaseCombatAIComponent", "Invalid reference destroyable component on (%llu)!", m_Parent->GetObjectID()); Game::logger->Log("BaseCombatAIComponent", "Invalid reference destroyable component on (%llu)!", m_ParentEntity->GetObjectID());
return false; return false;
} }
auto* quickbuild = entity->GetComponent<RebuildComponent>(); auto* quickbuild = entity->GetComponent<QuickBuildComponent>();
if (quickbuild != nullptr) { if (quickbuild) {
const auto state = quickbuild->GetState(); const auto state = quickbuild->GetState();
if (state != eRebuildState::COMPLETED) { if (state != eRebuildState::COMPLETED) {
@@ -575,7 +556,7 @@ bool BaseCombatAIComponent::IsEnemy(LWOOBJID target) const {
auto candidateList = destroyable->GetFactionIDs(); auto candidateList = destroyable->GetFactionIDs();
for (auto value : candidateList) { for (const auto value : candidateList) {
if (std::find(enemyList.begin(), enemyList.end(), value) != enemyList.end()) { if (std::find(enemyList.begin(), enemyList.end(), value) != enemyList.end()) {
return true; return true;
} }
@@ -588,7 +569,7 @@ void BaseCombatAIComponent::SetTarget(const LWOOBJID target) {
if (this->m_Target == target) return; if (this->m_Target == target) return;
m_Target = target; m_Target = target;
m_DirtyStateOrTarget = true; m_DirtyStateOrTarget = true;
EntityManager::Instance()->SerializeEntity(m_Parent); EntityManager::Instance()->SerializeEntity(m_ParentEntity);
} }
Entity* BaseCombatAIComponent::GetTargetEntity() const { Entity* BaseCombatAIComponent::GetTargetEntity() const {
@@ -597,8 +578,7 @@ Entity* BaseCombatAIComponent::GetTargetEntity() const {
void BaseCombatAIComponent::Taunt(LWOOBJID offender, float threat) { void BaseCombatAIComponent::Taunt(LWOOBJID offender, float threat) {
// Can't taunt self // Can't taunt self
if (offender == m_Parent->GetObjectID()) if (offender == m_ParentEntity->GetObjectID()) return;
return;
m_ThreatEntries[offender] += threat; m_ThreatEntries[offender] += threat;
m_DirtyThreat = true; m_DirtyThreat = true;
@@ -633,9 +613,7 @@ void BaseCombatAIComponent::ClearThreat() {
} }
void BaseCombatAIComponent::Wander() { void BaseCombatAIComponent::Wander() {
if (!m_MovementAI->AtFinalWaypoint()) { if (!m_MovementAI->AtFinalWaypoint()) return;
return;
}
m_MovementAI->SetHaltDistance(0); m_MovementAI->SetHaltDistance(0);
@@ -678,9 +656,7 @@ void BaseCombatAIComponent::OnAggro() {
auto* target = GetTargetEntity(); auto* target = GetTargetEntity();
if (target == nullptr) { if (!target) return;
return;
}
m_MovementAI->SetHaltDistance(m_AttackRadius); m_MovementAI->SetHaltDistance(m_AttackRadius);
@@ -703,7 +679,7 @@ void BaseCombatAIComponent::OnAggro() {
m_MovementAI->SetDestination(targetPos); m_MovementAI->SetDestination(targetPos);
SetAiState(AiState::tether); SetAiState(AiState::Tether);
} }
m_Timer += 0.5f; m_Timer += 0.5f;
@@ -729,7 +705,7 @@ void BaseCombatAIComponent::OnTether() {
m_MovementAI->SetDestination(m_StartPosition); m_MovementAI->SetDestination(m_StartPosition);
SetAiState(AiState::aggro); SetAiState(AiState::Aggro);
} else { } else {
if (IsMech() && Vector3::DistanceSquared(targetPos, currentPos) > m_AttackRadius * m_AttackRadius * 3 * 3) return; if (IsMech() && Vector3::DistanceSquared(targetPos, currentPos) > m_AttackRadius * m_AttackRadius * 3 * 3) return;
@@ -741,62 +717,18 @@ void BaseCombatAIComponent::OnTether() {
m_Timer += 0.5f; 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) { void BaseCombatAIComponent::Stun(const float time) {
if (m_StunImmune || m_StunTime > time) { if (m_StunImmune || m_StunTime > time) return;
return;
}
m_StunTime = time; m_StunTime = time;
m_Stunned = true; 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) { void BaseCombatAIComponent::LookAt(const NiPoint3& point) {
if (m_Stunned) { if (m_Stunned) return;
return;
}
m_Parent->SetRotation(NiQuaternion::LookAt(m_Parent->GetPosition(), point)); m_ParentEntity->SetRotation(NiQuaternion::LookAt(m_ParentEntity->GetPosition(), point));
}
void BaseCombatAIComponent::SetDisabled(bool value) {
m_Disabled = value;
}
bool BaseCombatAIComponent::GetDistabled() const {
return m_Disabled;
} }
void BaseCombatAIComponent::Sleep() { void BaseCombatAIComponent::Sleep() {

View File

@@ -10,6 +10,7 @@
#include "Component.h" #include "Component.h"
#include "eReplicaComponentType.h" #include "eReplicaComponentType.h"
#include <memory>
#include <vector> #include <vector>
#include <map> #include <map>
@@ -19,20 +20,25 @@ class Entity;
/** /**
* The current state of the AI * The current state of the AI
*/ */
enum class AiState : int { enum class AiState : int32_t {
idle = 0, // Doing nothing Idle = 0, // Doing nothing
aggro, // Waiting for an enemy to cross / running back to spawn Aggro, // Waiting for an enemy to cross / running back to spawn
tether, // Chasing an enemy Tether, // Chasing an enemy
spawn, // Spawning into the world Spawn, // Spawning into the world
dead // Killed Dead // Killed
}; };
/** /**
* Represents a skill that can be cast by this enemy, including its cooldowns, which determines how often the skill * Represents a skill that can be cast by this enemy, including its cooldowns, which determines how often the skill
* may be cast. * 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; uint32_t skillId;
float cooldown; float cooldown;
@@ -45,13 +51,16 @@ struct AiSkillEntry
/** /**
* Handles the AI of entities, making them wander, tether and attack their enemies * Handles the AI of entities, making them wander, tether and attack their enemies
*/ */
class BaseCombatAIComponent : public Component { class BaseCombatAIComponent final : public Component {
public: 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(Entity* parentEntity, uint32_t id);
~BaseCombatAIComponent() override; ~BaseCombatAIComponent() override;
void LoadTemplateData() override;
void LoadConfigData() override;
void Startup() override;
void Update(float deltaTime) override; void Update(float deltaTime) override;
void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags); void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags);
@@ -146,37 +155,37 @@ public:
* Gets whether or not the entity is currently stunned * Gets whether or not the entity is currently stunned
* @return whether 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 * (un)stuns the entity, determining whether it'll be able to attack other entities
* @param value whether the enemy is stunned * @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 * Gets if this entity may be stunned
* @return 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 * Set the stun immune value, determining if the entity may be stunned
* @param value * @param value
*/ */
void SetStunImmune(bool value); void SetStunImmune(bool value) { m_StunImmune = value; }
/** /**
* Gets the current speed at which an entity runs when tethering * Gets the current speed at which an entity runs when tethering
* @return 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 * Sets the speed at which an entity will tether
* @param value the new tether speed * @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 * 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 * Gets the radius that will cause this entity to get aggro'd, causing a target chase
* @return the aggro radius of the entity * @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 * Sets the aggro radius, causing the entity to start chasing enemies in this range
* @param value the aggro radius to set * @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 * 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 * (dis)ables the AI, causing it to stop/start attacking enemies
* @param value * @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 * Gets the current state of the AI, whether or not it's looking for enemies to attack
* @return * @return
*/ */
bool GetDistabled() const; bool GetDistabled() const { return m_Disabled; }
/** /**
* Turns the entity asleep, stopping updates to its physics volumes * 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 * Whether the current entity is a mech enemy, needed as mechs tether radius works differently
* @return whether this entity is a mech * @return whether this entity is a mech
*/ */
bool IsMech(); bool IsMech() const { return m_ParentEntity->GetLOT() == 6253; };
int32_t m_ComponentId;
}; };
#endif // BASECOMBATAICOMPONENT_H #endif // BASECOMBATAICOMPONENT_H

View File

@@ -6,66 +6,50 @@
#include "Game.h" #include "Game.h"
#include "dLogger.h" #include "dLogger.h"
#include "GameMessages.h" #include "GameMessages.h"
#include <BitStream.h> #include "BitStream.h"
#include "eTriggerEventType.h" #include "eTriggerEventType.h"
BouncerComponent::BouncerComponent(Entity* parent) : Component(parent) { BouncerComponent::BouncerComponent(Entity* parent) : Component(parent) {
m_PetEnabled = false; m_BounceOnCollision = false;
m_PetBouncerEnabled = false; m_DirtyBounceInfo = false;
m_PetSwitchLoaded = false;
if (parent->GetLOT() == 7625) {
LookupPetSwitch();
}
} }
BouncerComponent::~BouncerComponent() { void BouncerComponent::Startup() {
if (m_ParentEntity->GetLOT() == 7625) LookupPetSwitch();
} }
void BouncerComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags) { void BouncerComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags) {
outBitStream->Write(m_PetEnabled); outBitStream->Write(bIsInitialUpdate || m_DirtyBounceInfo);
if (m_PetEnabled) { if (bIsInitialUpdate || m_DirtyBounceInfo) {
outBitStream->Write(m_PetBouncerEnabled); outBitStream->Write(m_BounceOnCollision);
if (!bIsInitialUpdate) m_DirtyBounceInfo = false;
} }
} }
Entity* BouncerComponent::GetParentEntity() const { void BouncerComponent::SetBounceOnCollision(bool value) {
return m_Parent; if (m_BounceOnCollision == value) return;
m_BounceOnCollision = value;
m_DirtyBounceInfo = true;
} }
void BouncerComponent::SetPetEnabled(bool value) { void BouncerComponent::SetBouncerEnabled(bool value) {
m_PetEnabled = value; m_BounceOnCollision = value;
EntityManager::Instance()->SerializeEntity(m_Parent); GameMessages::SendBouncerActiveStatus(m_ParentEntity->GetObjectID(), value, UNASSIGNED_SYSTEM_ADDRESS);
}
void BouncerComponent::SetPetBouncerEnabled(bool value) { EntityManager::Instance()->SerializeEntity(m_ParentEntity);
m_PetBouncerEnabled = value;
GameMessages::SendBouncerActiveStatus(m_Parent->GetObjectID(), value, UNASSIGNED_SYSTEM_ADDRESS);
EntityManager::Instance()->SerializeEntity(m_Parent);
if (value) { if (value) {
m_Parent->TriggerEvent(eTriggerEventType::PET_ON_SWITCH, m_Parent); m_ParentEntity->TriggerEvent(eTriggerEventType::PET_ON_SWITCH, m_ParentEntity);
GameMessages::SendPlayFXEffect(m_Parent->GetObjectID(), 1513, u"create", "PetOnSwitch", LWOOBJID_EMPTY, 1, 1, true); GameMessages::SendPlayFXEffect(m_ParentEntity->GetObjectID(), 1513, u"create", "PetOnSwitch");
} else { } else {
m_Parent->TriggerEvent(eTriggerEventType::PET_OFF_SWITCH, m_Parent); m_ParentEntity->TriggerEvent(eTriggerEventType::PET_OFF_SWITCH, m_ParentEntity);
GameMessages::SendStopFXEffect(m_Parent, true, "PetOnSwitch"); GameMessages::SendStopFXEffect(m_ParentEntity, true, "PetOnSwitch");
} }
}
bool BouncerComponent::GetPetEnabled() const {
return m_PetEnabled;
}
bool BouncerComponent::GetPetBouncerEnabled() const {
return m_PetBouncerEnabled;
} }
void BouncerComponent::LookupPetSwitch() { void BouncerComponent::LookupPetSwitch() {
const auto& groups = m_Parent->GetGroups(); const auto& groups = m_ParentEntity->GetGroups();
for (const auto& group : groups) { for (const auto& group : groups) {
const auto& entities = EntityManager::Instance()->GetEntitiesInGroup(group); const auto& entities = EntityManager::Instance()->GetEntitiesInGroup(group);
@@ -73,24 +57,22 @@ void BouncerComponent::LookupPetSwitch() {
for (auto* entity : entities) { for (auto* entity : entities) {
auto* switchComponent = entity->GetComponent<SwitchComponent>(); auto* switchComponent = entity->GetComponent<SwitchComponent>();
if (switchComponent != nullptr) { if (!switchComponent) continue;
switchComponent->SetPetBouncer(this); switchComponent->SetPetBouncer(this);
m_PetSwitchLoaded = true; m_DirtyBounceInfo = true;
m_PetEnabled = true;
EntityManager::Instance()->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) { float retryTime = 0.5f;
Game::logger->Log("BouncerComponent", "Failed to load pet bouncer"); 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]() { m_ParentEntity->AddCallbackTimer(retryTime, [this]() {
LookupPetSwitch(); LookupPetSwitch();
}); });
}
} }

View File

@@ -10,41 +10,27 @@
/** /**
* Attached to bouncer entities, allowing other entities to bounce off of it * Attached to bouncer entities, allowing other entities to bounce off of it
*/ */
class BouncerComponent : public Component { class BouncerComponent final : public Component {
public: public:
static const eReplicaComponentType ComponentType = eReplicaComponentType::BOUNCER; inline static const eReplicaComponentType ComponentType = eReplicaComponentType::BOUNCER;
BouncerComponent(Entity* parentEntity); BouncerComponent(Entity* parentEntity);
~BouncerComponent() override;
void Startup() override;
void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags); 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 * 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 * @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, * Sets whether or not this bouncer is currently being activated by a pet, allowing entities to bounce off of it,
* also displays FX accordingly. * also displays FX accordingly.
* @param value whether or not this bouncer is activated by a pet * @param value whether or not this bouncer is activated by a pet
*/ */
void SetPetBouncerEnabled(bool value); void SetBouncerEnabled(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;
/** /**
* Finds the switch used to activate this bouncer if its pet-enabled and stores this components' state there * 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(); void LookupPetSwitch();
private: private:
/**
* Whether this bouncer needs to be activated by a pet
*/
bool m_PetEnabled;
/** /**
* Whether this bouncer is currently being activated by a pet * Whether this bouncer is currently being activated by a pet
*/ */
bool m_PetBouncerEnabled; bool m_BounceOnCollision;
/** bool m_DirtyBounceInfo;
* Whether the pet switch for this bouncer has been located
*/
bool m_PetSwitchLoaded;
}; };
#endif // BOUNCERCOMPONENT_H #endif // BOUNCERCOMPONENT_H

View File

@@ -14,20 +14,12 @@
std::unordered_map<int32_t, std::vector<BuffParameter>> BuffComponent::m_Cache{}; 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) { void BuffComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags) {
if (!bIsInitialUpdate) return; 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) { for (const auto& buff : m_Buffs) {
outBitStream->Write<uint32_t>(buff.first); outBitStream->Write<uint32_t>(buff.first);
outBitStream->Write0(); outBitStream->Write0();
@@ -47,7 +39,7 @@ void BuffComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUp
} }
} }
outBitStream->Write0(); outBitStream->Write0(); // immunities
} }
void BuffComponent::Update(float deltaTime) { void BuffComponent::Update(float deltaTime) {
@@ -55,30 +47,28 @@ void BuffComponent::Update(float deltaTime) {
* Loop through all buffs and apply deltaTime to ther time. * Loop through all buffs and apply deltaTime to ther time.
* If they have expired, remove the buff and break. * If they have expired, remove the buff and break.
*/ */
for (auto& buff : m_Buffs) { for (auto& [buffId, buffInfo] : m_Buffs) {
// For damage buffs // For damage buffs
if (buff.second.tick != 0.0f && buff.second.stacks > 0) { if (buffInfo.tick != 0.0f && buffInfo.stacks > 0) {
buff.second.tickTime -= deltaTime; buffInfo.tickTime -= deltaTime;
if (buff.second.tickTime <= 0.0f) { if (buffInfo.tickTime <= 0.0f) {
buff.second.tickTime = buff.second.tick; buffInfo.tickTime = buffInfo.tick;
buff.second.stacks--; 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. // These are indefinate buffs, don't update them.
if (buff.second.time == 0.0f) { if (buffInfo.time == 0.0f) continue;
continue;
}
buff.second.time -= deltaTime; buffInfo.time -= deltaTime;
if (buff.second.time <= 0.0f) { if (buffInfo.time <= 0.0f) {
RemoveBuff(buff.first); 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 cancelOnDamaged, bool cancelOnDeath, bool cancelOnLogout, bool cancelOnRemoveBuff,
bool cancelOnUi, bool cancelOnUnequip, bool cancelOnZone) { bool cancelOnUi, bool cancelOnUnequip, bool cancelOnZone) {
// Prevent buffs from stacking. // Prevent buffs from stacking.
if (HasBuff(id)) { if (HasBuff(id)) return;
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, (uint32_t)duration * 1000, addImmunity, cancelOnDamaged, cancelOnDeath,
cancelOnLogout, cancelOnRemoveBuff, cancelOnUi, cancelOnUnequip, cancelOnZone); 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; int32_t behaviorID = 0;
const auto& parameters = GetBuffParameters(id); const auto& parameters = GetBuffParameters(id);
auto* skillBehaviorTable = CDClientManager::Instance().GetTable<CDSkillBehaviorTable>();
for (const auto& parameter : parameters) { for (const auto& parameter : parameters) {
if (parameter.name == "overtime") { if (parameter.name == "overtime") {
auto* behaviorTemplateTable = CDClientManager::Instance().GetTable<CDSkillBehaviorTable>();
behaviorID = behaviorTemplateTable->GetSkillByID(parameter.values[0]).behaviorID; behaviorID = skillBehaviorTable->GetSkillByID(parameter.values.skillId).behaviorID;
stacks = static_cast<int32_t>(parameter.values[1]); stacks = static_cast<int32_t>(parameter.values.stacks);
tick = parameter.values[2]; tick = parameter.values.tick;
const auto unknown2 = parameter.values[3]; // Always 0 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.source = source;
buff.behaviorID = behaviorID; 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) { void BuffComponent::RemoveBuff(int32_t id, bool fromUnEquip, bool removeImmunity) {
const auto& iter = m_Buffs.find(id); const auto& iter = m_Buffs.find(id);
if (iter == m_Buffs.end()) { if (iter == m_Buffs.end()) return;
return;
}
GameMessages::SendRemoveBuff(m_Parent, fromUnEquip, removeImmunity, id); GameMessages::SendRemoveBuff(m_ParentEntity, fromUnEquip, removeImmunity, id);
m_Buffs.erase(iter); m_Buffs.erase(iter);
RemoveBuffEffect(id); RemoveBuffEffect(id);
} }
bool BuffComponent::HasBuff(int32_t id) {
return m_Buffs.find(id) != m_Buffs.end();
}
void BuffComponent::ApplyBuffEffect(int32_t id) { void BuffComponent::ApplyBuffEffect(int32_t id) {
const auto& parameters = GetBuffParameters(id); const auto& parameters = GetBuffParameters(id);
for (const auto& parameter : parameters) { for (const auto& parameter : parameters) {
if (parameter.name == "max_health") { if (parameter.name == "max_health") {
const auto maxHealth = parameter.value; const auto maxHealth = parameter.value;
auto* destroyable = this->GetParent()->GetComponent<DestroyableComponent>(); auto* destroyable = this->GetParentEntity()->GetComponent<DestroyableComponent>();
if (destroyable == nullptr) return; if (destroyable == nullptr) return;
@@ -157,7 +139,7 @@ void BuffComponent::ApplyBuffEffect(int32_t id) {
} else if (parameter.name == "max_armor") { } else if (parameter.name == "max_armor") {
const auto maxArmor = parameter.value; const auto maxArmor = parameter.value;
auto* destroyable = this->GetParent()->GetComponent<DestroyableComponent>(); auto* destroyable = this->GetParentEntity()->GetComponent<DestroyableComponent>();
if (destroyable == nullptr) return; if (destroyable == nullptr) return;
@@ -165,13 +147,13 @@ void BuffComponent::ApplyBuffEffect(int32_t id) {
} else if (parameter.name == "max_imagination") { } else if (parameter.name == "max_imagination") {
const auto maxImagination = parameter.value; const auto maxImagination = parameter.value;
auto* destroyable = this->GetParent()->GetComponent<DestroyableComponent>(); auto* destroyable = this->GetParentEntity()->GetComponent<DestroyableComponent>();
if (destroyable == nullptr) return; if (destroyable == nullptr) return;
destroyable->SetMaxImagination(destroyable->GetMaxImagination() + maxImagination); destroyable->SetMaxImagination(destroyable->GetMaxImagination() + maxImagination);
} else if (parameter.name == "speed") { } else if (parameter.name == "speed") {
auto* controllablePhysicsComponent = this->GetParent()->GetComponent<ControllablePhysicsComponent>(); auto* controllablePhysicsComponent = this->GetParentEntity()->GetComponent<ControllablePhysicsComponent>();
if (!controllablePhysicsComponent) return; if (!controllablePhysicsComponent) return;
const auto speed = parameter.value; const auto speed = parameter.value;
controllablePhysicsComponent->AddSpeedboost(speed); controllablePhysicsComponent->AddSpeedboost(speed);
@@ -185,29 +167,29 @@ void BuffComponent::RemoveBuffEffect(int32_t id) {
if (parameter.name == "max_health") { if (parameter.name == "max_health") {
const auto maxHealth = parameter.value; 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); destroyable->SetMaxHealth(destroyable->GetMaxHealth() - maxHealth);
} else if (parameter.name == "max_armor") { } else if (parameter.name == "max_armor") {
const auto maxArmor = parameter.value; 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); destroyable->SetMaxArmor(destroyable->GetMaxArmor() - maxArmor);
} else if (parameter.name == "max_imagination") { } else if (parameter.name == "max_imagination") {
const auto maxImagination = parameter.value; 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); destroyable->SetMaxImagination(destroyable->GetMaxImagination() - maxImagination);
} else if (parameter.name == "speed") { } else if (parameter.name == "speed") {
auto* controllablePhysicsComponent = this->GetParent()->GetComponent<ControllablePhysicsComponent>(); auto* controllablePhysicsComponent = this->GetParentEntity()->GetComponent<ControllablePhysicsComponent>();
if (!controllablePhysicsComponent) return; if (!controllablePhysicsComponent) return;
const auto speed = parameter.value; const auto speed = parameter.value;
controllablePhysicsComponent->RemoveSpeedboost(speed); controllablePhysicsComponent->RemoveSpeedboost(speed);
@@ -216,27 +198,19 @@ void BuffComponent::RemoveBuffEffect(int32_t id) {
} }
void BuffComponent::RemoveAllBuffs() { void BuffComponent::RemoveAllBuffs() {
for (const auto& buff : m_Buffs) { for (const auto& [buffId, buffInfo] : m_Buffs) {
RemoveBuffEffect(buff.first); RemoveBuffEffect(buffId);
} }
m_Buffs.clear(); m_Buffs.clear();
} }
void BuffComponent::Reset() {
RemoveAllBuffs();
}
void BuffComponent::ReApplyBuffs() { void BuffComponent::ReApplyBuffs() {
for (const auto& buff : m_Buffs) { for (const auto& [buffId, buffInfo] : m_Buffs) {
ApplyBuffEffect(buff.first); ApplyBuffEffect(buffId);
} }
} }
Entity* BuffComponent::GetParent() const {
return m_Parent;
}
void BuffComponent::LoadFromXml(tinyxml2::XMLDocument* doc) { void BuffComponent::LoadFromXml(tinyxml2::XMLDocument* doc) {
// Load buffs // Load buffs
auto* dest = doc->FirstChildElement("obj")->FirstChildElement("dest"); auto* dest = doc->FirstChildElement("obj")->FirstChildElement("dest");
@@ -245,9 +219,7 @@ void BuffComponent::LoadFromXml(tinyxml2::XMLDocument* doc) {
auto* buffElement = dest->FirstChildElement("buff"); auto* buffElement = dest->FirstChildElement("buff");
// Old character, no buffs to load // Old character, no buffs to load
if (buffElement == nullptr) { if (buffElement) return;
return;
}
auto* buffEntry = buffElement->FirstChildElement("b"); 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 std::vector<BuffParameter>& BuffComponent::GetBuffParameters(int32_t buffId) {
const auto& pair = m_Cache.find(buffId); const auto& pair = m_Cache.find(buffId);
if (pair != m_Cache.end()) { if (pair != m_Cache.end()) return pair->second;
return pair->second;
}
auto query = CDClientDatabase::CreatePreppedStmt( auto query = CDClientDatabase::CreatePreppedStmt("SELECT * FROM BuffParameters WHERE BuffID = ?;");
"SELECT * FROM BuffParameters WHERE BuffID = ?;");
query.bind(1, (int)buffId); query.bind(1, (int)buffId);
auto result = query.execQuery(); auto result = query.execQuery();
@@ -325,17 +294,15 @@ const std::vector<BuffParameter>& BuffComponent::GetBuffParameters(int32_t buffI
param.value = result.getFloatField(2); param.value = result.getFloatField(2);
if (!result.fieldIsNull(3)) { if (!result.fieldIsNull(3)) {
std::istringstream stream(result.getStringField(3)); const auto parameterInfo = result.getStringField(3);
std::string token; const auto values = GeneralUtils::SplitString(parameterInfo, ',');
if (values.size() >= 4) {
while (std::getline(stream, token, ',')) { GeneralUtils::TryParse(values.at(0), param.values.skillId);
try { GeneralUtils::TryParse(values.at(1), param.values.stacks);
const auto value = std::stof(token); GeneralUtils::TryParse(values.at(2), param.values.tick);
GeneralUtils::TryParse(values.at(3), param.values.unknown2);
param.values.push_back(value); } else {
} catch (std::invalid_argument& exception) { Game::logger->Log("BuffComponent", "Failed to parse %s into parameter struct. Too few parameters to split on.", parameterInfo);
Game::logger->Log("BuffComponent", "Failed to parse value (%s): (%s)!", token.c_str(), exception.what());
}
} }
} }

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 * 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 struct BuffParameter {
{ struct ParameterValues {
int32_t buffId; int32_t skillId = 0;
int32_t stacks = 0;
float tick = 0.0f;
int32_t unknown2 = 0;
};
int32_t buffId = 0;
std::string name; std::string name;
float value; float value = 0.0f;
std::vector<float> values; ParameterValues values;
int32_t effectId; int32_t effectId = 0;
}; };
/** /**
* Meta information about a buff that can be applied, e.g. how long it's applied, who applied it, etc. * 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; int32_t id = 0;
float time = 0; float time = 0;
float tick = 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. * 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: public:
static const eReplicaComponentType ComponentType = eReplicaComponentType::BUFF; inline static const eReplicaComponentType ComponentType = eReplicaComponentType::BUFF;
explicit BuffComponent(Entity* parent); explicit BuffComponent(Entity* parent) : Component(parent) {};
~BuffComponent();
Entity* GetParent() const;
void LoadFromXml(tinyxml2::XMLDocument* doc) override; void LoadFromXml(tinyxml2::XMLDocument* doc) override;
@@ -88,7 +88,7 @@ public:
* @param id the id of the buff to find * @param id the id of the buff to find
* @return whether or not the entity has a buff with the specified id active * @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. * 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 * 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 * Applies all effects for all buffs, active or not, again

View File

@@ -9,62 +9,40 @@
#include "Item.h" #include "Item.h"
#include "PropertyManagementComponent.h" #include "PropertyManagementComponent.h"
BuildBorderComponent::BuildBorderComponent(Entity* parent) : Component(parent) {
}
BuildBorderComponent::~BuildBorderComponent() {
}
void BuildBorderComponent::OnUse(Entity* originator) { void BuildBorderComponent::OnUse(Entity* originator) {
if (originator->GetCharacter()) { if (!originator->GetCharacter()) return;
const auto& entities = EntityManager::Instance()->GetEntitiesInGroup("PropertyPlaque");
auto buildArea = m_Parent->GetObjectID(); const auto& entities = EntityManager::Instance()->GetEntitiesInGroup("PropertyPlaque");
if (!entities.empty()) { auto buildArea = entities.empty() ? m_ParentEntity->GetObjectID() : entities.front()->GetObjectID();
buildArea = entities[0]->GetObjectID();
Game::logger->Log("BuildBorderComponent", "Using PropertyPlaque"); auto* inventoryComponent = originator->GetComponent<InventoryComponent>();
}
auto* inventoryComponent = originator->GetComponent<InventoryComponent>(); if (!inventoryComponent) return;
if (inventoryComponent == nullptr) { auto* thinkingHat = inventoryComponent->FindItemByLot(LOT_THINKING_CAP);
return;
}
auto* thinkingHat = inventoryComponent->FindItemByLot(6086); if (!thinkingHat) return;
if (thinkingHat == nullptr) { Game::logger->Log("BuildBorderComponent", "Using BuildArea %llu for player %llu", buildArea, originator->GetObjectID());
return;
}
inventoryComponent->PushEquippedItems(); inventoryComponent->PushEquippedItems();
Game::logger->Log("BuildBorderComponent", "Starting with %llu", buildArea); if (PropertyManagementComponent::Instance()) {
GameMessages::SendStartArrangingWithItem(
if (PropertyManagementComponent::Instance() != nullptr) { originator,
GameMessages::SendStartArrangingWithItem( originator->GetSystemAddress(),
originator, true,
originator->GetSystemAddress(), buildArea,
true, originator->GetPosition(),
buildArea, 0,
originator->GetPosition(), thinkingHat->GetId(),
0, thinkingHat->GetLot(),
thinkingHat->GetId(), 4,
thinkingHat->GetLot(), 0,
4, -1
0, );
-1, } else {
NiPoint3::ZERO, GameMessages::SendStartArrangingWithItem(originator, originator->GetSystemAddress(), true, buildArea, originator->GetPosition());
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
} }
} }

View File

@@ -6,27 +6,23 @@
#ifndef BUILDBORDERCOMPONENT_H #ifndef BUILDBORDERCOMPONENT_H
#define BUILDBORDERCOMPONENT_H #define BUILDBORDERCOMPONENT_H
#include "BitStream.h"
#include "Entity.h"
#include "Component.h" #include "Component.h"
#include "eReplicaComponentType.h" #include "eReplicaComponentType.h"
/** /**
* Component for the build border, allowing the user to start building when interacting with it * 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: public:
static const eReplicaComponentType ComponentType = eReplicaComponentType::BUILD_BORDER; inline static const eReplicaComponentType ComponentType = eReplicaComponentType::BUILD_BORDER;
BuildBorderComponent(Entity* parent); BuildBorderComponent(Entity* parent) : Component(parent) { };
~BuildBorderComponent() override;
/** /**
* Causes the originator to start build with this entity as a reference point * Causes the originator to start build with this entity as a reference point
* @param originator the entity (probably a player) that triggered the event * @param originator the entity (probably a player) that triggered the event
*/ */
void OnUse(Entity* originator) override; void OnUse(Entity* originator) override;
private:
}; };
#endif // BUILDBORDERCOMPONENT_H #endif // BUILDBORDERCOMPONENT_H

View File

@@ -1,37 +1,49 @@
set(DGAME_DCOMPONENTS_SOURCES "BaseCombatAIComponent.cpp" set(DGAME_DCOMPONENTS_SOURCES "AchievementVendorComponent.cpp"
"ActivityComponent.cpp"
"BaseCombatAIComponent.cpp"
"BouncerComponent.cpp" "BouncerComponent.cpp"
"BuffComponent.cpp" "BuffComponent.cpp"
"BuildBorderComponent.cpp" "BuildBorderComponent.cpp"
"CharacterComponent.cpp" "CharacterComponent.cpp"
"CollectibleComponent.cpp"
"Component.cpp" "Component.cpp"
"ControllablePhysicsComponent.cpp" "ControllablePhysicsComponent.cpp"
"DestroyableComponent.cpp" "DestroyableComponent.cpp"
"DonationVendorComponent.cpp"
"GateRushComponent.cpp"
"InventoryComponent.cpp" "InventoryComponent.cpp"
"ItemComponent.cpp"
"LevelProgressionComponent.cpp" "LevelProgressionComponent.cpp"
"LUPExhibitComponent.cpp" "LUPExhibitComponent.cpp"
"MinigameControlComponent.cpp"
"MissionComponent.cpp" "MissionComponent.cpp"
"MissionOfferComponent.cpp" "MissionOfferComponent.cpp"
"ModelComponent.cpp" "ModelBehaviorComponent.cpp"
"ModuleAssemblyComponent.cpp" "ModuleAssemblyComponent.cpp"
"MovementAIComponent.cpp" "MovementAIComponent.cpp"
"MovingPlatformComponent.cpp" "MovingPlatformComponent.cpp"
"MutableModelBehaviorComponent.cpp"
"PetComponent.cpp" "PetComponent.cpp"
"PhantomPhysicsComponent.cpp" "PhantomPhysicsComponent.cpp"
"PlayerForcedMovementComponent.cpp" "PlayerForcedMovementComponent.cpp"
"PossessableComponent.cpp" "PossessableComponent.cpp"
"PossessorComponent.cpp" "PossessionComponent.cpp"
"PropertyComponent.cpp" "PropertyComponent.cpp"
"PropertyEntranceComponent.cpp" "PropertyEntranceComponent.cpp"
"PropertyManagementComponent.cpp" "PropertyManagementComponent.cpp"
"PropertyVendorComponent.cpp" "PropertyVendorComponent.cpp"
"ProximityMonitorComponent.cpp" "ProximityMonitorComponent.cpp"
"RacingComponent.cpp"
"RacingControlComponent.cpp" "RacingControlComponent.cpp"
"RacingSoundTriggerComponent.cpp"
"RacingStatsComponent.cpp"
"RailActivatorComponent.cpp" "RailActivatorComponent.cpp"
"RebuildComponent.cpp" "QuickBuildComponent.cpp"
"RenderComponent.cpp" "RenderComponent.cpp"
"RigidbodyPhantomPhysicsComponent.cpp" "RigidbodyPhantomPhysicsComponent.cpp"
"RocketLaunchLupComponent.cpp" "MultiZoneEntranceComponent.cpp"
"RocketLaunchpadControlComponent.cpp" "RocketLaunchpadControlComponent.cpp"
"ScriptComponent.cpp"
"ScriptedActivityComponent.cpp" "ScriptedActivityComponent.cpp"
"ShootingGalleryComponent.cpp" "ShootingGalleryComponent.cpp"
"SimplePhysicsComponent.cpp" "SimplePhysicsComponent.cpp"
@@ -39,5 +51,5 @@ set(DGAME_DCOMPONENTS_SOURCES "BaseCombatAIComponent.cpp"
"SoundTriggerComponent.cpp" "SoundTriggerComponent.cpp"
"SwitchComponent.cpp" "SwitchComponent.cpp"
"TriggerComponent.cpp" "TriggerComponent.cpp"
"VehiclePhysicsComponent.cpp" "HavokVehiclePhysicsComponent.cpp"
"VendorComponent.cpp" PARENT_SCOPE) "VendorComponent.cpp" PARENT_SCOPE)

View File

@@ -10,7 +10,6 @@
#include "InventoryComponent.h" #include "InventoryComponent.h"
#include "ControllablePhysicsComponent.h" #include "ControllablePhysicsComponent.h"
#include "EntityManager.h" #include "EntityManager.h"
#include "VehiclePhysicsComponent.h"
#include "GameMessages.h" #include "GameMessages.h"
#include "Item.h" #include "Item.h"
#include "Amf3.h" #include "Amf3.h"
@@ -67,16 +66,12 @@ bool CharacterComponent::LandingAnimDisabled(int zoneID) {
return false; return false;
} }
CharacterComponent::~CharacterComponent() {
}
void CharacterComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags) { void CharacterComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags) {
if (bIsInitialUpdate) { if (bIsInitialUpdate) {
outBitStream->Write0(); outBitStream->Write0(); // Claim codes. Dont't belive these have an effect.
outBitStream->Write0(); outBitStream->Write0(); // Claim codes. Dont't belive these have an effect.
outBitStream->Write0(); outBitStream->Write0(); // Claim codes. Dont't belive these have an effect.
outBitStream->Write0(); outBitStream->Write0(); // Claim codes. Dont't belive these have an effect.
outBitStream->Write(m_Character->GetHairColor()); outBitStream->Write(m_Character->GetHairColor());
outBitStream->Write(m_Character->GetHairStyle()); outBitStream->Write(m_Character->GetHairStyle());
@@ -123,6 +118,8 @@ void CharacterComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInit
outBitStream->Write(m_RacesFinished); outBitStream->Write(m_RacesFinished);
outBitStream->Write(m_FirstPlaceRaceFinishes); 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->Write0();
outBitStream->Write(m_IsLanding); outBitStream->Write(m_IsLanding);
if (m_IsLanding) { if (m_IsLanding) {
@@ -133,20 +130,24 @@ void CharacterComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInit
} }
} }
outBitStream->Write(m_DirtyGMInfo); outBitStream->Write(bIsInitialUpdate || m_DirtyGMInfo);
if (m_DirtyGMInfo) { if (bIsInitialUpdate || m_DirtyGMInfo) {
outBitStream->Write(m_PvpEnabled); outBitStream->Write(m_PvpEnabled);
outBitStream->Write(m_IsGM); outBitStream->Write(m_IsGM);
outBitStream->Write(m_GMLevel); outBitStream->Write(m_GMLevel);
outBitStream->Write(m_EditorEnabled); outBitStream->Write(m_EditorEnabled);
outBitStream->Write(m_EditorLevel); outBitStream->Write(m_EditorLevel);
if (!bIsInitialUpdate) m_DirtyGMInfo = false;
} }
outBitStream->Write(m_DirtyCurrentActivity); outBitStream->Write(bIsInitialUpdate || m_DirtyCurrentActivity);
if (m_DirtyCurrentActivity) outBitStream->Write(m_CurrentActivity); if (bIsInitialUpdate || m_DirtyCurrentActivity) {
outBitStream->Write(m_CurrentActivity);
if (!bIsInitialUpdate) m_DirtyCurrentActivity = false;
}
outBitStream->Write(m_DirtySocialInfo); outBitStream->Write(bIsInitialUpdate || m_DirtySocialInfo);
if (m_DirtySocialInfo) { if (bIsInitialUpdate || m_DirtySocialInfo) {
outBitStream->Write(m_GuildID); outBitStream->Write(m_GuildID);
outBitStream->Write<unsigned char>(static_cast<unsigned char>(m_GuildName.size())); outBitStream->Write<unsigned char>(static_cast<unsigned char>(m_GuildName.size()));
if (!m_GuildName.empty()) if (!m_GuildName.empty())
@@ -154,6 +155,7 @@ void CharacterComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInit
outBitStream->Write(m_IsLEGOClubMember); outBitStream->Write(m_IsLEGOClubMember);
outBitStream->Write(m_CountryCode); outBitStream->Write(m_CountryCode);
if (!bIsInitialUpdate) m_DirtySocialInfo = false;
} }
} }
@@ -162,21 +164,21 @@ bool CharacterComponent::GetPvpEnabled() const {
} }
void CharacterComponent::SetPvpEnabled(const bool value) { void CharacterComponent::SetPvpEnabled(const bool value) {
if (m_PvpEnabled == value) return;
m_DirtyGMInfo = true; m_DirtyGMInfo = true;
m_PvpEnabled = value; m_PvpEnabled = value;
} }
void CharacterComponent::SetGMLevel(eGameMasterLevel gmlevel) { void CharacterComponent::SetGMLevel(eGameMasterLevel gmlevel) {
if (m_GMLevel == gmlevel) return;
m_DirtyGMInfo = true; m_DirtyGMInfo = true;
if (gmlevel > eGameMasterLevel::CIVILIAN) m_IsGM = true; m_IsGM = gmlevel > eGameMasterLevel::CIVILIAN;
else m_IsGM = false;
m_GMLevel = gmlevel; m_GMLevel = gmlevel;
} }
void CharacterComponent::LoadFromXml(tinyxml2::XMLDocument* doc) { void CharacterComponent::LoadFromXml(tinyxml2::XMLDocument* doc) {
auto* character = doc->FirstChildElement("obj")->FirstChildElement("char");
tinyxml2::XMLElement* character = doc->FirstChildElement("obj")->FirstChildElement("char");
if (!character) { if (!character) {
Game::logger->Log("CharacterComponent", "Failed to find char tag while loading XML!"); Game::logger->Log("CharacterComponent", "Failed to find char tag while loading XML!");
return; return;
@@ -196,7 +198,7 @@ void CharacterComponent::LoadFromXml(tinyxml2::XMLDocument* doc) {
} }
// Load the zone statistics // Load the zone statistics
m_ZoneStatistics = {}; m_ZoneStatistics.clear();
auto zoneStatistics = character->FirstChildElement("zs"); auto zoneStatistics = character->FirstChildElement("zs");
if (zoneStatistics) { 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 = rocketConfig ? GeneralUtils::ASCIIToUTF16(rocketConfig->Value()) : u"";
m_LastRocketConfig = GeneralUtils::ASCIIToUTF16(rocketConfig->Value());
} else {
m_LastRocketConfig = u"";
}
// //
// Begin custom attributes // Begin custom attributes
// //
// Load the last rocket item ID // Load the last rocket item ID
const tinyxml2::XMLAttribute* lastRocketItemID = character->FindAttribute("lrid"); const auto* lastRocketItemID = character->FindAttribute("lrid");
if (lastRocketItemID) { if (lastRocketItemID) {
m_LastRocketItemID = lastRocketItemID->Int64Value(); m_LastRocketItemID = lastRocketItemID->Int64Value();
} }
@@ -245,11 +243,11 @@ void CharacterComponent::LoadFromXml(tinyxml2::XMLDocument* doc) {
m_IsGM = true; m_IsGM = true;
m_DirtyGMInfo = true; m_DirtyGMInfo = true;
m_EditorLevel = m_GMLevel; 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: //Annoying guild bs:
const tinyxml2::XMLAttribute* guildName = character->FindAttribute("gn"); const auto* guildName = character->FindAttribute("gn");
if (guildName) { if (guildName) {
const char* gn = guildName->Value(); const char* gn = guildName->Value();
int64_t gid = 0; int64_t gid = 0;
@@ -270,10 +268,8 @@ void CharacterComponent::LoadFromXml(tinyxml2::XMLDocument* doc) {
if (!m_Character) return; if (!m_Character) return;
//Check to see if we're landing: // If the loaded zoneID is different from the current zoneID, we are landing
if (m_Character->GetZoneID() != Game::server->GetZoneID()) { m_IsLanding = m_Character->GetZoneID() != Game::server->GetZoneID();
m_IsLanding = true;
}
if (LandingAnimDisabled(m_Character->GetZoneID()) || LandingAnimDisabled(Game::server->GetZoneID()) || m_LastRocketConfig.empty()) { if (LandingAnimDisabled(m_Character->GetZoneID()) || LandingAnimDisabled(Game::server->GetZoneID()) || m_LastRocketConfig.empty()) {
m_IsLanding = false; //Don't make us land on VE/minigames lol 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 // done with minifig
tinyxml2::XMLElement* character = doc->FirstChildElement("obj")->FirstChildElement("char"); auto* character = doc->FirstChildElement("obj")->FirstChildElement("char");
if (!character) { if (!character) {
Game::logger->Log("CharacterComponent", "Failed to find char tag while updating XML!"); Game::logger->Log("CharacterComponent", "Failed to find char tag while updating XML!");
return; return;
@@ -317,15 +313,15 @@ void CharacterComponent::UpdateXml(tinyxml2::XMLDocument* doc) {
if (!zoneStatistics) zoneStatistics = doc->NewElement("zs"); if (!zoneStatistics) zoneStatistics = doc->NewElement("zs");
zoneStatistics->DeleteChildren(); zoneStatistics->DeleteChildren();
for (auto pair : m_ZoneStatistics) { for (const auto&[mapId, zoneStatisticToSave] : m_ZoneStatistics) {
auto zoneStatistic = doc->NewElement("s"); auto zoneStatistic = doc->NewElement("s");
zoneStatistic->SetAttribute("map", pair.first); zoneStatistic->SetAttribute("map", mapId);
zoneStatistic->SetAttribute("ac", pair.second.m_AchievementsCollected); zoneStatistic->SetAttribute("ac", zoneStatisticToSave.m_AchievementsCollected);
zoneStatistic->SetAttribute("bc", pair.second.m_BricksCollected); zoneStatistic->SetAttribute("bc", zoneStatisticToSave.m_BricksCollected);
zoneStatistic->SetAttribute("cc", pair.second.m_CoinsCollected); zoneStatistic->SetAttribute("cc", zoneStatisticToSave.m_CoinsCollected);
zoneStatistic->SetAttribute("es", pair.second.m_EnemiesSmashed); zoneStatistic->SetAttribute("es", zoneStatisticToSave.m_EnemiesSmashed);
zoneStatistic->SetAttribute("qbc", pair.second.m_QuickBuildsCompleted); zoneStatistic->SetAttribute("qbc", zoneStatisticToSave.m_QuickBuildsCompleted);
zoneStatistics->LinkEndChild(zoneStatistic); zoneStatistics->LinkEndChild(zoneStatistic);
} }
@@ -351,7 +347,7 @@ void CharacterComponent::UpdateXml(tinyxml2::XMLDocument* doc) {
// //
auto newUpdateTimestamp = std::time(nullptr); 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; m_TotalTimePlayed += newUpdateTimestamp - m_LastUpdateTimestamp;
character->SetAttribute("time", m_TotalTimePlayed); character->SetAttribute("time", m_TotalTimePlayed);
@@ -392,7 +388,7 @@ Item* CharacterComponent::RocketEquip(Entity* player) {
if (!rocket) return rocket; if (!rocket) return rocket;
// build and define the rocket config // build and define the rocket config
for (LDFBaseData* data : rocket->GetConfig()) { for (auto* data : rocket->GetConfig()) {
if (data->GetKey() == u"assemblyPartLOTs") { if (data->GetKey() == u"assemblyPartLOTs") {
std::string newRocketStr = data->GetValueAsString() + ";"; std::string newRocketStr = data->GetValueAsString() + ";";
GeneralUtils::ReplaceInString(newRocketStr, "+", ";"); GeneralUtils::ReplaceInString(newRocketStr, "+", ";");
@@ -472,9 +468,7 @@ void CharacterComponent::TrackImaginationDelta(int32_t imagination) {
} }
void CharacterComponent::TrackArmorDelta(int32_t armor) { void CharacterComponent::TrackArmorDelta(int32_t armor) {
if (armor > 0) { if (armor > 0) UpdatePlayerStatistic(TotalArmorRepaired, armor);
UpdatePlayerStatistic(TotalArmorRepaired, armor);
}
} }
void CharacterComponent::TrackRebuildComplete() { void CharacterComponent::TrackRebuildComplete() {
@@ -486,17 +480,16 @@ void CharacterComponent::TrackRebuildComplete() {
void CharacterComponent::TrackRaceCompleted(bool won) { void CharacterComponent::TrackRaceCompleted(bool won) {
m_RacesFinished++; m_RacesFinished++;
if (won) if (won) m_FirstPlaceRaceFinishes++;
m_FirstPlaceRaceFinishes++;
} }
void CharacterComponent::TrackPositionUpdate(const NiPoint3& newPosition) { 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) { if (m_IsRacing) {
UpdatePlayerStatistic(DistanceDriven, (uint64_t)distance); UpdatePlayerStatistic(DistanceDriven, static_cast<uint64_t>(distance));
} else { } 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::string CharacterComponent::StatisticsToString() const {
std::stringstream result; std::stringstream result;
result << std::to_string(m_CurrencyCollected) << ';' result
<< std::to_string(m_BricksCollected) << ';' << m_CurrencyCollected << ';'
<< std::to_string(m_SmashablesSmashed) << ';' << m_BricksCollected << ';'
<< std::to_string(m_QuickBuildsCompleted) << ';' << m_SmashablesSmashed << ';'
<< std::to_string(m_EnemiesSmashed) << ';' << m_QuickBuildsCompleted << ';'
<< std::to_string(m_RocketsUsed) << ';' << m_EnemiesSmashed << ';'
<< std::to_string(m_MissionsCompleted) << ';' << m_RocketsUsed << ';'
<< std::to_string(m_PetsTamed) << ';' << m_MissionsCompleted << ';'
<< std::to_string(m_ImaginationPowerUpsCollected) << ';' << m_PetsTamed << ';'
<< std::to_string(m_LifePowerUpsCollected) << ';' << m_ImaginationPowerUpsCollected << ';'
<< std::to_string(m_ArmorPowerUpsCollected) << ';' << m_LifePowerUpsCollected << ';'
<< std::to_string(m_MetersTraveled) << ';' << m_ArmorPowerUpsCollected << ';'
<< std::to_string(m_TimesSmashed) << ';' << m_MetersTraveled << ';'
<< std::to_string(m_TotalDamageTaken) << ';' << m_TimesSmashed << ';'
<< std::to_string(m_TotalDamageHealed) << ';' << m_TotalDamageTaken << ';'
<< std::to_string(m_TotalArmorRepaired) << ';' << m_TotalDamageHealed << ';'
<< std::to_string(m_TotalImaginationRestored) << ';' << m_TotalArmorRepaired << ';'
<< std::to_string(m_TotalImaginationUsed) << ';' << m_TotalImaginationRestored << ';'
<< std::to_string(m_DistanceDriven) << ';' << m_TotalImaginationUsed << ';'
<< std::to_string(m_TimeAirborneInCar) << ';' << m_DistanceDriven << ';'
<< std::to_string(m_RacingImaginationPowerUpsCollected) << ';' << m_TimeAirborneInCar << ';'
<< std::to_string(m_RacingImaginationCratesSmashed) << ';' << m_RacingImaginationPowerUpsCollected << ';'
<< std::to_string(m_RacingCarBoostsActivated) << ';' << m_RacingImaginationCratesSmashed << ';'
<< std::to_string(m_RacingTimesWrecked) << ';' << m_RacingCarBoostsActivated << ';'
<< std::to_string(m_RacingSmashablesSmashed) << ';' << m_RacingTimesWrecked << ';'
<< std::to_string(m_RacesFinished) << ';' << m_RacingSmashablesSmashed << ';'
<< std::to_string(m_FirstPlaceRaceFinishes) << ';'; << m_RacesFinished << ';'
<< m_FirstPlaceRaceFinishes << ';';
return result.str(); return result.str();
} }
uint64_t CharacterComponent::GetStatisticFromSplit(std::vector<std::string> split, uint32_t index) { 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) { ZoneStatistics& CharacterComponent::GetZoneStatisticsForMap(LWOMAPID mapID) {
auto stats = m_ZoneStatistics.find(mapID); auto stats = m_ZoneStatistics.find(mapID);
if (stats == m_ZoneStatistics.end()) if (stats == m_ZoneStatistics.end()) m_ZoneStatistics.insert({ mapID, {0, 0, 0, 0, 0 } });
m_ZoneStatistics.insert({ mapID, {0, 0, 0, 0, 0 } });
return m_ZoneStatistics.at(mapID); return m_ZoneStatistics.at(mapID);
} }
@@ -732,8 +725,8 @@ void CharacterComponent::RemoveVentureVisionEffect(std::string ventureVisionType
} }
void CharacterComponent::UpdateClientMinimap(bool showFaction, std::string ventureVisionType) const { void CharacterComponent::UpdateClientMinimap(bool showFaction, std::string ventureVisionType) const {
if (!m_Parent) return; if (!m_ParentEntity) return;
AMFArrayValue arrayToSend; AMFArrayValue arrayToSend;
arrayToSend.Insert(ventureVisionType, showFaction); 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 * Represents a character, including their rockets and stats
*/ */
class CharacterComponent : public Component { class CharacterComponent final : public Component {
public: public:
static const eReplicaComponentType ComponentType = eReplicaComponentType::CHARACTER; inline static const eReplicaComponentType ComponentType = eReplicaComponentType::CHARACTER;
CharacterComponent(Entity* parent, Character* character); CharacterComponent(Entity* parent, Character* character);
~CharacterComponent() override;
void LoadFromXml(tinyxml2::XMLDocument* doc) override; void LoadFromXml(tinyxml2::XMLDocument* doc) override;
void UpdateXml(tinyxml2::XMLDocument* doc) override; void UpdateXml(tinyxml2::XMLDocument* doc) override;

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 "Component.h"
#include "DluAssert.h"
Component::Component(Entity* owningEntity) {
Component::Component(Entity* parent) { DluAssert(owningEntity != nullptr);
m_Parent = parent; m_ParentEntity = owningEntity;
}
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) {
} }

View File

@@ -1,52 +1,74 @@
#pragma once #pragma once
#include "../thirdparty/tinyxml2/tinyxml2.h" #include "tinyxml2.h"
class Entity; class Entity;
namespace RakNet {
class BitStream;
}
/** /**
* Component base class, provides methods for game loop updates, usage events and loading and saving to XML. * Component base class, provides methods for game loop updates, usage events and loading and saving to XML.
*/ */
class Component class Component {
{
public: public:
Component(Entity* parent); Component(Entity* owningEntity);
virtual ~Component(); virtual ~Component() {};
/** /**
* Gets the owner of this component * Gets the owner of this component
* @return the owner of this component * @return the owner of this component
*/ */
Entity* GetParent() const; Entity* GetParentEntity() const { return m_ParentEntity; };
/**
* Updates the component in the game loop
* @param deltaTime time passed since last update
*/
virtual void Update(float deltaTime);
/** /**
* Event called when this component is being used, e.g. when some entity interacted with it * Event called when this component is being used, e.g. when some entity interacted with it
* @param originator * @param originator
*/ */
virtual void OnUse(Entity* originator); virtual void OnUse(Entity* originator) {};
/** /**
* Save data from this componennt to character XML * Save data from this componennt to character XML
* @param doc the document to write data to * @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 * Load base data for this component from character XML
* @param doc the document to read data from * @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: protected:
/** /**
* The entity that owns this component * 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 "BitStream.h"
#include "dLogger.h" #include "dLogger.h"
#include "Game.h" #include "Game.h"
#include "dServer.h"
#include "dpWorld.h" #include "dpWorld.h"
#include "dpEntity.h" #include "dpEntity.h"
@@ -51,34 +52,30 @@ ControllablePhysicsComponent::ControllablePhysicsComponent(Entity* entity) : Com
m_ImmuneToStunTurnCount = 0; m_ImmuneToStunTurnCount = 0;
m_ImmuneToStunUseItemCount = 0; m_ImmuneToStunUseItemCount = 0;
if (entity->GetLOT() != 1) // Other physics entities we care about will be added by BaseCombatAI // Other physics entities we care about will be added by BaseCombatAI
return; 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; float radius = 1.5f;
m_dpEntity = new dpEntity(m_Parent->GetObjectID(), radius, false); m_dpEntity = new dpEntity(m_ParentEntity->GetObjectID(), radius, false);
m_dpEntity->SetCollisionGroup(COLLISION_GROUP_DYNAMIC | COLLISION_GROUP_FRIENDLY); m_dpEntity->SetCollisionGroup(COLLISION_GROUP_DYNAMIC | COLLISION_GROUP_FRIENDLY);
dpWorld::Instance().AddEntity(m_dpEntity); dpWorld::Instance().AddEntity(m_dpEntity);
}
} }
ControllablePhysicsComponent::~ControllablePhysicsComponent() { ControllablePhysicsComponent::~ControllablePhysicsComponent() {
if (m_dpEntity) { if (!m_dpEntity) return;
dpWorld::Instance().RemoveEntity(m_dpEntity); 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) { 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) { if (bIsInitialUpdate) {
outBitStream->Write(m_InJetpackMode); outBitStream->Write(m_InJetpackMode);
if (m_InJetpackMode) { if (m_InJetpackMode) {
@@ -99,33 +96,32 @@ void ControllablePhysicsComponent::Serialize(RakNet::BitStream* outBitStream, bo
if (m_IgnoreMultipliers) m_DirtyCheats = false; if (m_IgnoreMultipliers) m_DirtyCheats = false;
outBitStream->Write(m_DirtyCheats); outBitStream->Write(bIsInitialUpdate || m_DirtyCheats);
if (m_DirtyCheats) { if (bIsInitialUpdate || m_DirtyCheats) {
outBitStream->Write(m_GravityScale); outBitStream->Write(m_GravityScale);
outBitStream->Write(m_SpeedMultiplier); outBitStream->Write(m_SpeedMultiplier);
if (!bIsInitialUpdate) m_DirtyCheats = false;
m_DirtyCheats = false;
} }
outBitStream->Write(m_DirtyEquippedItemInfo); outBitStream->Write(bIsInitialUpdate || m_DirtyEquippedItemInfo);
if (m_DirtyEquippedItemInfo) { if (bIsInitialUpdate || m_DirtyEquippedItemInfo) {
outBitStream->Write(m_PickupRadius); outBitStream->Write(m_PickupRadius);
outBitStream->Write(m_InJetpackMode); outBitStream->Write(m_InJetpackMode);
m_DirtyEquippedItemInfo = false; if (!bIsInitialUpdate) m_DirtyEquippedItemInfo = false;
} }
outBitStream->Write(m_DirtyBubble); outBitStream->Write(bIsInitialUpdate || m_DirtyBubble);
if (m_DirtyBubble) { if (bIsInitialUpdate || m_DirtyBubble) {
outBitStream->Write(m_IsInBubble); outBitStream->Write(m_IsInBubble);
if (m_IsInBubble) { if (m_IsInBubble) {
outBitStream->Write(m_BubbleType); outBitStream->Write(m_BubbleType);
outBitStream->Write(m_SpecialAnims); outBitStream->Write(m_SpecialAnims);
} }
m_DirtyBubble = false; if (!bIsInitialUpdate) m_DirtyBubble = false;
} }
outBitStream->Write(m_DirtyPosition || bIsInitialUpdate); outBitStream->Write(bIsInitialUpdate || m_DirtyPosition);
if (m_DirtyPosition || bIsInitialUpdate) { if (bIsInitialUpdate || m_DirtyPosition) {
outBitStream->Write(m_Position.x); outBitStream->Write(m_Position.x);
outBitStream->Write(m_Position.y); outBitStream->Write(m_Position.y);
outBitStream->Write(m_Position.z); outBitStream->Write(m_Position.z);
@@ -138,20 +134,22 @@ void ControllablePhysicsComponent::Serialize(RakNet::BitStream* outBitStream, bo
outBitStream->Write(m_IsOnGround); outBitStream->Write(m_IsOnGround);
outBitStream->Write(m_IsOnRail); outBitStream->Write(m_IsOnRail);
outBitStream->Write(m_DirtyVelocity); outBitStream->Write(bIsInitialUpdate || m_DirtyVelocity);
if (m_DirtyVelocity) { if (bIsInitialUpdate || m_DirtyVelocity) {
outBitStream->Write(m_Velocity.x); outBitStream->Write(m_Velocity.x);
outBitStream->Write(m_Velocity.y); outBitStream->Write(m_Velocity.y);
outBitStream->Write(m_Velocity.z); outBitStream->Write(m_Velocity.z);
if (!bIsInitialUpdate) m_DirtyVelocity = false;
} }
outBitStream->Write(m_DirtyAngularVelocity); outBitStream->Write(bIsInitialUpdate || m_DirtyAngularVelocity);
if (m_DirtyAngularVelocity) { if (bIsInitialUpdate || m_DirtyAngularVelocity) {
outBitStream->Write(m_AngularVelocity.x); outBitStream->Write(m_AngularVelocity.x);
outBitStream->Write(m_AngularVelocity.y); outBitStream->Write(m_AngularVelocity.y);
outBitStream->Write(m_AngularVelocity.z); outBitStream->Write(m_AngularVelocity.z);
if (!bIsInitialUpdate) m_DirtyAngularVelocity = false;
} }
if (!bIsInitialUpdate) m_DirtyPosition = false;
outBitStream->Write0(); outBitStream->Write0();
} }
@@ -162,22 +160,45 @@ void ControllablePhysicsComponent::Serialize(RakNet::BitStream* outBitStream, bo
} }
void ControllablePhysicsComponent::LoadFromXml(tinyxml2::XMLDocument* doc) { void ControllablePhysicsComponent::LoadFromXml(tinyxml2::XMLDocument* doc) {
tinyxml2::XMLElement* character = doc->FirstChildElement("obj")->FirstChildElement("char"); tinyxml2::XMLElement* characterElem = doc->FirstChildElement("obj")->FirstChildElement("char");
if (!character) { if (!characterElem) {
Game::logger->Log("ControllablePhysicsComponent", "Failed to find char tag!"); Game::logger->Log("ControllablePhysicsComponent", "Failed to find char tag!");
return; return;
} }
m_Parent->GetCharacter()->LoadXmlRespawnCheckpoints(); m_ParentEntity->GetCharacter()->LoadXmlRespawnCheckpoints();
character->QueryAttribute("lzx", &m_Position.x); characterElem->QueryAttribute("lzx", &m_Position.x);
character->QueryAttribute("lzy", &m_Position.y); characterElem->QueryAttribute("lzy", &m_Position.y);
character->QueryAttribute("lzz", &m_Position.z); characterElem->QueryAttribute("lzz", &m_Position.z);
character->QueryAttribute("lzrx", &m_Rotation.x); characterElem->QueryAttribute("lzrx", &m_Rotation.x);
character->QueryAttribute("lzry", &m_Rotation.y); characterElem->QueryAttribute("lzry", &m_Rotation.y);
character->QueryAttribute("lzrz", &m_Rotation.z); characterElem->QueryAttribute("lzrz", &m_Rotation.z);
character->QueryAttribute("lzrw", &m_Rotation.w); 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; m_DirtyPosition = true;
} }
@@ -208,9 +229,7 @@ void ControllablePhysicsComponent::UpdateXml(tinyxml2::XMLDocument* doc) {
} }
void ControllablePhysicsComponent::SetPosition(const NiPoint3& pos) { void ControllablePhysicsComponent::SetPosition(const NiPoint3& pos) {
if (m_Static) { if (m_Static || pos == m_Position) return;
return;
}
m_Position.x = pos.x; m_Position.x = pos.x;
m_Position.y = pos.y; m_Position.y = pos.y;
@@ -221,9 +240,7 @@ void ControllablePhysicsComponent::SetPosition(const NiPoint3& pos) {
} }
void ControllablePhysicsComponent::SetRotation(const NiQuaternion& rot) { void ControllablePhysicsComponent::SetRotation(const NiQuaternion& rot) {
if (m_Static) { if (m_Static || rot == m_Rotation) return;
return;
}
m_Rotation = rot; m_Rotation = rot;
m_DirtyPosition = true; m_DirtyPosition = true;
@@ -232,9 +249,7 @@ void ControllablePhysicsComponent::SetRotation(const NiQuaternion& rot) {
} }
void ControllablePhysicsComponent::SetVelocity(const NiPoint3& vel) { void ControllablePhysicsComponent::SetVelocity(const NiPoint3& vel) {
if (m_Static) { if (m_Static || m_Velocity == vel) return;
return;
}
m_Velocity = vel; m_Velocity = vel;
m_DirtyPosition = true; m_DirtyPosition = true;
@@ -244,9 +259,7 @@ void ControllablePhysicsComponent::SetVelocity(const NiPoint3& vel) {
} }
void ControllablePhysicsComponent::SetAngularVelocity(const NiPoint3& vel) { void ControllablePhysicsComponent::SetAngularVelocity(const NiPoint3& vel) {
if (m_Static) { if (m_Static || vel == m_AngularVelocity) return;
return;
}
m_AngularVelocity = vel; m_AngularVelocity = vel;
m_DirtyPosition = true; m_DirtyPosition = true;
@@ -254,25 +267,15 @@ void ControllablePhysicsComponent::SetAngularVelocity(const NiPoint3& vel) {
} }
void ControllablePhysicsComponent::SetIsOnGround(bool val) { void ControllablePhysicsComponent::SetIsOnGround(bool val) {
m_DirtyPosition = true; if (m_IsOnGround == val) return;
m_IsOnGround = val; m_IsOnGround = val;
m_DirtyPosition = true;
} }
void ControllablePhysicsComponent::SetIsOnRail(bool val) { void ControllablePhysicsComponent::SetIsOnRail(bool val) {
m_DirtyPosition = true; if (m_IsOnRail == val) return;
m_IsOnRail = val; m_IsOnRail = val;
} m_DirtyPosition = true;
void ControllablePhysicsComponent::SetDirtyPosition(bool val) {
m_DirtyPosition = val;
}
void ControllablePhysicsComponent::SetDirtyVelocity(bool val) {
m_DirtyVelocity = val;
}
void ControllablePhysicsComponent::SetDirtyAngularVelocity(bool val) {
m_DirtyAngularVelocity = val;
} }
void ControllablePhysicsComponent::AddPickupRadiusScale(float value) { void ControllablePhysicsComponent::AddPickupRadiusScale(float value) {
@@ -297,10 +300,10 @@ void ControllablePhysicsComponent::RemovePickupRadiusScale(float value) {
m_PickupRadius = 0.0f; m_PickupRadius = 0.0f;
m_DirtyEquippedItemInfo = true; m_DirtyEquippedItemInfo = true;
for (uint32_t i = 0; i < m_ActivePickupRadiusScales.size(); i++) { 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; if (m_PickupRadius < candidateRadius) m_PickupRadius = candidateRadius;
} }
EntityManager::Instance()->SerializeEntity(m_Parent); EntityManager::Instance()->SerializeEntity(m_ParentEntity);
} }
void ControllablePhysicsComponent::AddSpeedboost(float value) { void ControllablePhysicsComponent::AddSpeedboost(float value) {
@@ -319,33 +322,33 @@ void ControllablePhysicsComponent::RemoveSpeedboost(float value) {
} }
// Recalculate speedboost since we removed one // 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 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(); if (levelProgressionComponent) m_SpeedBoost = levelProgressionComponent->GetSpeedBase();
} else { // Used the last applied speedboost } else { // Used the last applied speedboost
m_SpeedBoost = m_ActiveSpeedBoosts.back(); m_SpeedBoost = m_ActiveSpeedBoosts.back();
} }
SetSpeedMultiplier(m_SpeedBoost / 500.0f); // 500 being the base speed SetSpeedMultiplier(m_SpeedBoost / 500.0f); // 500 being the base speed
EntityManager::Instance()->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) { if (m_IsInBubble) {
Game::logger->Log("ControllablePhysicsComponent", "Already in bubble"); Game::logger->Log("ControllablePhysicsComponent", "%llu is already in bubble", m_ParentEntity->GetObjectID());
return; return;
} }
m_BubbleType = bubbleType; m_BubbleType = bubbleType;
m_IsInBubble = true; m_IsInBubble = true;
m_DirtyBubble = true; m_DirtyBubble = true;
m_SpecialAnims = specialAnims; m_SpecialAnims = specialAnims;
EntityManager::Instance()->SerializeEntity(m_Parent); EntityManager::Instance()->SerializeEntity(m_ParentEntity);
} }
void ControllablePhysicsComponent::DeactivateBubbleBuff(){ void ControllablePhysicsComponent::DeactivateBubbleBuff() {
m_DirtyBubble = true; m_DirtyBubble = true;
m_IsInBubble = false; m_IsInBubble = false;
EntityManager::Instance()->SerializeEntity(m_Parent); EntityManager::Instance()->SerializeEntity(m_ParentEntity);
}; };
void ControllablePhysicsComponent::SetStunImmunity( void ControllablePhysicsComponent::SetStunImmunity(
@@ -357,9 +360,9 @@ void ControllablePhysicsComponent::SetStunImmunity(
const bool bImmuneToStunJump, const bool bImmuneToStunJump,
const bool bImmuneToStunMove, const bool bImmuneToStunMove,
const bool bImmuneToStunTurn, 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 (bImmuneToStunAttack && m_ImmuneToStunAttackCount > 0) m_ImmuneToStunAttackCount -= 1;
if (bImmuneToStunEquip && m_ImmuneToStunEquipCount > 0) m_ImmuneToStunEquipCount -= 1; if (bImmuneToStunEquip && m_ImmuneToStunEquipCount > 0) m_ImmuneToStunEquipCount -= 1;
if (bImmuneToStunInteract && m_ImmuneToStunInteractCount > 0) m_ImmuneToStunInteractCount -= 1; if (bImmuneToStunInteract && m_ImmuneToStunInteractCount > 0) m_ImmuneToStunInteractCount -= 1;
@@ -378,7 +381,7 @@ void ControllablePhysicsComponent::SetStunImmunity(
} }
GameMessages::SendSetStunImmunity( GameMessages::SendSetStunImmunity(
m_Parent->GetObjectID(), state, m_Parent->GetSystemAddress(), originator, m_ParentEntity->GetObjectID(), state, m_ParentEntity->GetSystemAddress(), originator,
bImmuneToStunAttack, bImmuneToStunAttack,
bImmuneToStunEquip, bImmuneToStunEquip,
bImmuneToStunInteract, bImmuneToStunInteract,

View File

@@ -19,18 +19,18 @@ enum class eStateChangeType : uint32_t;
/** /**
* Handles the movement of controllable Entities, e.g. enemies and players * Handles the movement of controllable Entities, e.g. enemies and players
*/ */
class ControllablePhysicsComponent : public Component { class ControllablePhysicsComponent final : public Component {
public: public:
static const eReplicaComponentType ComponentType = eReplicaComponentType::CONTROLLABLE_PHYSICS; inline static const eReplicaComponentType ComponentType = eReplicaComponentType::CONTROLLABLE_PHYSICS;
ControllablePhysicsComponent(Entity* entity); ControllablePhysicsComponent(Entity* entity);
~ControllablePhysicsComponent() override; ~ControllablePhysicsComponent() override;
void Update(float deltaTime) override;
void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags); void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags);
void LoadFromXml(tinyxml2::XMLDocument* doc) override; void LoadFromXml(tinyxml2::XMLDocument* doc) override;
void ResetFlags(); void ResetFlags();
void UpdateXml(tinyxml2::XMLDocument* doc) override; void UpdateXml(tinyxml2::XMLDocument* doc) override;
void Startup() override;
/** /**
* Sets the position of this entity, also ensures this update is serialized next tick. * 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 * Mark the position as dirty, forcing a serialization update next tick
* @param val whether or not the position is dirty * @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 * Mark the velocity as dirty, forcing a serializtion update next tick
* @param val whether or not the velocity is dirty * @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 * Mark the angular velocity as dirty, forcing a serialization update next tick
* @param val whether or not the angular velocity is dirty * @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 * Sets whether or not the entity is currently wearing a jetpack

View File

@@ -11,7 +11,7 @@
#include "CDClientManager.h" #include "CDClientManager.h"
#include "CDDestructibleComponentTable.h" #include "CDDestructibleComponentTable.h"
#include "EntityManager.h" #include "EntityManager.h"
#include "RebuildComponent.h" #include "QuickBuildComponent.h"
#include "CppScripts.h" #include "CppScripts.h"
#include "Loot.h" #include "Loot.h"
#include "Character.h" #include "Character.h"
@@ -28,7 +28,7 @@
#include "MissionComponent.h" #include "MissionComponent.h"
#include "CharacterComponent.h" #include "CharacterComponent.h"
#include "PossessableComponent.h" #include "PossessableComponent.h"
#include "PossessorComponent.h" #include "PossessionComponent.h"
#include "InventoryComponent.h" #include "InventoryComponent.h"
#include "dZoneManager.h" #include "dZoneManager.h"
#include "WorldConfig.h" #include "WorldConfig.h"
@@ -37,8 +37,9 @@
#include "eGameActivity.h" #include "eGameActivity.h"
#include "CDComponentsRegistryTable.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_iArmor = 0;
m_fMaxArmor = 0.0f; m_fMaxArmor = 0.0f;
m_iImagination = 0; m_iImagination = 0;
@@ -51,7 +52,7 @@ DestroyableComponent::DestroyableComponent(Entity* parent) : Component(parent) {
m_IsGMImmune = false; m_IsGMImmune = false;
m_IsShielded = false; m_IsShielded = false;
m_DamageToAbsorb = 0; m_DamageToAbsorb = 0;
m_IsModuleAssembly = m_Parent->HasComponent(eReplicaComponentType::MODULE_ASSEMBLY); m_IsModuleAssembly = m_ParentEntity->HasComponent(eReplicaComponentType::MODULE_ASSEMBLY);
m_DirtyThreatList = false; m_DirtyThreatList = false;
m_HasThreats = false; m_HasThreats = false;
m_ExplodeFactor = 1.0f; m_ExplodeFactor = 1.0f;
@@ -62,6 +63,7 @@ DestroyableComponent::DestroyableComponent(Entity* parent) : Component(parent) {
m_MinCoins = 0; m_MinCoins = 0;
m_MaxCoins = 0; m_MaxCoins = 0;
m_DamageReduction = 0; m_DamageReduction = 0;
m_ComponentId = componentId;
m_ImmuneToBasicAttackCount = 0; m_ImmuneToBasicAttackCount = 0;
m_ImmuneToDamageOverTimeCount = 0; m_ImmuneToDamageOverTimeCount = 0;
@@ -73,49 +75,71 @@ DestroyableComponent::DestroyableComponent(Entity* parent) : Component(parent) {
m_ImmuneToQuickbuildInterruptCount = 0; m_ImmuneToQuickbuildInterruptCount = 0;
m_ImmuneToPullToPointCount = 0; m_ImmuneToPullToPointCount = 0;
} }
void DestroyableComponent::Startup() {
DestroyableComponent::~DestroyableComponent() {
} }
void DestroyableComponent::LoadConfigData() {
void DestroyableComponent::Reinitialize(LOT templateID) { SetIsSmashable(GetIsSmashable() | (m_ParentEntity->GetVarAs<int32_t>(u"is_smashable") != 0));
CDComponentsRegistryTable* compRegistryTable = CDClientManager::Instance().GetTable<CDComponentsRegistryTable>(); }
void DestroyableComponent::LoadTemplateData() {
int32_t buffComponentID = compRegistryTable->GetByIDAndType(templateID, eReplicaComponentType::BUFF); if (m_ParentEntity->IsPlayer()) return;
int32_t collectibleComponentID = compRegistryTable->GetByIDAndType(templateID, eReplicaComponentType::COLLECTIBLE); auto* destroyableComponentTable = CDClientManager::Instance().GetTable<CDDestructibleComponentTable>();
int32_t rebuildComponentID = compRegistryTable->GetByIDAndType(templateID, eReplicaComponentType::QUICK_BUILD); auto destroyableDataLookup = destroyableComponentTable->Query([this](CDDestructibleComponent entry) { return (entry.id == this->m_ComponentId); });
if (m_ComponentId == -1 || destroyableDataLookup.empty()) {
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 {
SetHealth(1); SetHealth(1);
SetImagination(0);
SetArmor(0); SetArmor(0);
SetMaxHealth(1); SetMaxHealth(1);
SetMaxImagination(0);
SetMaxArmor(0); SetMaxArmor(0);
SetIsSmashable(true); 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; return;
} }
auto* buffComponent = m_Parent->GetComponent<BuffComponent>(); auto* buffComponent = m_ParentEntity->GetComponent<BuffComponent>();
if (buffComponent != nullptr) { if (buffComponent != nullptr) {
buffComponent->LoadFromXml(doc); buffComponent->LoadFromXml(doc);
@@ -207,7 +231,7 @@ void DestroyableComponent::UpdateXml(tinyxml2::XMLDocument* doc) {
return; return;
} }
auto* buffComponent = m_Parent->GetComponent<BuffComponent>(); auto* buffComponent = m_ParentEntity->GetComponent<BuffComponent>();
if (buffComponent != nullptr) { if (buffComponent != nullptr) {
buffComponent->UpdateXml(doc); buffComponent->UpdateXml(doc);
@@ -224,7 +248,7 @@ void DestroyableComponent::UpdateXml(tinyxml2::XMLDocument* doc) {
void DestroyableComponent::SetHealth(int32_t value) { void DestroyableComponent::SetHealth(int32_t value) {
m_DirtyHealth = true; m_DirtyHealth = true;
auto* characterComponent = m_Parent->GetComponent<CharacterComponent>(); auto* characterComponent = m_ParentEntity->GetComponent<CharacterComponent>();
if (characterComponent != nullptr) { if (characterComponent != nullptr) {
characterComponent->TrackHealthDelta(value - m_iHealth); characterComponent->TrackHealthDelta(value - m_iHealth);
} }
@@ -244,16 +268,16 @@ void DestroyableComponent::SetMaxHealth(float value, bool playAnim) {
if (playAnim) { if (playAnim) {
// Now update the player bar // Now update the player bar
if (!m_Parent->GetParentUser()) return; if (!m_ParentEntity->GetParentUser()) return;
AMFArrayValue args; AMFArrayValue args;
args.Insert("amount", std::to_string(difference)); args.Insert("amount", std::to_string(difference));
args.Insert("type", "health"); args.Insert("type", "health");
GameMessages::SendUIMessageServerToSingleClient(m_Parent, m_Parent->GetParentUser()->GetSystemAddress(), "MaxPlayerBarUpdate", args); GameMessages::SendUIMessageServerToSingleClient(m_ParentEntity, m_ParentEntity->GetParentUser()->GetSystemAddress(), "MaxPlayerBarUpdate", args);
} }
EntityManager::Instance()->SerializeEntity(m_Parent); EntityManager::Instance()->SerializeEntity(m_ParentEntity);
} }
void DestroyableComponent::SetArmor(int32_t value) { 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. // If Destroyable Component already has zero armor do not trigger the passive ability again.
bool hadArmor = m_iArmor > 0; bool hadArmor = m_iArmor > 0;
auto* characterComponent = m_Parent->GetComponent<CharacterComponent>(); auto* characterComponent = m_ParentEntity->GetComponent<CharacterComponent>();
if (characterComponent != nullptr) { if (characterComponent != nullptr) {
characterComponent->TrackArmorDelta(value - m_iArmor); characterComponent->TrackArmorDelta(value - m_iArmor);
} }
m_iArmor = value; m_iArmor = value;
auto* inventroyComponent = m_Parent->GetComponent<InventoryComponent>(); auto* inventroyComponent = m_ParentEntity->GetComponent<InventoryComponent>();
if (m_iArmor == 0 && inventroyComponent != nullptr && hadArmor) { if (m_iArmor == 0 && inventroyComponent != nullptr && hadArmor) {
inventroyComponent->TriggerPassiveAbility(PassiveAbilityTrigger::SentinelArmor); inventroyComponent->TriggerPassiveAbility(PassiveAbilityTrigger::SentinelArmor);
} }
@@ -285,29 +309,29 @@ void DestroyableComponent::SetMaxArmor(float value, bool playAnim) {
if (playAnim) { if (playAnim) {
// Now update the player bar // Now update the player bar
if (!m_Parent->GetParentUser()) return; if (!m_ParentEntity->GetParentUser()) return;
AMFArrayValue args; AMFArrayValue args;
args.Insert("amount", std::to_string(value)); args.Insert("amount", std::to_string(value));
args.Insert("type", "armor"); args.Insert("type", "armor");
GameMessages::SendUIMessageServerToSingleClient(m_Parent, m_Parent->GetParentUser()->GetSystemAddress(), "MaxPlayerBarUpdate", args); GameMessages::SendUIMessageServerToSingleClient(m_ParentEntity, m_ParentEntity->GetParentUser()->GetSystemAddress(), "MaxPlayerBarUpdate", args);
} }
EntityManager::Instance()->SerializeEntity(m_Parent); EntityManager::Instance()->SerializeEntity(m_ParentEntity);
} }
void DestroyableComponent::SetImagination(int32_t value) { void DestroyableComponent::SetImagination(int32_t value) {
m_DirtyHealth = true; m_DirtyHealth = true;
auto* characterComponent = m_Parent->GetComponent<CharacterComponent>(); auto* characterComponent = m_ParentEntity->GetComponent<CharacterComponent>();
if (characterComponent != nullptr) { if (characterComponent != nullptr) {
characterComponent->TrackImaginationDelta(value - m_iImagination); characterComponent->TrackImaginationDelta(value - m_iImagination);
} }
m_iImagination = value; m_iImagination = value;
auto* inventroyComponent = m_Parent->GetComponent<InventoryComponent>(); auto* inventroyComponent = m_ParentEntity->GetComponent<InventoryComponent>();
if (m_iImagination == 0 && inventroyComponent != nullptr) { if (m_iImagination == 0 && inventroyComponent != nullptr) {
inventroyComponent->TriggerPassiveAbility(PassiveAbilityTrigger::AssemblyImagination); inventroyComponent->TriggerPassiveAbility(PassiveAbilityTrigger::AssemblyImagination);
} }
@@ -325,15 +349,15 @@ void DestroyableComponent::SetMaxImagination(float value, bool playAnim) {
if (playAnim) { if (playAnim) {
// Now update the player bar // Now update the player bar
if (!m_Parent->GetParentUser()) return; if (!m_ParentEntity->GetParentUser()) return;
AMFArrayValue args; AMFArrayValue args;
args.Insert("amount", std::to_string(difference)); args.Insert("amount", std::to_string(difference));
args.Insert("type", "imagination"); args.Insert("type", "imagination");
GameMessages::SendUIMessageServerToSingleClient(m_Parent, m_Parent->GetParentUser()->GetSystemAddress(), "MaxPlayerBarUpdate", args); GameMessages::SendUIMessageServerToSingleClient(m_ParentEntity, m_ParentEntity->GetParentUser()->GetSystemAddress(), "MaxPlayerBarUpdate", args);
} }
EntityManager::Instance()->SerializeEntity(m_Parent); EntityManager::Instance()->SerializeEntity(m_ParentEntity);
} }
void DestroyableComponent::SetDamageToAbsorb(int32_t value) { void DestroyableComponent::SetDamageToAbsorb(int32_t value) {
@@ -455,8 +479,8 @@ bool DestroyableComponent::IsImmune() const {
} }
bool DestroyableComponent::IsKnockbackImmune() const { bool DestroyableComponent::IsKnockbackImmune() const {
auto* characterComponent = m_Parent->GetComponent<CharacterComponent>(); auto* characterComponent = m_ParentEntity->GetComponent<CharacterComponent>();
auto* inventoryComponent = m_Parent->GetComponent<InventoryComponent>(); auto* inventoryComponent = m_ParentEntity->GetComponent<InventoryComponent>();
if (characterComponent != nullptr && inventoryComponent != nullptr && characterComponent->GetCurrentActivity() == eGameActivity::QUICKBUILDING) { if (characterComponent != nullptr && inventoryComponent != nullptr && characterComponent->GetCurrentActivity() == eGameActivity::QUICKBUILDING) {
const auto hasPassive = inventoryComponent->HasAnyPassive({ const auto hasPassive = inventoryComponent->HasAnyPassive({
@@ -499,7 +523,7 @@ bool DestroyableComponent::CheckValidity(const LWOOBJID target, const bool ignor
return false; return false;
} }
auto* targetQuickbuild = targetEntity->GetComponent<RebuildComponent>(); auto* targetQuickbuild = targetEntity->GetComponent<QuickBuildComponent>();
if (targetQuickbuild != nullptr) { if (targetQuickbuild != nullptr) {
const auto state = targetQuickbuild->GetState(); const auto state = targetQuickbuild->GetState();
@@ -532,7 +556,7 @@ void DestroyableComponent::Heal(const uint32_t health) {
SetHealth(current); SetHealth(current);
EntityManager::Instance()->SerializeEntity(m_Parent); EntityManager::Instance()->SerializeEntity(m_ParentEntity);
} }
@@ -550,7 +574,7 @@ void DestroyableComponent::Imagine(const int32_t deltaImagination) {
SetImagination(current); SetImagination(current);
EntityManager::Instance()->SerializeEntity(m_Parent); EntityManager::Instance()->SerializeEntity(m_ParentEntity);
} }
@@ -564,7 +588,7 @@ void DestroyableComponent::Repair(const uint32_t armor) {
SetArmor(current); SetArmor(current);
EntityManager::Instance()->SerializeEntity(m_Parent); EntityManager::Instance()->SerializeEntity(m_ParentEntity);
} }
@@ -616,13 +640,13 @@ void DestroyableComponent::Damage(uint32_t damage, const LWOOBJID source, uint32
SetIsShielded(absorb > 0); SetIsShielded(absorb > 0);
// Dismount on the possessable hit // Dismount on the possessable hit
auto possessable = m_Parent->GetComponent<PossessableComponent>(); auto* possessable = m_ParentEntity->GetComponent<PossessableComponent>();
if (possessable && possessable->GetDepossessOnHit()) { if (possessable && possessable->GetDepossessOnHit()) {
possessable->Dismount(); possessable->Dismount();
} }
// Dismount on the possessor hit // Dismount on the possessor hit
auto possessor = m_Parent->GetComponent<PossessorComponent>(); auto* possessor = m_ParentEntity->GetComponent<PossessionComponent>();
if (possessor) { if (possessor) {
auto possessableId = possessor->GetPossessable(); auto possessableId = possessor->GetPossessable();
if (possessableId != LWOOBJID_EMPTY) { if (possessableId != LWOOBJID_EMPTY) {
@@ -633,17 +657,17 @@ void DestroyableComponent::Damage(uint32_t damage, const LWOOBJID source, uint32
} }
} }
if (m_Parent->GetLOT() != 1) { if (m_ParentEntity->GetLOT() != 1) {
echo = true; echo = true;
} }
if (echo) { if (echo) {
EntityManager::Instance()->SerializeEntity(m_Parent); EntityManager::Instance()->SerializeEntity(m_ParentEntity);
} }
auto* attacker = EntityManager::Instance()->GetEntity(source); auto* attacker = EntityManager::Instance()->GetEntity(source);
m_Parent->OnHit(attacker); m_ParentEntity->OnHit(attacker);
m_Parent->OnHitOrHealResult(attacker, sourceDamage); m_ParentEntity->OnHitOrHealResult(attacker, sourceDamage);
NotifySubscribers(attacker, sourceDamage); NotifySubscribers(attacker, sourceDamage);
for (const auto& cb : m_OnHitCallbacks) { for (const auto& cb : m_OnHitCallbacks) {
@@ -651,7 +675,7 @@ void DestroyableComponent::Damage(uint32_t damage, const LWOOBJID source, uint32
} }
if (health != 0) { if (health != 0) {
auto* combatComponent = m_Parent->GetComponent<BaseCombatAIComponent>(); auto* combatComponent = m_ParentEntity->GetComponent<BaseCombatAIComponent>();
if (combatComponent != nullptr) { if (combatComponent != nullptr) {
combatComponent->Taunt(source, sourceDamage * 10); // * 10 is arbatrary 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 //check if hardcore mode is enabled
if (EntityManager::Instance()->GetHardcoreMode()) { if (EntityManager::Instance()->GetHardcoreMode()) {
DoHardcoreModeDrops(source); DoHardcoreModeDrops(source);
} }
Smash(source, eKillType::VIOLENT, u"", skillID); Smash(source, eKillType::VIOLENT, u"", skillID);
} }
void DestroyableComponent::Subscribe(LWOOBJID scriptObjId, CppScripts::Script* scriptToAdd) { void DestroyableComponent::Subscribe(CppScripts::Script* scriptToAdd) {
m_SubscribedScripts.insert(std::make_pair(scriptObjId, scriptToAdd)); auto foundScript = std::find(m_SubscribedScripts.begin(), m_SubscribedScripts.end(), scriptToAdd);
Game::logger->LogDebug("DestroyableComponent", "Added script %llu to entity %llu", scriptObjId, m_Parent->GetObjectID()); 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()); Game::logger->LogDebug("DestroyableComponent", "Number of subscribed scripts %i", m_SubscribedScripts.size());
} }
void DestroyableComponent::Unsubscribe(LWOOBJID scriptObjId) { void DestroyableComponent::Unsubscribe(CppScripts::Script* scriptToRemove) {
auto foundScript = m_SubscribedScripts.find(scriptObjId); auto foundScript = std::find(m_SubscribedScripts.begin(), m_SubscribedScripts.end(), scriptToRemove);
if (foundScript != m_SubscribedScripts.end()) { if (foundScript != m_SubscribedScripts.end()) {
m_SubscribedScripts.erase(foundScript); m_SubscribedScripts.erase(foundScript);
Game::logger->LogDebug("DestroyableComponent", "Removed script %llu from entity %llu", scriptObjId, m_Parent->GetObjectID()); Game::logger->LogDebug("DestroyableComponent", "Unsubscribed a script from entity %llu", m_ParentEntity->GetObjectID());
} else { Game::logger->LogDebug("DestroyableComponent", "Number of subscribed scripts %i", m_SubscribedScripts.size());
Game::logger->LogDebug("DestroyableComponent", "Tried to remove a script for Entity %llu but script %llu didnt exist", m_Parent->GetObjectID(), scriptObjId); 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) { void DestroyableComponent::NotifySubscribers(Entity* attacker, uint32_t damage) {
for (auto script : m_SubscribedScripts) { for (auto* script : m_SubscribedScripts) {
script.second->NotifyHitOrHealResult(m_Parent, attacker, damage); DluAssert(script != nullptr);
script->NotifyHitOrHealResult(m_ParentEntity, attacker, damage);
} }
} }
@@ -696,7 +726,7 @@ void DestroyableComponent::Smash(const LWOOBJID source, const eKillType killType
SetArmor(0); SetArmor(0);
SetHealth(0); SetHealth(0);
EntityManager::Instance()->SerializeEntity(m_Parent); EntityManager::Instance()->SerializeEntity(m_ParentEntity);
} }
m_KillerID = source; m_KillerID = source;
@@ -708,12 +738,12 @@ void DestroyableComponent::Smash(const LWOOBJID source, const eKillType killType
auto* team = TeamManager::Instance()->GetTeam(owner->GetObjectID()); 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>(); auto* inventoryComponent = owner->GetComponent<InventoryComponent>();
if (inventoryComponent != nullptr && isEnemy) { if (inventoryComponent != nullptr && isEnemy) {
inventoryComponent->TriggerPassiveAbility(PassiveAbilityTrigger::EnemySmashed, m_Parent); inventoryComponent->TriggerPassiveAbility(PassiveAbilityTrigger::EnemySmashed, m_ParentEntity);
} }
auto* missions = owner->GetComponent<MissionComponent>(); auto* missions = owner->GetComponent<MissionComponent>();
@@ -729,28 +759,28 @@ void DestroyableComponent::Smash(const LWOOBJID source, const eKillType killType
if (memberMissions == nullptr) continue; if (memberMissions == nullptr) continue;
memberMissions->Progress(eMissionTaskType::SMASH, m_Parent->GetLOT()); memberMissions->Progress(eMissionTaskType::SMASH, m_ParentEntity->GetLOT());
memberMissions->Progress(eMissionTaskType::USE_SKILL, m_Parent->GetLOT(), skillID); memberMissions->Progress(eMissionTaskType::USE_SKILL, m_ParentEntity->GetLOT(), skillID);
} }
} else { } else {
missions->Progress(eMissionTaskType::SMASH, m_Parent->GetLOT()); missions->Progress(eMissionTaskType::SMASH, m_ParentEntity->GetLOT());
missions->Progress(eMissionTaskType::USE_SKILL, m_Parent->GetLOT(), skillID); 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?! //NANI?!
if (!isPlayer) { if (!isPlayer) {
if (owner != nullptr) { if (owner != nullptr) {
auto* team = TeamManager::Instance()->GetTeam(owner->GetObjectID()); 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; LWOOBJID specificOwner = LWOOBJID_EMPTY;
auto* scriptedActivityComponent = m_Parent->GetComponent<ScriptedActivityComponent>(); auto* scriptedActivityComponent = m_ParentEntity->GetComponent<ScriptedActivityComponent>();
uint32_t teamSize = team->members.size(); uint32_t teamSize = team->members.size();
uint32_t lootMatrixId = GetLootMatrixID(); uint32_t lootMatrixId = GetLootMatrixID();
@@ -763,24 +793,24 @@ void DestroyableComponent::Smash(const LWOOBJID source, const eKillType killType
auto* member = EntityManager::Instance()->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 { } else {
for (const auto memberId : team->members) { // Free for all for (const auto memberId : team->members) { // Free for all
auto* member = EntityManager::Instance()->GetEntity(memberId); auto* member = EntityManager::Instance()->GetEntity(memberId);
if (member == nullptr) continue; 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 } 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 { } else {
//Check if this zone allows coin drops //Check if this zone allows coin drops
if (dZoneManager::Instance()->GetPlayerLoseCoinOnDeath()) { if (dZoneManager::Instance()->GetPlayerLoseCoinOnDeath()) {
auto* character = m_Parent->GetCharacter(); auto* character = m_ParentEntity->GetCharacter();
uint64_t coinsTotal = character->GetCoins(); uint64_t coinsTotal = character->GetCoins();
const uint64_t minCoinsToLose = dZoneManager::Instance()->GetWorldConfig()->coinsLostOnDeathMin; const uint64_t minCoinsToLose = dZoneManager::Instance()->GetWorldConfig()->coinsLostOnDeathMin;
if (coinsTotal >= minCoinsToLose) { if (coinsTotal >= minCoinsToLose) {
@@ -792,27 +822,23 @@ void DestroyableComponent::Smash(const LWOOBJID source, const eKillType killType
coinsTotal -= 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); character->SetCoins(coinsTotal, eLootSourceType::PICKUP);
} }
} }
Entity* zoneControl = EntityManager::Instance()->GetZoneControlEntity(); Entity* zoneControl = EntityManager::Instance()->GetZoneControlEntity();
for (CppScripts::Script* script : CppScripts::GetEntityScripts(zoneControl)) { zoneControl->GetScript()->OnPlayerDied(zoneControl, m_ParentEntity);
script->OnPlayerDied(zoneControl, m_Parent);
}
std::vector<Entity*> scriptedActs = EntityManager::Instance()->GetEntitiesByComponent(eReplicaComponentType::SCRIPTED_ACTIVITY); std::vector<Entity*> scriptedActs = EntityManager::Instance()->GetEntitiesByComponent(eReplicaComponentType::SCRIPTED_ACTIVITY);
for (Entity* scriptEntity : scriptedActs) { for (Entity* scriptEntity : scriptedActs) {
if (scriptEntity->GetObjectID() != zoneControl->GetObjectID()) { // Don't want to trigger twice on instance worlds if (scriptEntity->GetObjectID() != zoneControl->GetObjectID()) { // Don't want to trigger twice on instance worlds
for (CppScripts::Script* script : CppScripts::GetEntityScripts(scriptEntity)) { scriptEntity->GetScript()->OnPlayerDied(scriptEntity, m_ParentEntity);
script->OnPlayerDied(scriptEntity, m_Parent);
}
} }
} }
} }
m_Parent->Kill(owner); m_ParentEntity->Kill(owner);
} }
void DestroyableComponent::SetFaction(int32_t factionID, bool ignoreChecks) { void DestroyableComponent::SetFaction(int32_t factionID, bool ignoreChecks) {
@@ -823,16 +849,16 @@ void DestroyableComponent::SetFaction(int32_t factionID, bool ignoreChecks) {
} }
void DestroyableComponent::SetStatusImmunity( void DestroyableComponent::SetStatusImmunity(
const eStateChangeType state, const eStateChangeType state,
const bool bImmuneToBasicAttack, const bool bImmuneToBasicAttack,
const bool bImmuneToDamageOverTime, const bool bImmuneToDamageOverTime,
const bool bImmuneToKnockback, const bool bImmuneToKnockback,
const bool bImmuneToInterrupt, const bool bImmuneToInterrupt,
const bool bImmuneToSpeed, const bool bImmuneToSpeed,
const bool bImmuneToImaginationGain, const bool bImmuneToImaginationGain,
const bool bImmuneToImaginationLoss, const bool bImmuneToImaginationLoss,
const bool bImmuneToQuickbuildInterrupt, const bool bImmuneToQuickbuildInterrupt,
const bool bImmuneToPullToPoint) { const bool bImmuneToPullToPoint) {
if (state == eStateChangeType::POP) { if (state == eStateChangeType::POP) {
if (bImmuneToBasicAttack && m_ImmuneToBasicAttackCount > 0) m_ImmuneToBasicAttackCount -= 1; if (bImmuneToBasicAttack && m_ImmuneToBasicAttackCount > 0) m_ImmuneToBasicAttackCount -= 1;
@@ -845,7 +871,7 @@ void DestroyableComponent::SetStatusImmunity(
if (bImmuneToQuickbuildInterrupt && m_ImmuneToQuickbuildInterruptCount > 0) m_ImmuneToQuickbuildInterruptCount -= 1; if (bImmuneToQuickbuildInterrupt && m_ImmuneToQuickbuildInterruptCount > 0) m_ImmuneToQuickbuildInterruptCount -= 1;
if (bImmuneToPullToPoint && m_ImmuneToPullToPointCount > 0) m_ImmuneToPullToPointCount -= 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 (bImmuneToBasicAttack) m_ImmuneToBasicAttackCount += 1;
if (bImmuneToDamageOverTime) m_ImmuneToDamageOverTimeCount += 1; if (bImmuneToDamageOverTime) m_ImmuneToDamageOverTimeCount += 1;
if (bImmuneToKnockback) m_ImmuneToKnockbackCount += 1; if (bImmuneToKnockback) m_ImmuneToKnockbackCount += 1;
@@ -858,7 +884,7 @@ void DestroyableComponent::SetStatusImmunity(
} }
GameMessages::SendSetStatusImmunity( GameMessages::SendSetStatusImmunity(
m_Parent->GetObjectID(), state, m_Parent->GetSystemAddress(), m_ParentEntity->GetObjectID(), state, m_ParentEntity->GetSystemAddress(),
bImmuneToBasicAttack, bImmuneToBasicAttack,
bImmuneToDamageOverTime, bImmuneToDamageOverTime,
bImmuneToKnockback, bImmuneToKnockback,
@@ -872,7 +898,7 @@ void DestroyableComponent::SetStatusImmunity(
} }
void DestroyableComponent::FixStats() { void DestroyableComponent::FixStats() {
auto* entity = GetParent(); auto* entity = GetParentEntity();
if (entity == nullptr) return; if (entity == nullptr) return;
@@ -972,43 +998,43 @@ void DestroyableComponent::AddOnHitCallback(const std::function<void(Entity*)>&
m_OnHitCallbacks.push_back(callback); m_OnHitCallbacks.push_back(callback);
} }
void DestroyableComponent::DoHardcoreModeDrops(const LWOOBJID source){ void DestroyableComponent::DoHardcoreModeDrops(const LWOOBJID source) {
//check if this is a player: //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: //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 uscore = character->GetUScore();
auto uscoreToLose = uscore * (EntityManager::Instance()->GetHardcoreLoseUscoreOnDeathPercent() / 100); auto uscoreToLose = uscore * (EntityManager::Instance()->GetHardcoreLoseUscoreOnDeathPercent() / 100);
character->SetUScore(uscore - uscoreToLose); 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 (EntityManager::Instance()->GetHardcoreDropinventoryOnDeath()) { if (EntityManager::Instance()->GetHardcoreDropinventoryOnDeath()) {
//drop all items from inventory: //drop all items from inventory:
auto* inventory = m_Parent->GetComponent<InventoryComponent>(); auto* inventory = m_ParentEntity->GetComponent<InventoryComponent>();
if (inventory) { if (inventory) {
//get the items inventory: //get the items inventory:
auto items = inventory->GetInventory(eInventoryType::ITEMS); auto items = inventory->GetInventory(eInventoryType::ITEMS);
if (items){ if (items) {
auto itemMap = items->GetItems(); auto itemMap = items->GetItems();
if (!itemMap.empty()){ if (!itemMap.empty()) {
for (const auto& item : itemMap) { for (const auto& item : itemMap) {
//drop the item: //drop the item:
if (!item.second) continue; if (!item.second) continue;
// don't drop the thinkng cap // don't drop the thinkng cap
if (item.second->GetLot() == 6086) continue; 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); item.second->SetCount(0, false, false);
} }
EntityManager::Instance()->SerializeEntity(m_Parent); EntityManager::Instance()->SerializeEntity(m_ParentEntity);
} }
} }
} }
} }
//get character: //get character:
auto* chars = m_Parent->GetCharacter(); auto* chars = m_ParentEntity->GetCharacter();
if (chars) { if (chars) {
auto coins = chars->GetCoins(); auto coins = chars->GetCoins();
@@ -1016,13 +1042,13 @@ void DestroyableComponent::DoHardcoreModeDrops(const LWOOBJID source){
chars->SetCoins(0, eLootSourceType::NONE); chars->SetCoins(0, eLootSourceType::NONE);
//drop all coins: //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 // 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 // do this last so we don't get killed.... again
EntityManager::Instance()->DestructEntity(m_Parent); EntityManager::Instance()->DestructEntity(m_ParentEntity);
EntityManager::Instance()->ConstructEntity(m_Parent); EntityManager::Instance()->ConstructEntity(m_ParentEntity);
return; return;
} }
@@ -1039,7 +1065,7 @@ void DestroyableComponent::DoHardcoreModeDrops(const LWOOBJID source){
playerStats->SetUScore(playerStats->GetUScore() + uscore); playerStats->SetUScore(playerStats->GetUScore() + uscore);
GameMessages::SendModifyLEGOScore(player, player->GetSystemAddress(), uscore, eLootSourceType::MISSION); GameMessages::SendModifyLEGOScore(player, player->GetSystemAddress(), uscore, eLootSourceType::MISSION);
EntityManager::Instance()->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 * Represents the stats of an entity, for example its health, imagination and armor. Also handles factions, which
* indicate which enemies this entity has. * indicate which enemies this entity has.
*/ */
class DestroyableComponent : public Component { class DestroyableComponent final : public Component {
public: public:
static const eReplicaComponentType ComponentType = eReplicaComponentType::DESTROYABLE; inline static const eReplicaComponentType ComponentType = eReplicaComponentType::DESTROYABLE;
DestroyableComponent(Entity* parentEntity); DestroyableComponent(Entity* parentEntity, int32_t componentId = -1);
~DestroyableComponent() override;
void Startup() override;
void LoadConfigData() override;
void LoadTemplateData() override;
void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, uint32_t& flags); void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, uint32_t& flags);
void LoadFromXml(tinyxml2::XMLDocument* doc) override; void LoadFromXml(tinyxml2::XMLDocument* doc) override;
void UpdateXml(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 * 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. * stats will also update.
@@ -451,13 +447,15 @@ public:
*/ */
void NotifySubscribers(Entity* attacker, uint32_t damage); void NotifySubscribers(Entity* attacker, uint32_t damage);
void Subscribe(LWOOBJID scriptObjId, CppScripts::Script* scriptToAdd); void Subscribe(CppScripts::Script* scriptToAdd);
void Unsubscribe(LWOOBJID scriptObjId); void Unsubscribe(CppScripts::Script* scriptToRemove);
// handle hardcode mode drops // handle hardcode mode drops
void DoHardcoreModeDrops(const LWOOBJID source); void DoHardcoreModeDrops(const LWOOBJID source);
private: private:
// The ID of this component
int32_t m_ComponentId;
/** /**
* Whether or not the health should be serialized * Whether or not the health should be serialized
*/ */
@@ -589,9 +587,9 @@ private:
std::vector<std::function<void(Entity*)>> m_OnHitCallbacks; 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 * status immunity counters

View File

@@ -0,0 +1,23 @@
#include "DonationVendorComponent.h"
DonationVendorComponent::DonationVendorComponent(Entity* parent) : VendorComponent(parent) {
m_PercentComplete = 0.0;
m_TotalDonated = 0;
m_TotalRemaining = 0;
}
void DonationVendorComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags) {
VendorComponent::Serialize(outBitStream, bIsInitialUpdate, flags);
outBitStream->Write(bIsInitialUpdate || m_DirtyDonationVendor);
if (bIsInitialUpdate || m_DirtyDonationVendor) {
outBitStream->Write(m_PercentComplete);
outBitStream->Write(m_TotalDonated);
outBitStream->Write(m_TotalRemaining);
if (!bIsInitialUpdate) m_DirtyDonationVendor = false;
}
}
void DonationVendorComponent::LoadConfigData() {
m_ActivityId = m_ParentEntity->GetVar<uint32_t>(u"activityID");
VendorComponent::LoadConfigData();
}

View File

@@ -0,0 +1,44 @@
#ifndef __DONATIONVENDORCOMPONENT__H__
#define __DONATIONVENDORCOMPONENT__H__
#include "VendorComponent.h"
#include "eReplicaComponentType.h"
class Entity;
class DonationVendorComponent final : public VendorComponent {
public:
inline static const eReplicaComponentType ComponentType = eReplicaComponentType::DONATION_VENDOR;
DonationVendorComponent(Entity* parent);
void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags);
void LoadConfigData() override;
void SetPercentComplete(float percentComplete){
if (m_PercentComplete == percentComplete) return;
m_PercentComplete = percentComplete;
m_DirtyDonationVendor = true;
}
void SetTotalDonated(float totalDonated){
if (m_TotalDonated == totalDonated) return;
m_TotalDonated = totalDonated;
m_DirtyDonationVendor = true;
}
void SetTotalRemaining(float totalRemaining){
if (m_TotalRemaining == totalRemaining) return;
m_TotalRemaining = totalRemaining;
m_DirtyDonationVendor = true;
}
private:
bool m_DirtyDonationVendor = false;
float m_PercentComplete = 0.0;
int32_t m_TotalDonated = 0;
int32_t m_TotalRemaining = 0;
uint32_t m_ActivityId = 0;
};
#endif //!__DONATIONVENDORCOMPONENT__H__

View File

@@ -0,0 +1,5 @@
#include "GateRushComponent.h"
GateRushComponent::GateRushComponent(Entity* parent, int32_t componentId) : RacingControlComponent(parent, componentId) {
}

View File

@@ -0,0 +1,15 @@
#ifndef __GATERUSHCONTROLCOMPONENT__H__
#define __GATERUSHCONTROLCOMPONENT__H__
#include "RacingControlComponent.h"
#include "eReplicaComponentType.h"
class Entity;
class GateRushComponent final : public RacingControlComponent {
public:
inline static const eReplicaComponentType ComponentType = eReplicaComponentType::GATE_RUSH_CONTROL;
GateRushComponent(Entity* parent, int32_t componentId);
};
#endif //!__GATERUSHCONTROLCOMPONENT__H__

View File

@@ -1,7 +1,7 @@
#include "VehiclePhysicsComponent.h" #include "HavokVehiclePhysicsComponent.h"
#include "EntityManager.h" #include "EntityManager.h"
VehiclePhysicsComponent::VehiclePhysicsComponent(Entity* parent) : Component(parent) { HavokVehiclePhysicsComponent::HavokVehiclePhysicsComponent(Entity* parent) : Component(parent) {
m_Position = NiPoint3::ZERO; m_Position = NiPoint3::ZERO;
m_Rotation = NiQuaternion::IDENTITY; m_Rotation = NiQuaternion::IDENTITY;
m_Velocity = NiPoint3::ZERO; m_Velocity = NiPoint3::ZERO;
@@ -14,69 +14,53 @@ VehiclePhysicsComponent::VehiclePhysicsComponent(Entity* parent) : Component(par
m_EndBehavior = GeneralUtils::GenerateRandomNumber<uint32_t>(0, 7); m_EndBehavior = GeneralUtils::GenerateRandomNumber<uint32_t>(0, 7);
} }
VehiclePhysicsComponent::~VehiclePhysicsComponent() { void HavokVehiclePhysicsComponent::SetPosition(const NiPoint3& pos) {
}
void VehiclePhysicsComponent::SetPosition(const NiPoint3& pos) {
if (pos == m_Position) return; if (pos == m_Position) return;
m_DirtyPosition = true; m_DirtyPosition = true;
m_Position = pos; m_Position = pos;
} }
void VehiclePhysicsComponent::SetRotation(const NiQuaternion& rot) { void HavokVehiclePhysicsComponent::SetRotation(const NiQuaternion& rot) {
if (rot == m_Rotation) return; if (rot == m_Rotation) return;
m_DirtyPosition = true; m_DirtyPosition = true;
m_Rotation = rot; m_Rotation = rot;
} }
void VehiclePhysicsComponent::SetVelocity(const NiPoint3& vel) { void HavokVehiclePhysicsComponent::SetVelocity(const NiPoint3& vel) {
if (vel == m_Velocity) return; if (vel == m_Velocity) return;
m_DirtyPosition = true; m_DirtyPosition = true;
m_Velocity = vel; m_Velocity = vel;
} }
void VehiclePhysicsComponent::SetAngularVelocity(const NiPoint3& vel) { void HavokVehiclePhysicsComponent::SetAngularVelocity(const NiPoint3& vel) {
if (vel == m_AngularVelocity) return; if (vel == m_AngularVelocity) return;
m_DirtyPosition = true; m_DirtyPosition = true;
m_AngularVelocity = vel; m_AngularVelocity = vel;
} }
void VehiclePhysicsComponent::SetIsOnGround(bool val) { void HavokVehiclePhysicsComponent::SetIsOnGround(bool val) {
if (val == m_IsOnGround) return; if (val == m_IsOnGround) return;
m_DirtyPosition = true; m_DirtyPosition = true;
m_IsOnGround = val; m_IsOnGround = val;
} }
void VehiclePhysicsComponent::SetIsOnRail(bool val) { void HavokVehiclePhysicsComponent::SetIsOnRail(bool val) {
if (val == m_IsOnRail) return; if (val == m_IsOnRail) return;
m_DirtyPosition = true; m_DirtyPosition = true;
m_IsOnRail = val; m_IsOnRail = val;
} }
void VehiclePhysicsComponent::SetRemoteInputInfo(const RemoteInputInfo& remoteInputInfo) { void HavokVehiclePhysicsComponent::SetRemoteInputInfo(const RemoteInputInfo& remoteInputInfo) {
if (m_RemoteInputInfo == remoteInputInfo) return; if (m_RemoteInputInfo == remoteInputInfo) return;
this->m_RemoteInputInfo = remoteInputInfo; this->m_RemoteInputInfo = remoteInputInfo;
m_DirtyRemoteInput = true; m_DirtyRemoteInput = true;
} }
void VehiclePhysicsComponent::SetDirtyPosition(bool val) { void HavokVehiclePhysicsComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags) {
m_DirtyPosition = val;
}
void VehiclePhysicsComponent::SetDirtyVelocity(bool val) {
m_DirtyVelocity = val;
}
void VehiclePhysicsComponent::SetDirtyAngularVelocity(bool val) {
m_DirtyAngularVelocity = val;
}
void VehiclePhysicsComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags) {
outBitStream->Write(bIsInitialUpdate || m_DirtyPosition); outBitStream->Write(bIsInitialUpdate || m_DirtyPosition);
if (bIsInitialUpdate || m_DirtyPosition) { if (bIsInitialUpdate || m_DirtyPosition) {
m_DirtyPosition = false; if (!bIsInitialUpdate) m_DirtyPosition = false;
outBitStream->Write(m_Position.x); outBitStream->Write(m_Position.x);
outBitStream->Write(m_Position.y); outBitStream->Write(m_Position.y);
outBitStream->Write(m_Position.z); outBitStream->Write(m_Position.z);
@@ -95,7 +79,7 @@ void VehiclePhysicsComponent::Serialize(RakNet::BitStream* outBitStream, bool bI
outBitStream->Write(m_Velocity.x); outBitStream->Write(m_Velocity.x);
outBitStream->Write(m_Velocity.y); outBitStream->Write(m_Velocity.y);
outBitStream->Write(m_Velocity.z); outBitStream->Write(m_Velocity.z);
m_DirtyVelocity = false; if (!bIsInitialUpdate) m_DirtyVelocity = false;
} }
outBitStream->Write(bIsInitialUpdate || m_DirtyAngularVelocity); outBitStream->Write(bIsInitialUpdate || m_DirtyAngularVelocity);
@@ -104,7 +88,7 @@ void VehiclePhysicsComponent::Serialize(RakNet::BitStream* outBitStream, bool bI
outBitStream->Write(m_AngularVelocity.x); outBitStream->Write(m_AngularVelocity.x);
outBitStream->Write(m_AngularVelocity.y); outBitStream->Write(m_AngularVelocity.y);
outBitStream->Write(m_AngularVelocity.z); outBitStream->Write(m_AngularVelocity.z);
m_DirtyAngularVelocity = false; if (!bIsInitialUpdate) m_DirtyAngularVelocity = false;
} }
outBitStream->Write0(); // local_space_info. TODO: Implement this outBitStream->Write0(); // local_space_info. TODO: Implement this
@@ -115,7 +99,7 @@ void VehiclePhysicsComponent::Serialize(RakNet::BitStream* outBitStream, bool bI
outBitStream->Write(m_RemoteInputInfo.m_RemoteInputY); outBitStream->Write(m_RemoteInputInfo.m_RemoteInputY);
outBitStream->Write(m_RemoteInputInfo.m_IsPowersliding); outBitStream->Write(m_RemoteInputInfo.m_IsPowersliding);
outBitStream->Write(m_RemoteInputInfo.m_IsModified); outBitStream->Write(m_RemoteInputInfo.m_IsModified);
m_DirtyRemoteInput = false; if (!bIsInitialUpdate) m_DirtyRemoteInput = false;
} }
outBitStream->Write(125.0f); // remote_input_ping TODO: Figure out how this should be calculated as it seems to be constant through the whole race. outBitStream->Write(125.0f); // remote_input_ping TODO: Figure out how this should be calculated as it seems to be constant through the whole race.
@@ -132,12 +116,3 @@ void VehiclePhysicsComponent::Serialize(RakNet::BitStream* outBitStream, bool bI
outBitStream->Write0(); outBitStream->Write0();
} }
void VehiclePhysicsComponent::Update(float deltaTime) {
if (m_SoftUpdate > 5) {
EntityManager::Instance()->SerializeEntity(m_Parent);
m_SoftUpdate = 0;
} else {
m_SoftUpdate += deltaTime;
}
}

View File

@@ -26,17 +26,14 @@ struct RemoteInputInfo {
/** /**
* Physics component for vehicles. * Physics component for vehicles.
*/ */
class VehiclePhysicsComponent : public Component { class HavokVehiclePhysicsComponent final : public Component {
public: public:
static const eReplicaComponentType ComponentType = eReplicaComponentType::VEHICLE_PHYSICS; inline static const eReplicaComponentType ComponentType = eReplicaComponentType::HAVOK_VEHICLE_PHYSICS;
VehiclePhysicsComponent(Entity* parentEntity); HavokVehiclePhysicsComponent(Entity* parentEntity);
~VehiclePhysicsComponent() override;
void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags); void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags);
void Update(float deltaTime) override;
/** /**
* Sets the position * Sets the position
* @param pos the new position * @param pos the new position
@@ -109,9 +106,9 @@ public:
*/ */
const bool GetIsOnRail() const { return m_IsOnRail; } const bool GetIsOnRail() const { return m_IsOnRail; }
void SetDirtyPosition(bool val); void SetDirtyPosition(bool val) { m_DirtyPosition = val; }
void SetDirtyVelocity(bool val); void SetDirtyVelocity(bool val) { m_DirtyVelocity = val; }
void SetDirtyAngularVelocity(bool val); void SetDirtyAngularVelocity(bool val) { m_DirtyAngularVelocity = val; }
void SetRemoteInputInfo(const RemoteInputInfo&); void SetRemoteInputInfo(const RemoteInputInfo&);
private: private:

File diff suppressed because it is too large Load Diff

View File

@@ -18,7 +18,7 @@
#include "Component.h" #include "Component.h"
#include "ItemSetPassiveAbility.h" #include "ItemSetPassiveAbility.h"
#include "eItemSetPassiveAbilityID.h" #include "eItemSetPassiveAbilityID.h"
#include "PossessorComponent.h" #include "PossessionComponent.h"
#include "eInventoryType.h" #include "eInventoryType.h"
#include "eReplicaComponentType.h" #include "eReplicaComponentType.h"
#include "eLootSourceType.h" #include "eLootSourceType.h"
@@ -35,17 +35,17 @@ enum class eItemType : int32_t;
* of different types, each type representing a different group of items, see `eInventoryType` for a list of * of different types, each type representing a different group of items, see `eInventoryType` for a list of
* inventories. * inventories.
*/ */
class InventoryComponent : public Component class InventoryComponent final : public Component
{ {
public: public:
static const eReplicaComponentType ComponentType = eReplicaComponentType::INVENTORY; inline static const eReplicaComponentType ComponentType = eReplicaComponentType::INVENTORY;
explicit InventoryComponent(Entity* parent, tinyxml2::XMLDocument* document = nullptr); explicit InventoryComponent(Entity* parent, tinyxml2::XMLDocument* document = nullptr);
void Update(float deltaTime) override; void Update(float deltaTime) override;
void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags); void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags);
void LoadXml(tinyxml2::XMLDocument* document); void LoadXml(tinyxml2::XMLDocument* document);
void UpdateXml(tinyxml2::XMLDocument* document) override; void UpdateXml(tinyxml2::XMLDocument* document) override;
void ResetFlags(); void ResetFlags() { m_Dirty = false; };
/** /**
* Returns an inventory of the specified type, if it exists * Returns an inventory of the specified type, if it exists
@@ -58,10 +58,12 @@ public:
* Returns all the inventories this entity has, indexed by type * Returns all the inventories this entity has, indexed by type
* @return all the inventories this entity has, indexed by type * @return all the inventories this entity has, indexed by type
*/ */
const std::map<eInventoryType, Inventory*>& GetInventories() const; const std::map<eInventoryType, Inventory*>& GetInventories() const { return m_Inventories; }
/** /**
* Returns the amount of items this entity possesses of a certain LOT * Returns the amount of items this entity possesses of a certain LOT
* This method counts the lot count for all inventories, including inventories the player may not be able to see.
* If you need the count in a specific inventory, call the inventory equivalent.
* @param lot the lot to search for * @param lot the lot to search for
* @return the amount of items this entity possesses the specified LOT * @return the amount of items this entity possesses the specified LOT
*/ */
@@ -79,7 +81,7 @@ public:
* Returns the items that are currently equipped by this entity * Returns the items that are currently equipped by this entity
* @return the items that are currently equipped by this entity * @return the items that are currently equipped by this entity
*/ */
const EquipmentMap& GetEquippedItems() const; const EquipmentMap& GetEquippedItems() const { return m_Equipped; }
/** /**
* Adds an item to the inventory of the entity * Adds an item to the inventory of the entity
@@ -206,7 +208,7 @@ public:
* @param item the Item to unequip * @param item the Item to unequip
* @return if we were successful * @return if we were successful
*/ */
void HandlePossession(Item* item); void HandlePossession(Item* item) const;
/** /**
* Adds a buff related to equipping a lot to the entity * Adds a buff related to equipping a lot to the entity
@@ -247,13 +249,13 @@ public:
* Sets the current consumable lot * Sets the current consumable lot
* @param lot the lot to set as consumable * @param lot the lot to set as consumable
*/ */
void SetConsumable(LOT lot); void SetConsumable(LOT lot) { m_Consumable = lot; };
/** /**
* Returns the current consumable lot * Returns the current consumable lot
* @return the current consumable lot * @return the current consumable lot
*/ */
LOT GetConsumable() const; LOT GetConsumable() const { return m_Consumable; }
/** /**
* Finds all the buffs related to a lot * Finds all the buffs related to a lot
@@ -285,7 +287,7 @@ public:
* Triggers one of the passive abilities from the equipped item set * Triggers one of the passive abilities from the equipped item set
* @param trigger the trigger to fire * @param trigger the trigger to fire
*/ */
void TriggerPassiveAbility(PassiveAbilityTrigger trigger, Entity* target = nullptr); void TriggerPassiveAbility(PassiveAbilityTrigger trigger, Entity* target = nullptr) const;
/** /**
* Returns if the entity has any of the passed passive abilities equipped * Returns if the entity has any of the passed passive abilities equipped
@@ -325,13 +327,13 @@ public:
* @param id the id of the object to check for * @param id the id of the object to check for
* @return if the provided object ID is in this inventory and is a pet * @return if the provided object ID is in this inventory and is a pet
*/ */
bool IsPet(LWOOBJID id) const; bool IsPet(const LWOOBJID& id) const { return m_Pets.find(id) != m_Pets.end(); }
/** /**
* Removes pet database information from the item with the specified object id * Removes pet database information from the item with the specified object id
* @param id the object id to remove pet info for * @param id the object id to remove pet info for
*/ */
void RemoveDatabasePet(LWOOBJID id); void RemoveDatabasePet(const LWOOBJID& id) { m_Pets.erase(id); }
/** /**
* Returns the current behavior slot active for the passed item type * Returns the current behavior slot active for the passed item type
@@ -359,14 +361,14 @@ public:
* *
* @param equippedItem The item script to lookup and call equip on * @param equippedItem The item script to lookup and call equip on
*/ */
void EquipScripts(Item* equippedItem); void EquipScripts(Item* equippedItem) const;
/** /**
* Call this when you unequip an item. This calls OnFactionTriggerItemUnequipped for any scripts found on the items. * Call this when you unequip an item. This calls OnFactionTriggerItemUnequipped for any scripts found on the items.
* *
* @param unequippedItem The item script to lookup and call unequip on * @param unequippedItem The item script to lookup and call unequip on
*/ */
void UnequipScripts(Item* unequippedItem); void UnequipScripts(Item* unequippedItem) const;
~InventoryComponent() override; ~InventoryComponent() override;

View File

@@ -0,0 +1,32 @@
#include "ItemComponent.h"
#include "Entity.h"
#include "eUgcModerationStatus.h"
ItemComponent::ItemComponent(Entity* parent) : Component(parent) {
m_ParentEntity = parent;
m_DirtyItemInfo = false;
m_UgId = m_ParentEntity->GetVarAs<LWOOBJID>(u"userModelID");
if (m_UgId == LWOOBJID_EMPTY) m_UgId = m_ParentEntity->GetObjectID();
m_UgModerationStatus = eUgcModerationStatus::NoStatus;
m_UgDescription = u"";
}
void ItemComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags) {
outBitStream->Write(m_DirtyItemInfo || bIsInitialUpdate);
if (m_DirtyItemInfo || bIsInitialUpdate) {
outBitStream->Write(m_UgId);
outBitStream->Write(m_UgModerationStatus);
outBitStream->Write(!m_UgDescription.empty());
if (!m_UgDescription.empty()){
outBitStream->Write<uint32_t>(m_UgDescription.length());
outBitStream->Write(reinterpret_cast<const char*>(m_UgDescription.c_str()), m_UgDescription.length() * sizeof(uint16_t));
}
m_DirtyItemInfo = false;
}
}

View File

@@ -0,0 +1,55 @@
#ifndef __ITEMCOMPONENT__H__
#define __ITEMCOMPONENT__H__
#pragma once
#include "dCommonVars.h"
#include "RakNetTypes.h"
#include "NiPoint3.h"
#include "NiQuaternion.h"
#include "Component.h"
#include "eReplicaComponentType.h"
class Entity;
enum class eUgcModerationStatus : uint32_t;
class ItemComponent final : public Component {
public:
static const eReplicaComponentType ComponentType = eReplicaComponentType::ITEM;
ItemComponent(Entity* parent);
void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags);
void SetUgId(LWOOBJID id) { m_UgId = id; m_DirtyItemInfo = true; };
LWOOBJID GetUgId() { return m_UgId; };
void SetUgModerationStatus(eUgcModerationStatus status) { m_UgModerationStatus = status; m_DirtyItemInfo = true; };
eUgcModerationStatus GetUgModerationStatus() { return m_UgModerationStatus; };
void SetUgDescription(std::u16string description) { m_UgDescription = description; m_DirtyItemInfo = true; };
std::u16string GetUgDescription() { return m_UgDescription;};
private:
/**
* If we have change the item info
*/
bool m_DirtyItemInfo;
/**
* The ID of the user that made the model
*/
LWOOBJID m_UgId;
/**
* Whether or not the description of this item is approved.
*/
eUgcModerationStatus m_UgModerationStatus;
/**
* The user generated description
*/
std::u16string m_UgDescription;
};
#endif //!__ITEMCOMPONENT__H__

View File

@@ -3,42 +3,32 @@
#include "EntityManager.h" #include "EntityManager.h"
LUPExhibitComponent::LUPExhibitComponent(Entity* parent) : Component(parent) { LUPExhibitComponent::LUPExhibitComponent(Entity* parent) : Component(parent) {
m_Exhibits = { 11121, 11295, 11423, 11979 };
m_ExhibitIndex = 0; m_ExhibitIndex = 0;
m_UpdateTimer = 0.0f;
m_Exhibit = m_Exhibits[m_ExhibitIndex]; m_Exhibit = m_Exhibits.front();
m_DirtyExhibitInfo = true;
}
LUPExhibitComponent::~LUPExhibitComponent() {
} }
void LUPExhibitComponent::Update(float deltaTime) { void LUPExhibitComponent::Update(float deltaTime) {
m_UpdateTimer += deltaTime; m_UpdateTimer += deltaTime;
if (m_UpdateTimer < 20.0f) return;
if (m_UpdateTimer > 20.0f) { NextExhibit();
NextExhibit(); m_UpdateTimer = 0.0f;
m_UpdateTimer = 0.0f;
}
} }
void LUPExhibitComponent::NextExhibit() { void LUPExhibitComponent::NextExhibit() {
m_ExhibitIndex++; m_ExhibitIndex++;
if (m_ExhibitIndex >= m_Exhibits.size()) { // After 1361 years, this will skip exhibit 4 one time. I think modulo is ok here.
m_ExhibitIndex = 0; m_Exhibit = m_Exhibits.at(m_ExhibitIndex % m_Exhibits.size());
} EntityManager::Instance()->SerializeEntity(m_ParentEntity);
m_Exhibit = m_Exhibits[m_ExhibitIndex];
EntityManager::Instance()->SerializeEntity(m_Parent);
} }
void LUPExhibitComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, uint32_t& flags) { void LUPExhibitComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, uint32_t& flags) {
outBitStream->Write1(); // Dirty flag? outBitStream->Write(bIsInitialUpdate || m_DirtyExhibitInfo);
outBitStream->Write(m_Exhibit); if (bIsInitialUpdate || m_DirtyExhibitInfo) {
outBitStream->Write(m_Exhibit);
if (!bIsInitialUpdate) m_DirtyExhibitInfo = false;
}
} }

View File

@@ -8,13 +8,12 @@
* Component that handles the LOT that is shown in the LUP exhibit in the LUP world. Works by setting a timer and * Component that handles the LOT that is shown in the LUP exhibit in the LUP world. Works by setting a timer and
* switching the LOTs around that we'd like to display. * switching the LOTs around that we'd like to display.
*/ */
class LUPExhibitComponent : public Component class LUPExhibitComponent final : public Component
{ {
public: public:
static const eReplicaComponentType ComponentType = eReplicaComponentType::EXHIBIT; inline static const eReplicaComponentType ComponentType = eReplicaComponentType::EXHIBIT;
LUPExhibitComponent(Entity* parent); LUPExhibitComponent(Entity* parent);
~LUPExhibitComponent();
void Update(float deltaTime) override; void Update(float deltaTime) override;
void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, uint32_t& flags); void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, uint32_t& flags);
@@ -36,10 +35,13 @@ private:
/** /**
* The list of possible exhibits to show * The list of possible exhibits to show
*/ */
std::vector<LOT> m_Exhibits; const std::vector<LOT> m_Exhibits = { 11121, 11295, 11423, 11979 };
/** /**
* The current index in the exhibit list * The current index in the exhibit list
*/ */
size_t m_ExhibitIndex; size_t m_ExhibitIndex;
// Whether or not to notify clients of a change in the visible exhibit
bool m_DirtyExhibitInfo;
}; };

View File

@@ -7,7 +7,7 @@
#include "CDRewardsTable.h" #include "CDRewardsTable.h"
LevelProgressionComponent::LevelProgressionComponent(Entity* parent) : Component(parent) { LevelProgressionComponent::LevelProgressionComponent(Entity* parent) : Component(parent) {
m_Parent = parent; m_ParentEntity = parent;
m_Level = 1; m_Level = 1;
m_SpeedBase = 500.0f; m_SpeedBase = 500.0f;
m_CharacterVersion = eCharacterVersion::LIVE; m_CharacterVersion = eCharacterVersion::LIVE;
@@ -35,55 +35,60 @@ void LevelProgressionComponent::LoadFromXml(tinyxml2::XMLDocument* doc) {
uint32_t characterVersion; uint32_t characterVersion;
level->QueryAttribute("cv", &characterVersion); level->QueryAttribute("cv", &characterVersion);
m_CharacterVersion = static_cast<eCharacterVersion>(characterVersion); m_CharacterVersion = static_cast<eCharacterVersion>(characterVersion);
auto* controllablePhysicsComponent = m_ParentEntity->GetComponent<ControllablePhysicsComponent>();
if (!controllablePhysicsComponent) return;
controllablePhysicsComponent->SetSpeedMultiplier(GetSpeedBase() / 500.0f);
} }
void LevelProgressionComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags) { void LevelProgressionComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags) {
outBitStream->Write(bIsInitialUpdate || m_DirtyLevelInfo); outBitStream->Write(bIsInitialUpdate || m_DirtyLevelInfo);
if (bIsInitialUpdate || m_DirtyLevelInfo) outBitStream->Write(m_Level); if (bIsInitialUpdate || m_DirtyLevelInfo) {
m_DirtyLevelInfo = false; outBitStream->Write(m_Level);
if (!bIsInitialUpdate) m_DirtyLevelInfo = false;
}
} }
void LevelProgressionComponent::HandleLevelUp() { void LevelProgressionComponent::HandleLevelUp() {
auto* rewardsTable = CDClientManager::Instance().GetTable<CDRewardsTable>(); auto* rewardsTable = CDClientManager::Instance().GetTable<CDRewardsTable>();
const auto& rewards = rewardsTable->GetByLevelID(m_Level); const auto& rewards = rewardsTable->GetByLevelID(m_Level);
bool rewardingItem = rewards.size() > 0; if (rewards.empty()) return;
auto* inventoryComponent = m_Parent->GetComponent<InventoryComponent>(); auto* inventoryComponent = m_ParentEntity->GetComponent<InventoryComponent>();
auto* controllablePhysicsComponent = m_Parent->GetComponent<ControllablePhysicsComponent>(); auto* controllablePhysicsComponent = m_ParentEntity->GetComponent<ControllablePhysicsComponent>();
if (!inventoryComponent || !controllablePhysicsComponent) return; if (!inventoryComponent || !controllablePhysicsComponent) return;
// Tell the client we beginning to send level rewards. // Tell the client we beginning to send level rewards.
if (rewardingItem) GameMessages::NotifyLevelRewards(m_Parent->GetObjectID(), m_Parent->GetSystemAddress(), m_Level, rewardingItem); GameMessages::NotifyLevelRewards(m_ParentEntity->GetObjectID(), m_ParentEntity->GetSystemAddress(), m_Level, true);
for (auto* reward : rewards) { for (auto* reward : rewards) {
switch (reward->rewardType) { switch (reward->rewardType) {
case 0: case 0:
inventoryComponent->AddItem(reward->value, reward->count, eLootSourceType::LEVEL_REWARD); inventoryComponent->AddItem(reward->value, reward->count, eLootSourceType::LEVEL_REWARD);
break; break;
case 4: case 4: {
{
auto* items = inventoryComponent->GetInventory(eInventoryType::ITEMS); auto* items = inventoryComponent->GetInventory(eInventoryType::ITEMS);
if (!items) continue;
items->SetSize(items->GetSize() + reward->value); items->SetSize(items->GetSize() + reward->value);
break;
} }
break;
case 9: case 9:
SetSpeedBase(static_cast<float>(reward->value) ); SetSpeedBase(static_cast<float>(reward->value));
controllablePhysicsComponent->SetSpeedMultiplier(GetSpeedBase() / 500.0f); controllablePhysicsComponent->SetSpeedMultiplier(GetSpeedBase() / 500.0f);
break; break;
case 11: case 11:
case 12: case 12:
break;
default: default:
break; break;
} }
} }
// Tell the client we have finished sending level rewards. // Tell the client we have finished sending level rewards.
if (rewardingItem) GameMessages::NotifyLevelRewards(m_Parent->GetObjectID(), m_Parent->GetSystemAddress(), m_Level, !rewardingItem); GameMessages::NotifyLevelRewards(m_ParentEntity->GetObjectID(), m_ParentEntity->GetSystemAddress(), m_Level, false);
} }
void LevelProgressionComponent::SetRetroactiveBaseSpeed(){ void LevelProgressionComponent::SetRetroactiveBaseSpeed() {
if (m_Level >= 20) m_SpeedBase = 525.0f; if (m_Level >= 20) m_SpeedBase = 525.0f;
auto* controllablePhysicsComponent = m_Parent->GetComponent<ControllablePhysicsComponent>(); auto* controllablePhysicsComponent = m_ParentEntity->GetComponent<ControllablePhysicsComponent>();
if (controllablePhysicsComponent) controllablePhysicsComponent->SetSpeedMultiplier(m_SpeedBase / 500.0f); if (controllablePhysicsComponent) controllablePhysicsComponent->SetSpeedMultiplier(m_SpeedBase / 500.0f);
} }

View File

@@ -11,9 +11,9 @@
* *
*/ */
class LevelProgressionComponent : public Component { class LevelProgressionComponent final : public Component {
public: public:
static const eReplicaComponentType ComponentType = eReplicaComponentType::LEVEL_PROGRESSION; inline static const eReplicaComponentType ComponentType = eReplicaComponentType::LEVEL_PROGRESSION;
/** /**
* Constructor for this component * Constructor for this component
@@ -45,7 +45,11 @@ public:
* Sets the level of the entity * Sets the level of the entity
* @param level the level to set * @param level the level to set
*/ */
void SetLevel(uint32_t level) { m_Level = level; m_DirtyLevelInfo = true; } void SetLevel(uint32_t level) {
if (m_Level == level) return;
m_Level = level;
m_DirtyLevelInfo = true;
}
/** /**
* Gets the current Speed Base of the entity * Gets the current Speed Base of the entity
@@ -98,7 +102,7 @@ private:
float m_SpeedBase; float m_SpeedBase;
/** /**
* The Character format version * The Character format version. Certain bug fixes increment this version number.
*/ */
eCharacterVersion m_CharacterVersion; eCharacterVersion m_CharacterVersion;

View File

@@ -0,0 +1,7 @@
#include "Entity.h"
#include "MinigameControlComponent.h"
MinigameControlComponent::MinigameControlComponent(Entity* parent, int32_t componentId) : ActivityComponent(parent, componentId) {
}

View File

@@ -0,0 +1,15 @@
#ifndef __MINIGAMECONTROLCOMPONENT__H__
#define __MINIGAMECONTROLCOMPONENT__H__
#include "ActivityComponent.h"
#include "eReplicaComponentType.h"
class Entity;
class MinigameControlComponent : public ActivityComponent {
public:
inline static const eReplicaComponentType ComponentType = eReplicaComponentType::MINIGAME_CONTROL;
MinigameControlComponent(Entity* parent, int32_t componentId);
};
#endif //!__MINIGAMECONTROLCOMPONENT__H__

View File

@@ -1,9 +1,8 @@
/* /*
* Darkflame Universe * Darkflame Universe
* Copyright 2019 * Copyright 2023
*/ */
#include <sstream>
#include <string> #include <string>
#include "MissionComponent.h" #include "MissionComponent.h"
@@ -19,62 +18,40 @@
#include "MissionPrerequisites.h" #include "MissionPrerequisites.h"
#include "AchievementCacheKey.h" #include "AchievementCacheKey.h"
#include "eMissionState.h" #include "eMissionState.h"
#include "GeneralUtils.h"
// MARK: Mission Component
std::unordered_map<AchievementCacheKey, std::vector<uint32_t>> MissionComponent::m_AchievementCache = {}; std::unordered_map<AchievementCacheKey, std::vector<uint32_t>> MissionComponent::m_AchievementCache = {};
//! Initializer
MissionComponent::MissionComponent(Entity* parent) : Component(parent) { MissionComponent::MissionComponent(Entity* parent) : Component(parent) {
m_LastUsedMissionOrderUID = dZoneManager::Instance()->GetUniqueMissionIdStartingValue(); m_LastUsedMissionOrderUID = dZoneManager::Instance()->GetUniqueMissionIdStartingValue();
} }
//! Destructor
MissionComponent::~MissionComponent() { MissionComponent::~MissionComponent() {
for (const auto& mission : m_Missions) { for (const auto& [missionId, mission] : m_Missions) {
delete mission.second; delete mission;
} }
this->m_Missions.clear();
} }
Mission* MissionComponent::GetMission(const uint32_t missionId) const { Mission* MissionComponent::GetMission(const uint32_t missionId) const {
if (m_Missions.count(missionId) == 0) { if (m_Missions.count(missionId) == 0) return nullptr;
return nullptr;
}
const auto& index = m_Missions.find(missionId); const auto& index = m_Missions.find(missionId);
if (index == m_Missions.end()) { return index == m_Missions.end() ? nullptr : index->second;
return nullptr;
}
return index->second;
} }
eMissionState MissionComponent::GetMissionState(const uint32_t missionId) const { eMissionState MissionComponent::GetMissionState(const uint32_t missionId) const {
auto* mission = GetMission(missionId); auto* mission = GetMission(missionId);
if (mission == nullptr) { if (!mission) return CanAccept(missionId) ? eMissionState::AVAILABLE : eMissionState::UNKNOWN;
return CanAccept(missionId) ? eMissionState::AVAILABLE : eMissionState::UNKNOWN;
}
return mission->GetMissionState(); return mission->GetMissionState();
} }
const std::unordered_map<uint32_t, Mission*>& MissionComponent::GetMissions() const {
return m_Missions;
}
bool MissionComponent::CanAccept(const uint32_t missionId) const { bool MissionComponent::CanAccept(const uint32_t missionId) const {
return MissionPrerequisites::CanAccept(missionId, m_Missions); return MissionPrerequisites::CanAccept(missionId, m_Missions);
} }
void MissionComponent::AcceptMission(const uint32_t missionId, const bool skipChecks) { void MissionComponent::AcceptMission(const uint32_t missionId, const bool skipChecks) {
if (!skipChecks && !CanAccept(missionId)) { if (!skipChecks && !CanAccept(missionId)) {
return; return;
@@ -83,7 +60,7 @@ void MissionComponent::AcceptMission(const uint32_t missionId, const bool skipCh
// If this is a daily mission, it may already be "accepted" // If this is a daily mission, it may already be "accepted"
auto* mission = this->GetMission(missionId); auto* mission = this->GetMission(missionId);
if (mission != nullptr) { if (mission) {
if (mission->GetClientInfo().repeatable) { if (mission->GetClientInfo().repeatable) {
mission->Accept(); mission->Accept();
if (mission->IsMission()) mission->SetUniqueMissionOrderID(++m_LastUsedMissionOrderUID); if (mission->IsMission()) mission->SetUniqueMissionOrderID(++m_LastUsedMissionOrderUID);
@@ -100,54 +77,41 @@ void MissionComponent::AcceptMission(const uint32_t missionId, const bool skipCh
this->m_Missions.insert_or_assign(missionId, mission); this->m_Missions.insert_or_assign(missionId, mission);
if (missionId == 1728) { //Needs to send a mail
//Needs to send a mail if (missionId == 1728) Mail::HandleNotificationRequest(m_ParentEntity->GetSystemAddress(), m_ParentEntity->GetObjectID());
auto address = m_Parent->GetSystemAddress();
Mail::HandleNotificationRequest(address, m_Parent->GetObjectID());
}
} }
void MissionComponent::CompleteMission(const uint32_t missionId, const bool skipChecks, const bool yieldRewards) { void MissionComponent::CompleteMission(const uint32_t missionId, const bool skipChecks, const bool yieldRewards) {
// Get the mission first // Get the mission first
auto* mission = this->GetMission(missionId); auto* mission = this->GetMission(missionId);
if (mission == nullptr) { if (!mission) {
AcceptMission(missionId, skipChecks); AcceptMission(missionId, skipChecks);
mission = this->GetMission(missionId); mission = this->GetMission(missionId);
if (mission == nullptr) { if (!mission) return;
return;
}
} }
//If this mission is not repeatable, and already completed, we stop here. //If this mission is not repeatable, and already completed, we stop here.
if (mission->IsComplete() && !mission->IsRepeatable()) { if (mission->IsComplete() && !mission->IsRepeatable()) return;
return;
}
mission->Complete(yieldRewards); mission->Complete(yieldRewards);
} }
void MissionComponent::RemoveMission(uint32_t missionId) { void MissionComponent::RemoveMission(const uint32_t missionId) {
auto* mission = this->GetMission(missionId); auto missionItr = m_Missions.find(missionId);
if (mission == nullptr) { if (missionItr == m_Missions.end()) return;
return;
}
delete mission; delete missionItr->second;
m_Missions.erase(missionId); m_Missions.erase(missionItr);
} }
void MissionComponent::Progress(eMissionTaskType type, int32_t value, LWOOBJID associate, const std::string& targets, int32_t count, bool ignoreAchievements) { void MissionComponent::Progress(const eMissionTaskType type, const int32_t value, const LWOOBJID& associate, const std::string& targets, const int32_t count, const bool ignoreAchievements) {
for (const auto& pair : m_Missions) { for (const auto& [missionId, mission] : m_Missions) {
auto* mission = pair.second; if (!mission) continue;
if (mission->IsAchievement() && ignoreAchievements) continue; if (mission->IsAchievement() && ignoreAchievements) continue;
if (mission->IsComplete()) continue; if (mission->IsComplete()) continue;
@@ -163,73 +127,57 @@ void MissionComponent::Progress(eMissionTaskType type, int32_t value, LWOOBJID a
void MissionComponent::ForceProgress(const uint32_t missionId, const uint32_t taskId, const int32_t value, const bool acceptMission) { void MissionComponent::ForceProgress(const uint32_t missionId, const uint32_t taskId, const int32_t value, const bool acceptMission) {
auto* mission = GetMission(missionId); auto* mission = GetMission(missionId);
if (mission == nullptr) { if (!mission) {
if (!acceptMission) { if (!acceptMission) return;
return;
}
AcceptMission(missionId); AcceptMission(missionId);
mission = GetMission(missionId); mission = GetMission(missionId);
if (mission == nullptr) { if (!mission) return;
return;
}
} }
for (auto* element : mission->GetTasks()) { std::for_each(mission->GetTasks().begin(), mission->GetTasks().end(), [value, taskId](MissionTask* element) {
if (element->GetClientInfo().uid != taskId) continue; if (element->GetClientInfo().uid != taskId) return;
element->AddProgress(value); element->AddProgress(value);
} });
if (!mission->IsComplete()) { if (!mission->IsComplete()) mission->CheckCompletion();
mission->CheckCompletion();
}
} }
void MissionComponent::ForceProgressTaskType(const uint32_t missionId, const uint32_t taskType, const int32_t value, const bool acceptMission) { void MissionComponent::ForceProgressTaskType(const uint32_t missionId, const uint32_t taskType, const int32_t value, const bool acceptMission) {
auto* mission = GetMission(missionId); auto* mission = GetMission(missionId);
if (mission == nullptr) { if (!mission) {
if (!acceptMission) { if (!acceptMission) return;
return;
}
CDMissions missionInfo; CDMissions missionInfo;
if (!GetMissionInfo(missionId, missionInfo)) { if (!GetMissionInfo(missionId, missionInfo)) return;
return;
}
if (missionInfo.isMission) { if (missionInfo.isMission) return;
return;
}
AcceptMission(missionId); AcceptMission(missionId);
mission = GetMission(missionId); mission = GetMission(missionId);
if (mission == nullptr) { if (!mission) return;
return;
}
} }
for (auto* element : mission->GetTasks()) { std::for_each(mission->GetTasks().begin(), mission->GetTasks().end(), [value, taskType](MissionTask* element) {
if (element->GetType() != static_cast<eMissionTaskType>(taskType)) continue; if (element->GetType() != static_cast<eMissionTaskType>(taskType)) return;
element->AddProgress(value); element->AddProgress(value);
} });
if (!mission->IsComplete()) { if (!mission->IsComplete()) mission->CheckCompletion();
mission->CheckCompletion();
}
} }
void MissionComponent::ForceProgressValue(uint32_t missionId, uint32_t taskType, int32_t value, bool acceptMission) { void MissionComponent::ForceProgressValue(const uint32_t missionId, const uint32_t taskType, const int32_t value, const bool acceptMission) {
auto* mission = GetMission(missionId); auto* mission = GetMission(missionId);
if (mission == nullptr) { if (!mission) {
if (!acceptMission) { if (!acceptMission) {
return; return;
} }
@@ -264,11 +212,11 @@ void MissionComponent::ForceProgressValue(uint32_t missionId, uint32_t taskType,
} }
} }
bool MissionComponent::GetMissionInfo(uint32_t missionId, CDMissions& result) { bool MissionComponent::GetMissionInfo(const uint32_t missionId, CDMissions& result) const {
auto* missionsTable = CDClientManager::Instance().GetTable<CDMissionsTable>(); auto* missionsTable = CDClientManager::Instance().GetTable<CDMissionsTable>();
const auto missions = missionsTable->Query([=](const CDMissions& entry) { const auto missions = missionsTable->Query([=](const CDMissions& entry) {
return entry.id == static_cast<int>(missionId); return entry.id == static_cast<uint32_t>(missionId);
}); });
if (missions.empty()) { if (missions.empty()) {
@@ -280,10 +228,7 @@ bool MissionComponent::GetMissionInfo(uint32_t missionId, CDMissions& result) {
return true; return true;
} }
#define MISSION_NEW_METHOD bool MissionComponent::LookForAchievements(const eMissionTaskType type, const int32_t value, const bool progress, const LWOOBJID& associate, const std::string& targets, const int32_t count) {
bool MissionComponent::LookForAchievements(eMissionTaskType type, int32_t value, bool progress, LWOOBJID associate, const std::string& targets, int32_t count) {
#ifdef MISSION_NEW_METHOD
// Query for achievments, using the cache // Query for achievments, using the cache
const auto& result = QueryAchievements(type, value, targets); const auto& result = QueryAchievements(type, value, targets);
@@ -291,9 +236,7 @@ bool MissionComponent::LookForAchievements(eMissionTaskType type, int32_t value,
for (const uint32_t missionID : result) { for (const uint32_t missionID : result) {
// Check if we already have this achievement // Check if we already have this achievement
if (GetMission(missionID) != nullptr) { if (GetMission(missionID)) continue;
continue;
}
// Check if we can accept this achievement // Check if we can accept this achievement
if (!MissionPrerequisites::CanAccept(missionID, m_Missions)) { if (!MissionPrerequisites::CanAccept(missionID, m_Missions)) {
@@ -311,87 +254,15 @@ bool MissionComponent::LookForAchievements(eMissionTaskType type, int32_t value,
any = true; any = true;
if (progress) { if (!progress) continue;
// Progress mission to bring it up to speed // Progress mission to bring it up to speed
instance->Progress(type, value, associate, targets, count); instance->Progress(type, value, associate, targets, count);
}
} }
return any; return any;
#else
auto* missionTasksTable = CDClientManager::Instance().GetTable<CDMissionTasksTable>();
auto* missionsTable = CDClientManager::Instance().GetTable<CDMissionsTable>();
auto tasks = missionTasksTable->Query([=](const CDMissionTasks& entry) {
return entry.taskType == static_cast<unsigned>(type);
});
auto any = false;
for (const auto& task : tasks) {
if (GetMission(task.id) != nullptr) {
continue;
}
const auto missionEntries = missionsTable->Query([=](const CDMissions& entry) {
return entry.id == static_cast<int>(task.id) && !entry.isMission;
});
if (missionEntries.empty()) {
continue;
}
const auto mission = missionEntries[0];
if (mission.isMission || !MissionPrerequisites::CanAccept(mission.id, m_Missions)) {
continue;
}
if (task.target != value && task.targetGroup != targets) {
auto stream = std::istringstream(task.targetGroup);
std::string token;
auto found = false;
while (std::getline(stream, token, ',')) {
try {
const auto target = std::stoul(token);
found = target == value;
if (found) {
break;
}
} catch (std::invalid_argument& exception) {
Game::logger->Log("MissionComponent", "Failed to parse target (%s): (%s)!", token.c_str(), exception.what());
}
}
if (!found) {
continue;
}
}
auto* instance = new Mission(this, mission.id);
m_Missions.insert_or_assign(mission.id, instance);
if (instance->IsMission()) instance->SetUniqueMissionOrderID(++m_LastUsedMissionOrderUID);
instance->Accept();
any = true;
if (progress) {
instance->Progress(type, value, associate, targets, count);
}
}
return any;
#endif
} }
const std::vector<uint32_t>& MissionComponent::QueryAchievements(eMissionTaskType type, int32_t value, const std::string targets) { const std::vector<uint32_t>& MissionComponent::QueryAchievements(const eMissionTaskType type, const int32_t value, const std::string& targets) {
// Create a hash which represent this query for achievements // Create a hash which represent this query for achievements
AchievementCacheKey toFind; AchievementCacheKey toFind;
toFind.SetType(type); toFind.SetType(type);
@@ -420,95 +291,68 @@ const std::vector<uint32_t>& MissionComponent::QueryAchievements(eMissionTaskTyp
// Seek the assosicated mission // Seek the assosicated mission
auto foundMission = false; auto foundMission = false;
const auto& mission = missionsTable->GetByMissionID(task.id, foundMission); const auto& cdMission = missionsTable->GetByMissionID(task.id, foundMission);
if (!foundMission || mission.isMission) { if (!foundMission || cdMission.isMission) continue;
continue;
}
// Compare the easy values // Compare the easy values
if (task.target == value || task.targetGroup == targets) { if (task.target == value || task.targetGroup == targets) {
result.push_back(mission.id); result.push_back(cdMission.id);
continue; continue;
} }
// Compare the target group, array separated by ',' // Compare the target group, array separated by ','
auto stream = std::istringstream(task.targetGroup); for (const auto& possibleMissionStr : GeneralUtils::SplitString(task.targetGroup, ',')) {
std::string token; uint32_t possibleMission;
if (GeneralUtils::TryParse(possibleMissionStr, possibleMission) && possibleMission == value) {
result.push_back(cdMission.id);
while (std::getline(stream, token, ',')) { break;
try {
if (std::stoi(token) == value) {
result.push_back(mission.id);
continue;
}
} catch (std::invalid_argument& exception) {
// Ignored
} }
} }
} }
// Insert into cache // Insert into cache and return the inserted value.
m_AchievementCache.insert_or_assign(toFind, result); return m_AchievementCache.insert_or_assign(toFind, result).first->second;
return m_AchievementCache.find(toFind)->second;
} }
bool MissionComponent::RequiresItem(const LOT lot) { bool MissionComponent::RequiresItem(const LOT lot) {
auto query = CDClientDatabase::CreatePreppedStmt( auto query = CDClientDatabase::CreatePreppedStmt("SELECT type FROM Objects WHERE id = ?;");
"SELECT type FROM Objects WHERE id = ?;"); query.bind(1, static_cast<int>(lot));
query.bind(1, (int)lot);
auto result = query.execQuery(); auto result = query.execQuery();
if (result.eof()) { if (result.eof()) return false;
return false;
}
if (!result.fieldIsNull(0)) { if (!result.fieldIsNull(0)) {
const auto type = std::string(result.getStringField(0)); const auto type = std::string(result.getStringField(0));
result.finalize(); if (type == "Powerup") return true;
if (type == "Powerup") {
return true;
}
} }
result.finalize(); for (const auto& [missionId, mission] : m_Missions) {
if (mission->IsComplete()) continue;
for (const auto& pair : m_Missions) {
auto* mission = pair.second;
if (mission->IsComplete()) {
continue;
}
for (auto* task : mission->GetTasks()) { for (auto* task : mission->GetTasks()) {
if (task->IsComplete() || task->GetType() != eMissionTaskType::GATHER) { if (task->IsComplete() || task->GetType() != eMissionTaskType::GATHER) {
continue; continue;
} }
if (!task->InAllTargets(lot)) { if (!task->InAllTargets(lot)) continue;
continue;
}
return true; return true;
} }
} }
return LookForAchievements(eMissionTaskType::GATHER, lot, false);
const auto required = LookForAchievements(eMissionTaskType::GATHER, lot, false);
return required;
} }
void MissionComponent::LoadFromXml(tinyxml2::XMLDocument* doc) { void MissionComponent::LoadFromXml(tinyxml2::XMLDocument* doc) {
if (doc == nullptr) return; if (!doc) return;
auto* mis = doc->FirstChildElement("obj")->FirstChildElement("mis"); auto* mis = doc->FirstChildElement("obj")->FirstChildElement("mis");
if (mis == nullptr) return; if (!mis) return;
auto* cur = mis->FirstChildElement("cur"); auto* cur = mis->FirstChildElement("cur");
auto* done = mis->FirstChildElement("done"); auto* done = mis->FirstChildElement("done");
@@ -516,7 +360,7 @@ void MissionComponent::LoadFromXml(tinyxml2::XMLDocument* doc) {
auto* doneM = done->FirstChildElement(); auto* doneM = done->FirstChildElement();
while (doneM) { while (doneM) {
int missionId; uint32_t missionId;
doneM->QueryAttribute("id", &missionId); doneM->QueryAttribute("id", &missionId);
@@ -533,7 +377,7 @@ void MissionComponent::LoadFromXml(tinyxml2::XMLDocument* doc) {
uint32_t missionOrder{}; uint32_t missionOrder{};
while (currentM) { while (currentM) {
int missionId; uint32_t missionId;
currentM->QueryAttribute("id", &missionId); currentM->QueryAttribute("id", &missionId);
@@ -543,7 +387,7 @@ void MissionComponent::LoadFromXml(tinyxml2::XMLDocument* doc) {
if (currentM->QueryAttribute("o", &missionOrder) == tinyxml2::XML_SUCCESS && mission->IsMission()) { if (currentM->QueryAttribute("o", &missionOrder) == tinyxml2::XML_SUCCESS && mission->IsMission()) {
mission->SetUniqueMissionOrderID(missionOrder); mission->SetUniqueMissionOrderID(missionOrder);
if (missionOrder > m_LastUsedMissionOrderUID) m_LastUsedMissionOrderUID = missionOrder; m_LastUsedMissionOrderUID = std::max(missionOrder, m_LastUsedMissionOrderUID);
} }
currentM = currentM->NextSiblingElement(); currentM = currentM->NextSiblingElement();
@@ -554,7 +398,7 @@ void MissionComponent::LoadFromXml(tinyxml2::XMLDocument* doc) {
void MissionComponent::UpdateXml(tinyxml2::XMLDocument* doc) { void MissionComponent::UpdateXml(tinyxml2::XMLDocument* doc) {
if (doc == nullptr) return; if (!doc) return;
auto shouldInsertMis = false; auto shouldInsertMis = false;
@@ -562,7 +406,7 @@ void MissionComponent::UpdateXml(tinyxml2::XMLDocument* doc) {
auto* mis = obj->FirstChildElement("mis"); auto* mis = obj->FirstChildElement("mis");
if (mis == nullptr) { if (!mis) {
mis = doc->NewElement("mis"); mis = doc->NewElement("mis");
shouldInsertMis = true; shouldInsertMis = true;
@@ -573,50 +417,33 @@ void MissionComponent::UpdateXml(tinyxml2::XMLDocument* doc) {
auto* done = doc->NewElement("done"); auto* done = doc->NewElement("done");
auto* cur = doc->NewElement("cur"); auto* cur = doc->NewElement("cur");
for (const auto& pair : m_Missions) { for (const auto& [missionId, mission] : m_Missions) {
auto* mission = pair.second; if (!mission) continue;
const auto complete = mission->IsComplete();
if (mission) { auto* missionElement = doc->NewElement("m");
const auto complete = mission->IsComplete();
auto* m = doc->NewElement("m"); if (!complete && mission->IsMission()) missionElement->SetAttribute("o", mission->GetUniqueMissionOrderID());
if (complete) { mission->UpdateXml(missionElement);
mission->UpdateXml(m);
done->LinkEndChild(m); cur->LinkEndChild(missionElement);
continue;
}
if (mission->IsMission()) m->SetAttribute("o", mission->GetUniqueMissionOrderID());
mission->UpdateXml(m);
cur->LinkEndChild(m);
}
} }
mis->InsertFirstChild(done); mis->InsertFirstChild(done);
mis->InsertEndChild(cur); mis->InsertEndChild(cur);
if (shouldInsertMis) { if (shouldInsertMis) obj->LinkEndChild(mis);
obj->LinkEndChild(mis);
}
} }
void MissionComponent::AddCollectible(int32_t collectibleID) { void MissionComponent::AddCollectible(const int32_t collectibleID) {
// Check if this collectible is already in the list if (!HasCollectible(collectibleID)) m_Collectibles.push_back(collectibleID);
if (HasCollectible(collectibleID)) {
return;
}
m_Collectibles.push_back(collectibleID);
} }
bool MissionComponent::HasCollectible(int32_t collectibleID) { bool MissionComponent::HasCollectible(const int32_t collectibleID) const {
return std::find(m_Collectibles.begin(), m_Collectibles.end(), collectibleID) != m_Collectibles.end(); return std::find(m_Collectibles.begin(), m_Collectibles.end(), collectibleID) != m_Collectibles.end();
} }
bool MissionComponent::HasMission(uint32_t missionId) { bool MissionComponent::HasMission(const uint32_t missionId) const {
return GetMission(missionId) != nullptr; return GetMission(missionId) != nullptr;
} }

View File

@@ -24,10 +24,10 @@ class AchievementCacheKey;
* The mission inventory of an entity. Tracks mission state for each mission that can be accepted and allows for * The mission inventory of an entity. Tracks mission state for each mission that can be accepted and allows for
* progression of each of the mission task types (see eMissionTaskType). * progression of each of the mission task types (see eMissionTaskType).
*/ */
class MissionComponent : public Component class MissionComponent final : public Component
{ {
public: public:
static const eReplicaComponentType ComponentType = eReplicaComponentType::MISSION; inline static const eReplicaComponentType ComponentType = eReplicaComponentType::MISSION;
explicit MissionComponent(Entity* parent); explicit MissionComponent(Entity* parent);
~MissionComponent() override; ~MissionComponent() override;
@@ -39,35 +39,35 @@ public:
* Returns all the missions for this entity, mapped by mission ID * Returns all the missions for this entity, mapped by mission ID
* @return the missions for this entity, mapped by mission ID * @return the missions for this entity, mapped by mission ID
*/ */
const std::unordered_map<uint32_t, Mission*>& GetMissions() const; const std::unordered_map<uint32_t, Mission*>& GetMissions() const { return m_Missions; };
/** /**
* Returns the mission for the given mission ID, if it exists * Returns the mission for the given mission ID, if it exists
* @param missionId the id of the mission to get * @param missionId the id of the mission to get
* @return the mission for the given mission ID * @return the mission for the given mission ID
*/ */
Mission* GetMission(uint32_t missionId) const; Mission* GetMission(const uint32_t missionId) const;
/** /**
* Returns the current state of the entities progression for the mission of the specified ID * Returns the current state of the entities progression for the mission of the specified ID
* @param missionId the ID of the mission to get the mission state for * @param missionId the ID of the mission to get the mission state for
* @return the mission state of the mission specified by the ID * @return the mission state of the mission specified by the ID
*/ */
eMissionState GetMissionState(uint32_t missionId) const; eMissionState GetMissionState(const uint32_t missionId) const;
/** /**
* Checks if the entity has all the requirements for accepting the mission specified by the ID. * Checks if the entity has all the requirements for accepting the mission specified by the ID.
* @param missionId the mission ID to check for if the character may accept it * @param missionId the mission ID to check for if the character may accept it
* @return whether this entity can accept the mission represented by the given mission ID * @return whether this entity can accept the mission represented by the given mission ID
*/ */
bool CanAccept(uint32_t missionId) const; bool CanAccept(const uint32_t missionId) const;
/** /**
* Accepts the mission specified by the ID, if the entity may accept it. Also stores it in the mission inventory. * Accepts the mission specified by the ID, if the entity may accept it. Also stores it in the mission inventory.
* @param missionId the ID of the mission to accept * @param missionId the ID of the mission to accept
* @param skipChecks skips the checks for the mission prerequisites * @param skipChecks skips the checks for the mission prerequisites
*/ */
void AcceptMission(uint32_t missionId, bool skipChecks = false); void AcceptMission(const uint32_t missionId, const bool skipChecks = false);
/** /**
* Completes the mission specified by the given ID, if the entity has fulfilled all progress requirements. * Completes the mission specified by the given ID, if the entity has fulfilled all progress requirements.
@@ -75,13 +75,13 @@ public:
* @param skipChecks skips the checks for having completed all of the mission tasks * @param skipChecks skips the checks for having completed all of the mission tasks
* @param yieldRewards whether to yield mission rewards, currently unused * @param yieldRewards whether to yield mission rewards, currently unused
*/ */
void CompleteMission(uint32_t missionId, bool skipChecks = false, bool yieldRewards = true); void CompleteMission(const uint32_t missionId, const bool skipChecks = false, const bool yieldRewards = true);
/** /**
* Removes the mission from the entities' mission chain. Not used for normal gameplay but useful for debugging. * Removes the mission from the entities' mission chain. Not used for normal gameplay but useful for debugging.
* @param missionId the ID of the mission to remove * @param missionId the ID of the mission to remove
*/ */
void RemoveMission(uint32_t missionId); void RemoveMission(const uint32_t missionId);
/** /**
* Attempts to progress mission tasks for a given type using parameters to progress. Note that this function is * Attempts to progress mission tasks for a given type using parameters to progress. Note that this function is
@@ -94,7 +94,7 @@ public:
* @param count the number to progress by, for example the number of items * @param count the number to progress by, for example the number of items
* @param ignoreAchievements do not progress achievements * @param ignoreAchievements do not progress achievements
*/ */
void Progress(eMissionTaskType type, int32_t value, LWOOBJID associate = 0, const std::string& targets = "", int32_t count = 1, bool ignoreAchievements = false); void Progress(const eMissionTaskType type, const int32_t value, const LWOOBJID& associate = 0, const std::string& targets = "", const int32_t count = 1, const bool ignoreAchievements = false);
/** /**
* Forces progression for a mission and task, ignoring checks * Forces progression for a mission and task, ignoring checks
@@ -103,7 +103,7 @@ public:
* @param value the value to progress with * @param value the value to progress with
* @param acceptMission accept the mission if it was not already accepted * @param acceptMission accept the mission if it was not already accepted
*/ */
void ForceProgress(uint32_t missionId, uint32_t taskId, int32_t value, bool acceptMission = true); void ForceProgress(const uint32_t missionId, const uint32_t taskId, const int32_t value, const bool acceptMission = true);
/** /**
* Forces progress for all tasks of a certain type that belong to the same mission * Forces progress for all tasks of a certain type that belong to the same mission
@@ -112,7 +112,7 @@ public:
* @param value the value to progress with * @param value the value to progress with
* @param acceptMission accept the mission if it wasn't already * @param acceptMission accept the mission if it wasn't already
*/ */
void ForceProgressTaskType(uint32_t missionId, uint32_t taskType, int32_t value, bool acceptMission = true); void ForceProgressTaskType(const uint32_t missionId, const uint32_t taskType, const int32_t value, const bool acceptMission = true);
/** /**
* Force progresses by checking the value and progressing by 1 * Force progresses by checking the value and progressing by 1
@@ -121,7 +121,7 @@ public:
* @param value the value to check the mission values before progressing * @param value the value to check the mission values before progressing
* @param acceptMission accept the mission if it wasn't already * @param acceptMission accept the mission if it wasn't already
*/ */
void ForceProgressValue(uint32_t missionId, uint32_t taskType, int32_t value, bool acceptMission = true); void ForceProgressValue(const uint32_t missionId, const uint32_t taskType, const int32_t value, const bool acceptMission = true);
/** /**
* Returns client database mission information for a mission * Returns client database mission information for a mission
@@ -129,7 +129,7 @@ public:
* @param result the result to store the information in * @param result the result to store the information in
* @return true if the information was succesfully retrieved, false otherwise * @return true if the information was succesfully retrieved, false otherwise
*/ */
bool GetMissionInfo(uint32_t missionId, CDMissions& result); bool GetMissionInfo(const uint32_t missionId, CDMissions& result) const;
/** /**
* Checks if there's any achievements we might be able to accept for the given parameters * Checks if there's any achievements we might be able to accept for the given parameters
@@ -141,34 +141,34 @@ public:
* @param count the number of values to progress by (differs by task type) * @param count the number of values to progress by (differs by task type)
* @return true if a achievement was accepted, false otherwise * @return true if a achievement was accepted, false otherwise
*/ */
bool LookForAchievements(eMissionTaskType type, int32_t value, bool progress = true, LWOOBJID associate = LWOOBJID_EMPTY, const std::string& targets = "", int32_t count = 1); bool LookForAchievements(const eMissionTaskType type, const int32_t value, const bool progress = true, const LWOOBJID& associate = LWOOBJID_EMPTY, const std::string& targets = "", const int32_t count = 1);
/** /**
* Checks if there's a mission active that requires the collection of the specified LOT * Checks if there's a mission active that requires the collection of the specified LOT
* @param lot the LOT to check for * @param lot the LOT to check for
* @return if there's a mission active that requires the collection of the specified LOT * @return if there's a mission active that requires the collection of the specified LOT
*/ */
bool RequiresItem(LOT lot); bool RequiresItem(const LOT lot);
/** /**
* Collects a collectable for the entity, unrendering it for the entity * Collects a collectable for the entity, unrendering it for the entity
* @param collectibleID the ID of the collectable to add * @param collectibleID the ID of the collectable to add
*/ */
void AddCollectible(int32_t collectibleID); void AddCollectible(const int32_t collectibleID);
/** /**
* Checks if the entity already has a collectible of the specified ID * Checks if the entity already has a collectible of the specified ID
* @param collectibleID the ID of the collectible to check * @param collectibleID the ID of the collectible to check
* @return if the entity already has a collectible of the specified ID * @return if the entity already has a collectible of the specified ID
*/ */
bool HasCollectible(int32_t collectibleID); bool HasCollectible(const int32_t collectibleID) const;
/** /**
* Checks if the entity has a certain mission in its inventory * Checks if the entity has a certain mission in its inventory
* @param missionId the ID of the mission to check * @param missionId the ID of the mission to check
* @return if the entity has a certain mission in its inventory * @return if the entity has a certain mission in its inventory
*/ */
bool HasMission(uint32_t missionId); bool HasMission(const uint32_t missionId) const;
private: private:
/** /**
@@ -189,7 +189,7 @@ private:
* @param targets optional targets to progress with * @param targets optional targets to progress with
* @return list of mission IDs (achievements) that can be progressed for the given parameters * @return list of mission IDs (achievements) that can be progressed for the given parameters
*/ */
static const std::vector<uint32_t>& QueryAchievements(eMissionTaskType type, int32_t value, const std::string targets); static const std::vector<uint32_t>& QueryAchievements(const eMissionTaskType type, const int32_t value, const std::string& targets);
/** /**
* As achievements can be hard to query, we here store a list of all the mission IDs that can be unlocked for a * As achievements can be hard to query, we here store a list of all the mission IDs that can be unlocked for a

View File

@@ -1,6 +1,6 @@
/* /*
* Darkflame Universe * Darkflame Universe
* Copyright 2019 * Copyright 2023
*/ */
#include <sstream> #include <sstream>
@@ -18,68 +18,33 @@
#include "CDComponentsRegistryTable.h" #include "CDComponentsRegistryTable.h"
OfferedMission::OfferedMission(const uint32_t missionId, const bool offersMission, const bool acceptsMission) { MissionOfferComponent::MissionOfferComponent(Entity* parent, const int32_t componentId) : Component(parent) {
this->missionId = missionId; m_ComponentId = componentId;
this->offersMission = offersMission;
this->acceptsMission = acceptsMission;
} }
void MissionOfferComponent::LoadTemplateData() {
if (m_ComponentId == -1) return;
// Now lookup the missions in the MissionNPCComponent table
auto* missionNpcComponentTable = CDClientManager::Instance().GetTable<CDMissionNPCComponentTable>();
uint32_t OfferedMission::GetMissionId() const { auto missions = missionNpcComponentTable->Query([=](const CDMissionNPCComponent& entry) {
return this->missionId; return entry.id == static_cast<int32_t>(m_ComponentId);
} });
bool OfferedMission::GetOfferMission() const { for (const auto& mission : missions) {
return this->offersMission; this->offeredMissions.emplace_back(
} std::make_unique<OfferedMission>(mission.missionID, mission.offersMission, mission.acceptsMission)
);
bool OfferedMission::GetAcceptMission() const {
return this->acceptsMission;
}
//------------------------ MissionOfferComponent below ------------------------
MissionOfferComponent::MissionOfferComponent(Entity* parent, const LOT parentLot) : Component(parent) {
auto* compRegistryTable = CDClientManager::Instance().GetTable<CDComponentsRegistryTable>();
auto value = compRegistryTable->GetByIDAndType(parentLot, eReplicaComponentType::MISSION_OFFER, -1);
if (value != -1) {
const uint32_t componentId = value;
// Now lookup the missions in the MissionNPCComponent table
auto* missionNpcComponentTable = CDClientManager::Instance().GetTable<CDMissionNPCComponentTable>();
auto missions = missionNpcComponentTable->Query([=](const CDMissionNPCComponent& entry) {
return entry.id == static_cast<unsigned>(componentId);
});
for (auto& mission : missions) {
auto* offeredMission = new OfferedMission(mission.missionID, mission.offersMission, mission.acceptsMission);
this->offeredMissions.push_back(offeredMission);
}
} }
} }
MissionOfferComponent::~MissionOfferComponent() {
for (auto* mission : this->offeredMissions) {
if (mission) {
delete mission;
mission = nullptr;
}
}
offeredMissions.clear();
}
void MissionOfferComponent::OnUse(Entity* originator) { void MissionOfferComponent::OnUse(Entity* originator) {
OfferMissions(originator); OfferMissions(originator);
} }
void MissionOfferComponent::OfferMissions(Entity* entity, const uint32_t specifiedMissionId) { void MissionOfferComponent::OfferMissions(Entity* entity, const uint32_t specifiedMissionId) {
// First, get the entity's MissionComponent. If there is not one, then we cannot offer missions to this entity. // First, get the entity's MissionComponent. If there is not one, then we cannot offer missions to this entity.
auto* missionComponent = static_cast<MissionComponent*>(entity->GetComponent(eReplicaComponentType::MISSION)); auto* missionComponent = entity->GetComponent<MissionComponent>();
if (!missionComponent) { if (!missionComponent) {
Game::logger->Log("MissionOfferComponent", "Unable to get mission component for Entity %llu", entity->GetObjectID()); Game::logger->Log("MissionOfferComponent", "Unable to get mission component for Entity %llu", entity->GetObjectID());
@@ -88,17 +53,15 @@ void MissionOfferComponent::OfferMissions(Entity* entity, const uint32_t specifi
std::vector<uint32_t> offered{}; std::vector<uint32_t> offered{};
CDMissions info{}; CDMissions missionInfo{};
if (specifiedMissionId > 0 && !Mission::IsValidMission(specifiedMissionId, info)) { if (specifiedMissionId > 0 && !Mission::IsValidMission(specifiedMissionId, missionInfo)) {
return; return;
} }
for (auto* offeredMission : this->offeredMissions) { for (const auto& offeredMission : offeredMissions) {
if (specifiedMissionId > 0) { if (specifiedMissionId > 0 && (offeredMission->GetMissionId() != specifiedMissionId && !missionInfo.isRandom)) {
if (offeredMission->GetMissionId() != specifiedMissionId && !info.isRandom) { continue;
continue;
}
} }
// First, check if we already have the mission // First, check if we already have the mission
@@ -106,17 +69,20 @@ void MissionOfferComponent::OfferMissions(Entity* entity, const uint32_t specifi
auto* mission = missionComponent->GetMission(missionId); auto* mission = missionComponent->GetMission(missionId);
if (mission != nullptr) { if (mission) {
if (specifiedMissionId <= 0) { if (specifiedMissionId <= 0) {
// Handles the odd case where the offer object should not display the mission again // Handles the odd case where the offer object should not display the mission again
if (!mission->IsComplete() && mission->GetClientInfo().offer_objectID == m_Parent->GetLOT() && mission->GetClientInfo().target_objectID != m_Parent->GetLOT() && mission->IsFetchMission()) { if (!mission->IsComplete() &&
mission->GetClientInfo().offer_objectID == m_ParentEntity->GetLOT() &&
mission->GetClientInfo().target_objectID != m_ParentEntity->GetLOT() &&
mission->IsFetchMission()) {
continue; continue;
} }
} }
// We have the mission, if it is not complete, offer it // We have the mission, if it is not complete, offer it
if (mission->IsActive() || mission->IsReadyToComplete()) { if (mission->IsActive() || mission->IsReadyToComplete()) {
GameMessages::SendOfferMission(entity->GetObjectID(), entity->GetSystemAddress(), missionId, m_Parent->GetObjectID()); GameMessages::SendOfferMission(entity->GetObjectID(), entity->GetSystemAddress(), missionId, m_ParentEntity->GetObjectID());
offered.push_back(missionId); offered.push_back(missionId);
@@ -127,50 +93,30 @@ void MissionOfferComponent::OfferMissions(Entity* entity, const uint32_t specifi
const auto canAccept = MissionPrerequisites::CanAccept(missionId, missionComponent->GetMissions()); const auto canAccept = MissionPrerequisites::CanAccept(missionId, missionComponent->GetMissions());
// Mission has not yet been accepted - check the prereqs // Mission has not yet been accepted - check the prereqs
if (!canAccept) if (!canAccept || !Mission::IsValidMission(missionId, missionInfo)) continue;
continue;
if (!Mission::IsValidMission(missionId, info)) { // This means the mission is part of a random pool of missions.
continue; if (missionInfo.isRandom && missionInfo.randomPool.empty()) continue;
}
const auto& randomPool = info.randomPool; if (missionInfo.isRandom && !missionInfo.randomPool.empty()) {
const auto isRandom = info.isRandom; auto randomMissionPoolStr = GeneralUtils::SplitString(missionInfo.randomPool, ',');
if (isRandom && randomPool.empty()) // This means the mission is part of a random pool of missions. std::vector<uint32_t> randomMissions;
{ for (const auto& randomMissionStr : randomMissionPoolStr) {
continue; uint32_t randomMission;
} if (GeneralUtils::TryParse(randomMissionStr, randomMission)) randomMissions.push_back(randomMission);
if (isRandom && !randomPool.empty()) {
std::istringstream stream(randomPool);
std::string token;
std::vector<uint32_t> randomMissionPool;
while (std::getline(stream, token, ',')) {
try {
const auto value = std::stoul(token);
randomMissionPool.push_back(value);
} catch (std::invalid_argument& exception) {
Game::logger->Log("MissionOfferComponent", "Failed to parse value (%s): (%s)!", token.c_str(), exception.what());
}
} }
if (specifiedMissionId > 0) { if (specifiedMissionId > 0) {
const auto& iter = std::find(randomMissionPool.begin(), randomMissionPool.end(), specifiedMissionId); if (std::find(randomMissions.begin(), randomMissions.end(), specifiedMissionId) != randomMissions.end() &&
MissionPrerequisites::CanAccept(specifiedMissionId, missionComponent->GetMissions())) {
if (iter != randomMissionPool.end() && MissionPrerequisites::CanAccept(specifiedMissionId, missionComponent->GetMissions())) { GameMessages::SendOfferMission(entity->GetObjectID(), entity->GetSystemAddress(), specifiedMissionId, m_ParentEntity->GetObjectID());
GameMessages::SendOfferMission(entity->GetObjectID(), entity->GetSystemAddress(), specifiedMissionId, m_Parent->GetObjectID());
return; return;
} }
} }
std::vector<uint32_t> canAcceptPool; std::vector<uint32_t> canAcceptPool;
for (const auto& sample : randomMissions) {
for (const auto sample : randomMissionPool) {
const auto state = missionComponent->GetMissionState(sample); const auto state = missionComponent->GetMissionState(sample);
if (state == eMissionState::ACTIVE || if (state == eMissionState::ACTIVE ||
@@ -180,31 +126,29 @@ void MissionOfferComponent::OfferMissions(Entity* entity, const uint32_t specifi
sample == specifiedMissionId) { sample == specifiedMissionId) {
mission = missionComponent->GetMission(sample); mission = missionComponent->GetMission(sample);
if (mission == nullptr || mission->IsAchievement()) { if (!mission || mission->IsAchievement()) continue;
continue;
}
GameMessages::SendOfferMission(entity->GetObjectID(), entity->GetSystemAddress(), sample, m_Parent->GetObjectID()); GameMessages::SendOfferMission(entity->GetObjectID(), entity->GetSystemAddress(), sample, m_ParentEntity->GetObjectID());
canAcceptPool.clear(); canAcceptPool.clear();
break; break;
} }
if (std::find(offered.begin(), offered.end(), sample) == offered.end() && MissionPrerequisites::CanAccept(sample, missionComponent->GetMissions())) { if (std::find(offered.begin(), offered.end(), sample) == offered.end() &&
MissionPrerequisites::CanAccept(sample, missionComponent->GetMissions())) {
canAcceptPool.push_back(sample); canAcceptPool.push_back(sample);
} }
} }
// If the mission is already active or we already completed one of them today // If the mission is already active or we already completed one of them today
if (canAcceptPool.empty()) if (canAcceptPool.empty()) continue;
continue;
const auto selected = canAcceptPool[GeneralUtils::GenerateRandomNumber<int>(0, canAcceptPool.size() - 1)]; const auto selected = canAcceptPool[GeneralUtils::GenerateRandomNumber<int32_t>(0, canAcceptPool.size() - 1)];
GameMessages::SendOfferMission(entity->GetObjectID(), entity->GetSystemAddress(), selected, m_Parent->GetObjectID()); GameMessages::SendOfferMission(entity->GetObjectID(), entity->GetSystemAddress(), selected, m_ParentEntity->GetObjectID());
} else if (std::find(offered.begin(), offered.end(), missionId) == offered.end() && offeredMission->GetOfferMission()) { } else if (std::find(offered.begin(), offered.end(), missionId) == offered.end() && offeredMission->GetOfferMission()) {
GameMessages::SendOfferMission(entity->GetObjectID(), entity->GetSystemAddress(), missionId, m_Parent->GetObjectID()); GameMessages::SendOfferMission(entity->GetObjectID(), entity->GetSystemAddress(), missionId, m_ParentEntity->GetObjectID());
} }
} }
} }

View File

@@ -1,15 +1,18 @@
/* /*
* Darkflame Universe * Darkflame Universe
* Copyright 2019 * Copyright 2023
*/ */
#ifndef MISSIONOFFERCOMPONENT_H #ifndef __MISSIONOFFERCOMPONENT_H__
#define MISSIONOFFERCOMPONENT_H #define __MISSIONOFFERCOMPONENT_H__
#pragma once
#include <memory>
#include <vector>
#include <stdint.h>
#include "dCommonVars.h" #include "dCommonVars.h"
#include "Component.h" #include "Component.h"
#include <vector>
#include <stdint.h>
#include "eReplicaComponentType.h" #include "eReplicaComponentType.h"
class Entity; class Entity;
@@ -18,25 +21,29 @@ class Entity;
* Light wrapper around missions that may be offered by an entity * Light wrapper around missions that may be offered by an entity
*/ */
struct OfferedMission { struct OfferedMission {
OfferedMission(uint32_t missionId, bool offersMission, bool acceptsMission); OfferedMission(const uint32_t missionId, const bool offersMission, const bool acceptsMission) {
this->missionId = missionId;
this->offersMission = offersMission;
this->acceptsMission = acceptsMission;
};
/** /**
* Returns the ID of the mission * Returns the ID of the mission
* @return the ID of the mission * @return the ID of the mission
*/ */
uint32_t GetMissionId() const; uint32_t GetMissionId() const { return missionId; };
/** /**
* Returns if this mission is offered by the entity * Returns if this mission is offered by the entity
* @return true if this mission is offered by the entity, false otherwise * @return true if this mission is offered by the entity, false otherwise
*/ */
bool GetOfferMission() const; bool GetOfferMission() const { return offersMission; };
/** /**
* Returns if this mission may be accepted by the entity (currently unused) * Returns if this mission may be accepted by the entity (currently unused)
* @return true if this mission may be accepted by the entity, false otherwise * @return true if this mission may be accepted by the entity, false otherwise
*/ */
bool GetAcceptMission() const; bool GetAcceptMission() const { return acceptsMission; };
private: private:
@@ -59,12 +66,13 @@ private:
/** /**
* Allows entities to offer missions to other entities, depending on their mission inventory progression. * Allows entities to offer missions to other entities, depending on their mission inventory progression.
*/ */
class MissionOfferComponent : public Component { class MissionOfferComponent final : public Component {
public: public:
static const eReplicaComponentType ComponentType = eReplicaComponentType::MISSION_OFFER; inline static const eReplicaComponentType ComponentType = eReplicaComponentType::MISSION_OFFER;
MissionOfferComponent(Entity* parent, LOT parentLot); MissionOfferComponent(Entity* parent, const int32_t componentId = -1);
~MissionOfferComponent() override;
void LoadTemplateData() override;
/** /**
* Handles the OnUse event triggered by some entity, determines which missions to show based on what they may * Handles the OnUse event triggered by some entity, determines which missions to show based on what they may
@@ -85,7 +93,9 @@ private:
/** /**
* The missions this entity has to offer * The missions this entity has to offer
*/ */
std::vector<OfferedMission*> offeredMissions; std::vector<std::unique_ptr<OfferedMission>> offeredMissions;
int32_t m_ComponentId;
}; };
#endif // MISSIONOFFERCOMPONENT_H #endif // __MISSIONOFFERCOMPONENT_H__

View File

@@ -0,0 +1,23 @@
#include "ModelBehaviorComponent.h"
#include "Entity.h"
#include "ePhysicsBehaviorType.h"
ModelBehaviorComponent::ModelBehaviorComponent(Entity* parent) : Component(parent) {
m_DirtyModelInfo = true;
m_IsPickable = false;
m_PhysicsType = ePhysicsBehaviorType::STANDARD;
m_OriginalPosition = m_ParentEntity->GetDefaultPosition();
m_OriginalRotation = m_ParentEntity->GetDefaultRotation();
}
void ModelBehaviorComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags) {
outBitStream->Write(m_DirtyModelInfo || bIsInitialUpdate);
if (m_DirtyModelInfo || bIsInitialUpdate) {
outBitStream->Write(m_IsPickable);
outBitStream->Write(m_PhysicsType);
outBitStream->Write(m_OriginalPosition);
outBitStream->Write(m_OriginalRotation);
if (!bIsInitialUpdate) m_DirtyModelInfo = false;
}
}

View File

@@ -7,15 +7,16 @@
#include "eReplicaComponentType.h" #include "eReplicaComponentType.h"
class Entity; class Entity;
enum class ePhysicsBehaviorType : int32_t;
/** /**
* Component that represents entities that are a model, e.g. collectible models and BBB models. * Component that represents entities that are a model, e.g. collectible models and BBB models.
*/ */
class ModelComponent : public Component { class ModelBehaviorComponent final : public Component {
public: public:
static const eReplicaComponentType ComponentType = eReplicaComponentType::MODEL; inline static const eReplicaComponentType ComponentType = eReplicaComponentType::MODEL_BEHAVIOR;
ModelComponent(Entity* parent); ModelBehaviorComponent(Entity* parent);
void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags); void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags);
@@ -29,7 +30,11 @@ public:
* Sets the original position of the model * Sets the original position of the model
* @param pos the original position to set * @param pos the original position to set
*/ */
void SetPosition(const NiPoint3& pos) { m_OriginalPosition = pos; } void SetPosition(const NiPoint3& pos) {
if (m_OriginalPosition == pos) return;
m_OriginalPosition = pos;
m_DirtyModelInfo = true;
}
/** /**
* Returns the original rotation of the model * Returns the original rotation of the model
@@ -41,10 +46,29 @@ public:
* Sets the original rotation of the model * Sets the original rotation of the model
* @param rot the original rotation to set * @param rot the original rotation to set
*/ */
void SetRotation(const NiQuaternion& rot) { m_OriginalRotation = rot; } void SetRotation(const NiQuaternion& rot) {
if (m_OriginalRotation == rot) return;
m_OriginalRotation = rot;
m_DirtyModelInfo = true;
}
private: private:
/**
* if the model info has changed
*/
bool m_DirtyModelInfo;
/**
* If the model is pickable
*/
bool m_IsPickable;
/**
* the phsyics type of the model
*/
ePhysicsBehaviorType m_PhysicsType;
/** /**
* The original position of the model * The original position of the model
*/ */
@@ -55,8 +79,4 @@ private:
*/ */
NiQuaternion m_OriginalRotation; NiQuaternion m_OriginalRotation;
/**
* The ID of the user that made the model
*/
LWOOBJID m_userModelID;
}; };

View File

@@ -1,31 +0,0 @@
#include "ModelComponent.h"
#include "Entity.h"
ModelComponent::ModelComponent(Entity* parent) : Component(parent) {
m_OriginalPosition = m_Parent->GetDefaultPosition();
m_OriginalRotation = m_Parent->GetDefaultRotation();
m_userModelID = m_Parent->GetVarAs<LWOOBJID>(u"userModelID");
}
void ModelComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags) {
// ItemComponent Serialization. Pets do not get this serialization.
if (!m_Parent->HasComponent(eReplicaComponentType::PET)) {
outBitStream->Write1();
outBitStream->Write<LWOOBJID>(m_userModelID != LWOOBJID_EMPTY ? m_userModelID : m_Parent->GetObjectID());
outBitStream->Write<int>(0);
outBitStream->Write0();
}
//actual model component:
outBitStream->Write1(); // Yes we are writing model info
outBitStream->Write0(); // Is pickable
outBitStream->Write<uint32_t>(2); // Physics type
outBitStream->Write(m_OriginalPosition); // Original position
outBitStream->Write(m_OriginalRotation); // Original rotation
outBitStream->Write1(); // We are writing behavior info
outBitStream->Write<uint32_t>(0); // Number of behaviors
outBitStream->Write1(); // Is this model paused
if (bIsInitialUpdate) outBitStream->Write0(); // We are not writing model editing info
}

View File

@@ -1,70 +1,31 @@
#include "ModuleAssemblyComponent.h" #include "ModuleAssemblyComponent.h"
#include "Entity.h"
ModuleAssemblyComponent::ModuleAssemblyComponent(Entity* parent) : Component(parent) { ModuleAssemblyComponent::ModuleAssemblyComponent(Entity* parent) : Component(parent) {
m_SubKey = LWOOBJID_EMPTY; m_SubKey = LWOOBJID_EMPTY;
m_UseOptionalParts = false; m_UseOptionalParts = false;
m_AssemblyPartsLOTs = u"";
}
ModuleAssemblyComponent::~ModuleAssemblyComponent() {
}
void ModuleAssemblyComponent::SetSubKey(LWOOBJID value) {
m_SubKey = value;
}
LWOOBJID ModuleAssemblyComponent::GetSubKey() const {
return m_SubKey;
}
void ModuleAssemblyComponent::SetUseOptionalParts(bool value) {
m_UseOptionalParts = value;
}
bool ModuleAssemblyComponent::GetUseOptionalParts() const {
return m_UseOptionalParts;
} }
void ModuleAssemblyComponent::SetAssemblyPartsLOTs(const std::u16string& value) { void ModuleAssemblyComponent::SetAssemblyPartsLOTs(const std::u16string& value) {
std::u16string val{}; m_AssemblyPartsLOTs = value;
std::replace(m_AssemblyPartsLOTs.begin(), m_AssemblyPartsLOTs.end(), u'+', u';');
val.reserve(value.size() + 1); // doesn't matter if we push back a ; or a +. The client splits on either of them.
// For congruency however, maintain one or the other.
for (auto character : value) { m_AssemblyPartsLOTs.push_back(u';');
if (character == '+') character = ';';
val.push_back(character);
}
val.push_back(';');
m_AssemblyPartsLOTs = val;
}
const std::u16string& ModuleAssemblyComponent::GetAssemblyPartsLOTs() const {
return m_AssemblyPartsLOTs;
} }
void ModuleAssemblyComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags) { void ModuleAssemblyComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags) {
if (bIsInitialUpdate) { if (!bIsInitialUpdate) return;
outBitStream->Write1(); outBitStream->Write(bIsInitialUpdate);
outBitStream->Write(m_SubKey != LWOOBJID_EMPTY); outBitStream->Write(m_SubKey != LWOOBJID_EMPTY);
if (m_SubKey != LWOOBJID_EMPTY) { if (m_SubKey != LWOOBJID_EMPTY) outBitStream->Write(m_SubKey);
outBitStream->Write(m_SubKey);
}
outBitStream->Write(m_UseOptionalParts); outBitStream->Write(m_UseOptionalParts);
outBitStream->Write(static_cast<uint16_t>(m_AssemblyPartsLOTs.size())); outBitStream->Write<uint16_t>(m_AssemblyPartsLOTs.size());
for (char16_t character : m_AssemblyPartsLOTs) { for (const char16_t character : m_AssemblyPartsLOTs) {
outBitStream->Write(character); outBitStream->Write(character);
}
} }
} }
void ModuleAssemblyComponent::Update(float deltaTime) {
}

View File

@@ -1,49 +1,51 @@
#ifndef __MODULEASSEMBLYCOMPONENT__H__
#define __MODULEASSEMBLYCOMPONENT__H__
#pragma once #pragma once
#include "BitStream.h"
#include "Entity.h"
#include "Component.h" #include "Component.h"
#include "eReplicaComponentType.h" #include "eReplicaComponentType.h"
namespace RakNet {
class BitStream;
};
/** /**
* Component that belongs to an object that may be modularly built, like cars and rockets. Note that this is not the * Component that belongs to an object that may be modularly built, like cars and rockets. Note that this is not the
* same as having said items in your inventory (the subkey for this component) this component is the one that * same as having said items in your inventory (the subkey for this component) this component is the one that
* renders the entity into the world. * renders the entity into the world.
*/ */
class ModuleAssemblyComponent : public Component { class ModuleAssemblyComponent final : public Component {
public: public:
static const eReplicaComponentType ComponentType = eReplicaComponentType::MODULE_ASSEMBLY; inline static const eReplicaComponentType ComponentType = eReplicaComponentType::MODULE_ASSEMBLY;
ModuleAssemblyComponent(Entity* parent); ModuleAssemblyComponent(Entity* parent);
~ModuleAssemblyComponent() override;
void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags); void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags);
void Update(float deltaTime) override;
/** /**
* Sets the subkey of this entity * Sets the subkey of this entity
* @param value the subkey to set * @param value the subkey to set
*/ */
void SetSubKey(LWOOBJID value); void SetSubKey(const LWOOBJID& value) { m_SubKey = value; };
/** /**
* Returns the subkey for this entity * Returns the subkey for this entity
* @return the subkey for this entity * @return the subkey for this entity
*/ */
LWOOBJID GetSubKey() const; LWOOBJID GetSubKey() const { return m_SubKey; };
/** /**
* Sets the optional parts value * Sets the optional parts value
* @param value the value to set * @param value the value to set
*/ */
void SetUseOptionalParts(bool value); void SetUseOptionalParts(bool value) { m_UseOptionalParts = value; };
/** /**
* Returns the optional parts value * Returns the optional parts value
* @return the value to set * @return the value to set
*/ */
bool GetUseOptionalParts() const; bool GetUseOptionalParts() const { return m_UseOptionalParts; };
/** /**
* Sets the assembly part lots (the subsections of this modular build) * Sets the assembly part lots (the subsections of this modular build)
@@ -55,7 +57,7 @@ public:
* Returns the assembly part lots (the subsections of this modular build) * Returns the assembly part lots (the subsections of this modular build)
* @return * @return
*/ */
const std::u16string& GetAssemblyPartsLOTs() const; const std::u16string& GetAssemblyPartsLOTs() const { return m_AssemblyPartsLOTs; };
private: private:
@@ -75,3 +77,5 @@ private:
*/ */
std::u16string m_AssemblyPartsLOTs; std::u16string m_AssemblyPartsLOTs;
}; };
#endif //!__MODULEASSEMBLYCOMPONENT__H__

View File

@@ -10,31 +10,28 @@
#include "EntityManager.h" #include "EntityManager.h"
#include "SimplePhysicsComponent.h" #include "SimplePhysicsComponent.h"
#include "CDClientManager.h" #include "CDClientManager.h"
#include "Game.h"
#include "dLogger.h"
#include "SimplePhysicsComponent.h"
#include "ControllablePhysicsComponent.h"
#include "CDComponentsRegistryTable.h" #include "CDComponentsRegistryTable.h"
#include "CDPhysicsComponentTable.h" #include "CDPhysicsComponentTable.h"
#include "CDMovementAIComponentTable.h"
#include "Entity.h"
#include "BaseCombatAIComponent.h"
std::map<LOT, float> MovementAIComponent::m_PhysicsSpeedCache = {}; std::map<LOT, float> MovementAIComponent::m_PhysicsSpeedCache = {};
MovementAIComponent::MovementAIComponent(Entity* parent, MovementAIInfo info) : Component(parent) { MovementAIComponent::MovementAIComponent(Entity* parent, int32_t componentId) : Component(parent) {
m_Info = std::move(info); m_ComponentId = componentId;
m_Done = true; m_Done = true;
m_BaseCombatAI = nullptr; m_BaseCombatAI = nullptr;
m_BaseCombatAI = reinterpret_cast<BaseCombatAIComponent*>(m_Parent->GetComponent(eReplicaComponentType::BASE_COMBAT_AI));
//Try and fix the insane values:
if (m_Info.wanderRadius > 5.0f) m_Info.wanderRadius = m_Info.wanderRadius * 0.5f;
if (m_Info.wanderRadius > 8.0f) m_Info.wanderRadius = 8.0f;
if (m_Info.wanderSpeed > 0.5f) m_Info.wanderSpeed = m_Info.wanderSpeed * 0.5f;
m_BaseSpeed = GetBaseSpeed(m_Parent->GetLOT());
m_NextWaypoint = GetCurrentPosition();
m_Acceleration = 0.4f; m_Acceleration = 0.4f;
m_Interrupted = false; m_Interrupted = false;
m_PullPoint = {}; m_PullPoint = NiPoint3::ZERO;
m_HaltDistance = 0; m_HaltDistance = 0;
m_Timer = 0; m_Timer = 0;
m_CurrentSpeed = 0; m_CurrentSpeed = 0;
@@ -43,7 +40,47 @@ MovementAIComponent::MovementAIComponent(Entity* parent, MovementAIInfo info) :
m_LockRotation = false; m_LockRotation = false;
} }
MovementAIComponent::~MovementAIComponent() = default; void MovementAIComponent::Startup() {
m_BaseCombatAI = m_ParentEntity->GetComponent<BaseCombatAIComponent>();
m_NextWaypoint = GetCurrentPosition();
}
void MovementAIComponent::LoadConfigData() {
bool useWanderDB = m_ParentEntity->GetVar<bool>(u"usewanderdb");
if (useWanderDB) return;
const auto wanderOverride = m_ParentEntity->GetVarAs<float>(u"wanderRadius");
if (wanderOverride != 0.0f) m_Info.wanderRadius = wanderOverride;
}
void MovementAIComponent::LoadTemplateData() {
m_BaseSpeed = GetBaseSpeed(m_ParentEntity->GetLOT());
if (m_ComponentId == -1) return;
auto* movementAiComponentTable = CDClientManager::Instance().GetTable<CDMovementAIComponentTable>();
auto movementEntries = movementAiComponentTable->Query([this](CDMovementAIComponent entry) {return (entry.id == this->m_ComponentId); });
if (movementEntries.empty()) return;
auto movementEntry = movementEntries.at(0);
MovementAIInfo moveInfo{};
moveInfo.movementType = movementEntry.MovementType;
moveInfo.wanderChance = movementEntry.WanderChance;
moveInfo.wanderRadius = movementEntry.WanderRadius;
moveInfo.wanderSpeed = movementEntry.WanderSpeed;
moveInfo.wanderDelayMax = movementEntry.WanderDelayMax;
moveInfo.wanderDelayMin = movementEntry.WanderDelayMin;
this->SetMoveInfo(moveInfo);
}
void MovementAIComponent::SetMoveInfo(const MovementAIInfo& info) {
m_Info = info;
//Try and fix the insane values:
if (m_Info.wanderRadius > 5.0f) m_Info.wanderRadius *= 0.5f;
if (m_Info.wanderRadius > 8.0f) m_Info.wanderRadius = 8.0f;
if (m_Info.wanderSpeed > 0.5f) m_Info.wanderSpeed *= 0.5f;
}
void MovementAIComponent::Update(const float deltaTime) { void MovementAIComponent::Update(const float deltaTime) {
if (m_Interrupted) { if (m_Interrupted) {
@@ -51,11 +88,11 @@ void MovementAIComponent::Update(const float deltaTime) {
const auto speed = deltaTime * 2.5f; const auto speed = deltaTime * 2.5f;
NiPoint3 velocity; NiPoint3 velocity(
(m_PullPoint.x - source.x) * speed,
velocity.x = (m_PullPoint.x - source.x) * speed; (m_PullPoint.y - source.y) * speed,
velocity.y = (m_PullPoint.y - source.y) * speed; (m_PullPoint.z - source.z) * speed
velocity.z = (m_PullPoint.z - source.z) * speed; );
SetPosition(source + velocity); SetPosition(source + velocity);
@@ -66,28 +103,24 @@ void MovementAIComponent::Update(const float deltaTime) {
return; return;
} }
if (AtFinalWaypoint()) // Are we done? // Are we done?
{ if (AtFinalWaypoint()) return;
return;
}
if (m_HaltDistance > 0) { if (m_HaltDistance > 0) {
if (Vector3::DistanceSquared(ApproximateLocation(), GetDestination()) < m_HaltDistance * m_HaltDistance) // Prevent us from hugging the target // Prevent us from hugging the target
{ if (Vector3::DistanceSquared(ApproximateLocation(), GetDestination()) < m_HaltDistance * m_HaltDistance) {
Stop(); Stop();
return; return;
} }
} }
if (m_Timer > 0) { if (m_Timer > 0.0f) {
m_Timer -= deltaTime; m_Timer -= deltaTime;
if (m_Timer > 0) { if (m_Timer > 0.0f) return;
return;
}
m_Timer = 0; m_Timer = 0.0f;
} }
const auto source = GetCurrentWaypoint(); const auto source = GetCurrentWaypoint();
@@ -149,11 +182,7 @@ nextAction:
SetVelocity(velocity); SetVelocity(velocity);
EntityManager::Instance()->SerializeEntity(m_Parent); EntityManager::Instance()->SerializeEntity(m_ParentEntity);
}
const MovementAIInfo& MovementAIComponent::GetInfo() const {
return m_Info;
} }
bool MovementAIComponent::AdvanceWaypointIndex() { bool MovementAIComponent::AdvanceWaypointIndex() {
@@ -167,37 +196,23 @@ bool MovementAIComponent::AdvanceWaypointIndex() {
} }
NiPoint3 MovementAIComponent::GetCurrentWaypoint() const { NiPoint3 MovementAIComponent::GetCurrentWaypoint() const {
if (m_PathIndex >= m_CurrentPath.size()) { return m_PathIndex >= m_CurrentPath.size() ? GetCurrentPosition() : m_CurrentPath[m_PathIndex];
return GetCurrentPosition();
}
return m_CurrentPath[m_PathIndex];
}
NiPoint3 MovementAIComponent::GetNextWaypoint() const {
return m_NextWaypoint;
}
NiPoint3 MovementAIComponent::GetCurrentPosition() const {
return m_Parent->GetPosition();
} }
NiPoint3 MovementAIComponent::ApproximateLocation() const { NiPoint3 MovementAIComponent::ApproximateLocation() const {
auto source = GetCurrentPosition(); auto source = GetCurrentPosition();
if (m_Done) { if (m_Done) return source;
return source;
}
auto destination = m_NextWaypoint; auto destination = m_NextWaypoint;
auto factor = m_TotalTime > 0 ? (m_TotalTime - m_Timer) / m_TotalTime : 0; auto factor = m_TotalTime > 0.0f ? (m_TotalTime - m_Timer) / m_TotalTime : 0.0f;
auto x = source.x + factor * (destination.x - source.x); NiPoint3 approximation = NiPoint3(
auto y = source.y + factor * (destination.y - source.y); source.x + factor * (destination.x - source.x),
auto z = source.z + factor * (destination.z - source.z); source.y + factor * (destination.y - source.y),
source.z + factor * (destination.z - source.z)
NiPoint3 approximation = NiPoint3(x, y, z); );
if (dpWorld::Instance().IsLoaded()) { if (dpWorld::Instance().IsLoaded()) {
approximation.y = dpWorld::Instance().GetNavMesh()->GetHeightAtPoint(approximation); approximation.y = dpWorld::Instance().GetNavMesh()->GetHeightAtPoint(approximation);
@@ -221,39 +236,30 @@ bool MovementAIComponent::Warp(const NiPoint3& point) {
SetPosition(destination); SetPosition(destination);
EntityManager::Instance()->SerializeEntity(m_Parent); EntityManager::Instance()->SerializeEntity(m_ParentEntity);
return true; return true;
} }
float MovementAIComponent::GetTimer() const {
return m_Timer;
}
bool MovementAIComponent::AtFinalWaypoint() const {
return m_Done;
}
void MovementAIComponent::Stop() { void MovementAIComponent::Stop() {
if (m_Done) { if (m_Done) return;
return;
}
SetPosition(ApproximateLocation()); SetPosition(ApproximateLocation());
SetVelocity(NiPoint3::ZERO); SetVelocity(NiPoint3::ZERO);
m_TotalTime = m_Timer = 0; m_TotalTime = 0.0f;
m_Timer = 0.0f;
m_Done = true; m_Done = true;
m_CurrentPath = {}; m_CurrentPath.clear();
m_PathIndex = 0; m_PathIndex = 0;
m_CurrentSpeed = 0; m_CurrentSpeed = 0.0f;
EntityManager::Instance()->SerializeEntity(m_Parent); EntityManager::Instance()->SerializeEntity(m_ParentEntity);
} }
void MovementAIComponent::PullToPoint(const NiPoint3& point) { void MovementAIComponent::PullToPoint(const NiPoint3& point) {
@@ -263,11 +269,9 @@ void MovementAIComponent::PullToPoint(const NiPoint3& point) {
m_PullPoint = point; m_PullPoint = point;
} }
void MovementAIComponent::SetPath(std::vector<NiPoint3> path) { void MovementAIComponent::SetPath(const std::vector<NiPoint3>& path) {
std::reverse(path.begin(), path.end()); for (auto itr = path.rbegin(); itr != path.rend(); ++itr) {
m_Queue.push(*itr);
for (const auto& point : path) {
m_Queue.push(point);
} }
SetDestination(m_Queue.top()); SetDestination(m_Queue.top());
@@ -275,7 +279,7 @@ void MovementAIComponent::SetPath(std::vector<NiPoint3> path) {
m_Queue.pop(); m_Queue.pop();
} }
float MovementAIComponent::GetBaseSpeed(LOT lot) { float MovementAIComponent::GetBaseSpeed(const LOT lot) {
// Check if the lot is in the cache // Check if the lot is in the cache
const auto& it = m_PhysicsSpeedCache.find(lot); const auto& it = m_PhysicsSpeedCache.find(lot);
@@ -283,30 +287,19 @@ float MovementAIComponent::GetBaseSpeed(LOT lot) {
return it->second; return it->second;
} }
CDComponentsRegistryTable* componentRegistryTable = CDClientManager::Instance().GetTable<CDComponentsRegistryTable>(); auto* componentRegistryTable = CDClientManager::Instance().GetTable<CDComponentsRegistryTable>();
CDPhysicsComponentTable* physicsComponentTable = CDClientManager::Instance().GetTable<CDPhysicsComponentTable>(); auto* physicsComponentTable = CDClientManager::Instance().GetTable<CDPhysicsComponentTable>();
int32_t componentID; int32_t componentID;
CDPhysicsComponent* physicsComponent = nullptr; CDPhysicsComponent* physicsComponent = nullptr;
componentID = componentRegistryTable->GetByIDAndType(lot, eReplicaComponentType::CONTROLLABLE_PHYSICS, -1); componentID = componentRegistryTable->GetByIDAndType(lot, eReplicaComponentType::CONTROLLABLE_PHYSICS, -1);
if (componentID != -1) { if (componentID == -1) {
physicsComponent = physicsComponentTable->GetByID(componentID); componentID = componentRegistryTable->GetByIDAndType(lot, eReplicaComponentType::SIMPLE_PHYSICS, -1);
goto foundComponent;
} }
componentID = componentRegistryTable->GetByIDAndType(lot, eReplicaComponentType::SIMPLE_PHYSICS, -1); physicsComponent = physicsComponentTable->GetByID(componentID);
if (componentID != -1) {
physicsComponent = physicsComponentTable->GetByID(componentID);
goto foundComponent;
}
foundComponent:
// Client defaults speed to 10 and if the speed is also null in the table, it defaults to 10. // Client defaults speed to 10 and if the speed is also null in the table, it defaults to 10.
float speed = 10.0f; float speed = 10.0f;
@@ -321,73 +314,36 @@ foundComponent:
return speed; return speed;
} }
void MovementAIComponent::SetPosition(const NiPoint3& value) { void MovementAIComponent::SetPosition(const NiPoint3& value) const {
auto* controllablePhysicsComponent = m_Parent->GetComponent<ControllablePhysicsComponent>(); m_ParentEntity->SetPosition(value);
if (controllablePhysicsComponent != nullptr) {
controllablePhysicsComponent->SetPosition(value);
return;
}
auto* simplePhysicsComponent = m_Parent->GetComponent<SimplePhysicsComponent>();
if (simplePhysicsComponent != nullptr) {
simplePhysicsComponent->SetPosition(value);
}
} }
void MovementAIComponent::SetRotation(const NiQuaternion& value) { void MovementAIComponent::SetRotation(const NiQuaternion& value) const {
if (m_LockRotation) { if (!m_LockRotation) m_ParentEntity->SetRotation(value);
return;
}
auto* controllablePhysicsComponent = m_Parent->GetComponent<ControllablePhysicsComponent>();
if (controllablePhysicsComponent != nullptr) {
controllablePhysicsComponent->SetRotation(value);
return;
}
auto* simplePhysicsComponent = m_Parent->GetComponent<SimplePhysicsComponent>();
if (simplePhysicsComponent != nullptr) {
simplePhysicsComponent->SetRotation(value);
}
} }
void MovementAIComponent::SetVelocity(const NiPoint3& value) { void MovementAIComponent::SetVelocity(const NiPoint3& value) const {
auto* controllablePhysicsComponent = m_Parent->GetComponent<ControllablePhysicsComponent>(); auto* controllablePhysicsComponent = m_ParentEntity->GetComponent<ControllablePhysicsComponent>();
if (controllablePhysicsComponent != nullptr) { if (controllablePhysicsComponent) {
controllablePhysicsComponent->SetVelocity(value); controllablePhysicsComponent->SetVelocity(value);
return; return;
} }
auto* simplePhysicsComponent = m_Parent->GetComponent<SimplePhysicsComponent>(); auto* simplePhysicsComponent = m_ParentEntity->GetComponent<SimplePhysicsComponent>();
if (simplePhysicsComponent != nullptr) { if (simplePhysicsComponent) {
simplePhysicsComponent->SetVelocity(value); simplePhysicsComponent->SetVelocity(value);
} }
} }
void MovementAIComponent::SetDestination(const NiPoint3& value) { void MovementAIComponent::SetDestination(const NiPoint3& value) {
if (m_Interrupted) { if (m_Interrupted) return;
return;
}
/*if (Vector3::DistanceSquared(value, GetDestination()) < 2 * 2)
{
return;
}*/
const auto location = ApproximateLocation(); const auto location = ApproximateLocation();
if (!AtFinalWaypoint()) { if (!AtFinalWaypoint()) SetPosition(location);
SetPosition(location);
}
std::vector<NiPoint3> computedPath; std::vector<NiPoint3> computedPath;
@@ -409,17 +365,15 @@ void MovementAIComponent::SetDestination(const NiPoint3& value) {
} }
} }
if (computedPath.empty()) // Somehow failed // Somehow failed
{ if (computedPath.empty()) return;
return;
}
m_CurrentPath.clear(); m_CurrentPath.clear();
m_CurrentPath.push_back(location); m_CurrentPath.push_back(location);
// Simply path // Simply path
for (auto point : computedPath) { for (auto& point : computedPath) {
if (dpWorld::Instance().IsLoaded()) { if (dpWorld::Instance().IsLoaded()) {
point.y = dpWorld::Instance().GetNavMesh()->GetHeightAtPoint(point); point.y = dpWorld::Instance().GetNavMesh()->GetHeightAtPoint(point);
} }
@@ -427,21 +381,18 @@ void MovementAIComponent::SetDestination(const NiPoint3& value) {
m_CurrentPath.push_back(point); m_CurrentPath.push_back(point);
} }
m_CurrentPath.push_back(computedPath[computedPath.size() - 1]); m_CurrentPath.push_back(computedPath.back());
m_PathIndex = 0; m_PathIndex = 0;
m_TotalTime = m_Timer = 0; m_TotalTime = 0.0f;
m_Timer = 0.0f;
m_Done = false; m_Done = false;
} }
NiPoint3 MovementAIComponent::GetDestination() const { NiPoint3 MovementAIComponent::GetDestination() const {
if (m_CurrentPath.empty()) { return m_CurrentPath.empty() ? GetCurrentPosition() : m_CurrentPath.back();
return GetCurrentPosition();
}
return m_CurrentPath[m_CurrentPath.size() - 1];
} }
void MovementAIComponent::SetSpeed(const float value) { void MovementAIComponent::SetSpeed(const float value) {
@@ -449,38 +400,6 @@ void MovementAIComponent::SetSpeed(const float value) {
m_Acceleration = value / 5; m_Acceleration = value / 5;
} }
float MovementAIComponent::GetSpeed() const { NiPoint3 MovementAIComponent::GetCurrentPosition() const {
return m_Speed; return m_ParentEntity->GetPosition();
}
void MovementAIComponent::SetAcceleration(const float value) {
m_Acceleration = value;
}
float MovementAIComponent::GetAcceleration() const {
return m_Acceleration;
}
void MovementAIComponent::SetHaltDistance(const float value) {
m_HaltDistance = value;
}
float MovementAIComponent::GetHaltDistance() const {
return m_HaltDistance;
}
void MovementAIComponent::SetCurrentSpeed(float value) {
m_CurrentSpeed = value;
}
float MovementAIComponent::GetCurrentSpeed() const {
return m_CurrentSpeed;
}
void MovementAIComponent::SetLockRotation(bool value) {
m_LockRotation = value;
}
bool MovementAIComponent::GetLockRotation() const {
return m_LockRotation;
} }

View File

@@ -3,18 +3,16 @@
* Copyright 2018 * Copyright 2018
*/ */
#ifndef MOVEMENTAICOMPONENT_H #ifndef __MOVEMENTAICOMPONENT_H__
#define MOVEMENTAICOMPONENT_H #define __MOVEMENTAICOMPONENT_H__
#pragma once
#include <cstdint>
#include <stack>
#include <vector>
#include "BitStream.h"
#include "Entity.h"
#include "GameMessages.h"
#include "EntityManager.h"
#include "Game.h"
#include "dLogger.h"
#include "Component.h" #include "Component.h"
#include "eReplicaComponentType.h" #include "eReplicaComponentType.h"
#include <vector>
class ControllablePhysicsComponent; class ControllablePhysicsComponent;
class BaseCombatAIComponent; class BaseCombatAIComponent;
@@ -23,6 +21,9 @@ class BaseCombatAIComponent;
* Information that describes the different variables used to make an entity move around * Information that describes the different variables used to make an entity move around
*/ */
struct MovementAIInfo { struct MovementAIInfo {
// copy assignment
MovementAIInfo& operator=(const MovementAIInfo& other) = default;
std::string movementType; std::string movementType;
/** /**
@@ -55,12 +56,14 @@ struct MovementAIInfo {
* Component that handles the movement settings of an entity. Not to be confused with the BaseCombatAI component that * Component that handles the movement settings of an entity. Not to be confused with the BaseCombatAI component that
* actually handles attackig and following enemy entities. * actually handles attackig and following enemy entities.
*/ */
class MovementAIComponent : public Component { class MovementAIComponent final : public Component {
public: public:
static const eReplicaComponentType ComponentType = eReplicaComponentType::MOVEMENT_AI; inline static const eReplicaComponentType ComponentType = eReplicaComponentType::MOVEMENT_AI;
MovementAIComponent(Entity* parentEntity, MovementAIInfo info); MovementAIComponent(Entity* parentEntity, int32_t componentId = -1);
~MovementAIComponent() override; void Startup() override;
void LoadTemplateData() override;
void LoadConfigData() override;
void Update(float deltaTime) override; void Update(float deltaTime) override;
@@ -68,7 +71,7 @@ public:
* Returns the basic settings that this entity uses to move around * Returns the basic settings that this entity uses to move around
* @return the basic settings that this entity uses to move around * @return the basic settings that this entity uses to move around
*/ */
const MovementAIInfo& GetInfo() const; const MovementAIInfo& GetInfo() const { return m_Info; };
/** /**
* Set a destination point for the entity to move towards * Set a destination point for the entity to move towards
@@ -86,61 +89,61 @@ public:
* Sets the max speed at which this entity may run * Sets the max speed at which this entity may run
* @param value the speed value to set * @param value the speed value to set
*/ */
void SetSpeed(float value); void SetSpeed(const float value);
/** /**
* Returns the max speed at which this entity may run * Returns the max speed at which this entity may run
* @return the max speed at which this entity may run * @return the max speed at which this entity may run
*/ */
float GetSpeed() const; float GetSpeed() const { return m_Speed; };
/** /**
* Sets how fast the entity will accelerate when not running at full speed * Sets how fast the entity will accelerate when not running at full speed
* @param value the acceleration to set * @param value the acceleration to set
*/ */
void SetAcceleration(float value); void SetAcceleration(const float value) { m_Acceleration = value; }
/** /**
* Returns the current speed at which this entity accelerates when not running at full speed * Returns the current speed at which this entity accelerates when not running at full speed
* @return the current speed at which this entity accelerates when not running at full speed * @return the current speed at which this entity accelerates when not running at full speed
*/ */
float GetAcceleration() const; float GetAcceleration() const { return m_Acceleration; };
/** /**
* Sets the halting distance (the distance at which we consider the target to be reached) * Sets the halting distance (the distance at which we consider the target to be reached)
* @param value the halting distance to set * @param value the halting distance to set
*/ */
void SetHaltDistance(float value); void SetHaltDistance(const float value) { m_HaltDistance = value; }
/** /**
* Returns the current halting distance (the distance at which we consider the target to be reached) * Returns the current halting distance (the distance at which we consider the target to be reached)
* @return the current halting distance * @return the current halting distance
*/ */
float GetHaltDistance() const; float GetHaltDistance() const { return m_HaltDistance; };
/** /**
* Sets the speed the entity is currently running at * Sets the speed the entity is currently running at
* @param value the speed value to set * @param value the speed value to set
*/ */
void SetCurrentSpeed(float value); void SetCurrentSpeed(const float value) { m_CurrentSpeed = value; }
/** /**
* Returns the speed the entity is currently running at * Returns the speed the entity is currently running at
* @return the speed the entity is currently running at * @return the speed the entity is currently running at
*/ */
float GetCurrentSpeed() const; float GetCurrentSpeed() const { return m_CurrentSpeed; };
/** /**
* Locks the rotation of this entity in place, depending on the argument * Locks the rotation of this entity in place, depending on the argument
* @param value if true, the entity will be rotationally locked * @param value if true, the entity will be rotationally locked
*/ */
void SetLockRotation(bool value); void SetLockRotation(const bool value) { m_LockRotation = value; }
/** /**
* Returns whether this entity is currently rotationally locked * Returns whether this entity is currently rotationally locked
* @return true if the entity is rotationally locked, false otherwise * @return true if the entity is rotationally locked, false otherwise
*/ */
bool GetLockRotation() const; bool GetLockRotation() const { return m_LockRotation; };
/** /**
* Attempts to update the waypoint index, making the entity move to the next waypoint * Attempts to update the waypoint index, making the entity move to the next waypoint
@@ -158,7 +161,7 @@ public:
* Returns the waypoint this entity is supposed to move towards next * Returns the waypoint this entity is supposed to move towards next
* @return the waypoint this entity is supposed to move towards next * @return the waypoint this entity is supposed to move towards next
*/ */
NiPoint3 GetNextWaypoint() const; NiPoint3 GetNextWaypoint() const { return m_NextWaypoint; };
/** /**
* Returns the current position of this entity * Returns the current position of this entity
@@ -184,13 +187,13 @@ public:
* Returns the time it will take to reach the final waypoint according to the current speed * Returns the time it will take to reach the final waypoint according to the current speed
* @return the time it will take to reach the final waypoint according to the current speed * @return the time it will take to reach the final waypoint according to the current speed
*/ */
float GetTimer() const; float GetTimer() const { return m_Timer; };
/** /**
* Returns if the entity is at its final waypoint * Returns if the entity is at its final waypoint
* @return if the entity is at its final waypoint * @return if the entity is at its final waypoint
*/ */
bool AtFinalWaypoint() const; bool AtFinalWaypoint() const { return m_Done; };
/** /**
* Renders the entity stationary * Renders the entity stationary
@@ -208,34 +211,35 @@ public:
* Sets a path to follow for the AI * Sets a path to follow for the AI
* @param path the path to follow * @param path the path to follow
*/ */
void SetPath(std::vector<NiPoint3> path); void SetPath(const std::vector<NiPoint3>& path);
/** /**
* Returns the base speed from the DB for a given LOT * Returns the base speed from the DB for a given LOT
* @param lot the lot to check for * @param lot the lot to check for
* @return the base speed of the lot * @return the base speed of the lot
*/ */
static float GetBaseSpeed(LOT lot); static float GetBaseSpeed(const LOT lot);
void SetMoveInfo(const MovementAIInfo& value);
private: private:
/** /**
* Sets the current position of the entity * Sets the current position of the entity
* @param value the position to set * @param value the position to set
*/ */
void SetPosition(const NiPoint3& value); void SetPosition(const NiPoint3& value) const;
/** /**
* Sets the current rotation of the entity * Sets the current rotation of the entity
* @param value the rotation to set * @param value the rotation to set
*/ */
void SetRotation(const NiQuaternion& value); void SetRotation(const NiQuaternion& value) const;
/** /**
* Sets the current velocity of the entityes * Sets the current velocity of the entityes
* @param value the velocity to set * @param value the velocity to set
*/ */
void SetVelocity(const NiPoint3& value); void SetVelocity(const NiPoint3& value) const;
/** /**
* Base information regarding the movement information for this entity * Base information regarding the movement information for this entity
@@ -326,6 +330,8 @@ private:
* Cache of all lots and their respective speeds * Cache of all lots and their respective speeds
*/ */
static std::map<LOT, float> m_PhysicsSpeedCache; static std::map<LOT, float> m_PhysicsSpeedCache;
int32_t m_ComponentId;
}; };
#endif // MOVEMENTAICOMPONENT_H #endif // __MOVEMENTAICOMPONENT_H__

View File

@@ -1,6 +1,6 @@
/* /*
* Darkflame Universe * Darkflame Universe
* Copyright 2019 * Copyright 2023
*/ */
#include "MovingPlatformComponent.h" #include "MovingPlatformComponent.h"
@@ -15,7 +15,7 @@
#include "Zone.h" #include "Zone.h"
MoverSubComponent::MoverSubComponent(const NiPoint3& startPos) { MoverSubComponent::MoverSubComponent(const NiPoint3& startPos) {
mPosition = {}; mPosition = NiPoint3::ZERO;
mState = eMovementPlatformState::Stopped; mState = eMovementPlatformState::Stopped;
mDesiredWaypointIndex = 0; // -1; mDesiredWaypointIndex = 0; // -1;
@@ -30,8 +30,6 @@ MoverSubComponent::MoverSubComponent(const NiPoint3& startPos) {
mIdleTimeElapsed = 0.0f; mIdleTimeElapsed = 0.0f;
} }
MoverSubComponent::~MoverSubComponent() = default;
void MoverSubComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags) const { void MoverSubComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags) const {
outBitStream->Write<bool>(true); outBitStream->Write<bool>(true);
@@ -57,12 +55,12 @@ void MoverSubComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsIniti
MovingPlatformComponent::MovingPlatformComponent(Entity* parent, const std::string& pathName) : Component(parent) { MovingPlatformComponent::MovingPlatformComponent(Entity* parent, const std::string& pathName) : Component(parent) {
m_MoverSubComponentType = eMoverSubComponentType::mover; m_MoverSubComponentType = eMoverSubComponentType::mover;
m_MoverSubComponent = new MoverSubComponent(m_Parent->GetDefaultPosition()); m_MoverSubComponent = new MoverSubComponent(m_ParentEntity->GetDefaultPosition());
m_PathName = GeneralUtils::ASCIIToUTF16(pathName); m_PathName = GeneralUtils::ASCIIToUTF16(pathName);
m_Path = dZoneManager::Instance()->GetZone()->GetPath(pathName); m_Path = dZoneManager::Instance()->GetZone()->GetPath(pathName);
m_NoAutoStart = false; m_NoAutoStart = false;
if (m_Path == nullptr) { if (!m_Path) {
Game::logger->Log("MovingPlatformComponent", "Path not found: %s", pathName.c_str()); Game::logger->Log("MovingPlatformComponent", "Path not found: %s", pathName.c_str());
} }
} }
@@ -75,13 +73,13 @@ void MovingPlatformComponent::Serialize(RakNet::BitStream* outBitStream, bool bI
// Here we don't serialize the moving platform to let the client simulate the movement // Here we don't serialize the moving platform to let the client simulate the movement
if (!m_Serialize) { if (!m_Serialize) {
outBitStream->Write<bool>(false); outBitStream->Write0();
outBitStream->Write<bool>(false); outBitStream->Write0();
return; return;
} }
outBitStream->Write<bool>(true); outBitStream->Write1();
auto hasPath = !m_PathingStopped && !m_PathName.empty(); auto hasPath = !m_PathingStopped && !m_PathName.empty();
outBitStream->Write(hasPath); outBitStream->Write(hasPath);
@@ -133,7 +131,7 @@ void MovingPlatformComponent::SetMovementState(eMovementPlatformState value) {
subComponent->mState = value; subComponent->mState = value;
EntityManager::Instance()->SerializeEntity(m_Parent); EntityManager::Instance()->SerializeEntity(m_ParentEntity);
} }
void MovingPlatformComponent::GotoWaypoint(uint32_t index, bool stopAtWaypoint) { void MovingPlatformComponent::GotoWaypoint(uint32_t index, bool stopAtWaypoint) {
@@ -147,7 +145,7 @@ void MovingPlatformComponent::GotoWaypoint(uint32_t index, bool stopAtWaypoint)
} }
void MovingPlatformComponent::StartPathing() { void MovingPlatformComponent::StartPathing() {
//GameMessages::SendStartPathing(m_Parent); //GameMessages::SendStartPathing(m_ParentEntity);
m_PathingStopped = false; m_PathingStopped = false;
auto* subComponent = static_cast<MoverSubComponent*>(m_MoverSubComponent); auto* subComponent = static_cast<MoverSubComponent*>(m_MoverSubComponent);
@@ -167,14 +165,14 @@ void MovingPlatformComponent::StartPathing() {
targetPosition = nextWaypoint.position; targetPosition = nextWaypoint.position;
} else { } else {
subComponent->mPosition = m_Parent->GetPosition(); subComponent->mPosition = m_ParentEntity->GetPosition();
subComponent->mSpeed = 1.0f; subComponent->mSpeed = 1.0f;
subComponent->mWaitTime = 2.0f; subComponent->mWaitTime = 2.0f;
targetPosition = m_Parent->GetPosition() + NiPoint3(0.0f, 10.0f, 0.0f); targetPosition = m_ParentEntity->GetPosition() + NiPoint3(0.0f, 10.0f, 0.0f);
} }
m_Parent->AddCallbackTimer(subComponent->mWaitTime, [this] { m_ParentEntity->AddCallbackTimer(subComponent->mWaitTime, [this] {
SetMovementState(eMovementPlatformState::Moving); SetMovementState(eMovementPlatformState::Moving);
}); });
@@ -182,19 +180,17 @@ void MovingPlatformComponent::StartPathing() {
const auto travelNext = subComponent->mWaitTime + travelTime; const auto travelNext = subComponent->mWaitTime + travelTime;
m_Parent->AddCallbackTimer(travelTime, [subComponent, this] { m_ParentEntity->AddCallbackTimer(travelTime, [subComponent, this] {
for (CppScripts::Script* script : CppScripts::GetEntityScripts(m_Parent)) { m_ParentEntity->GetScript()->OnWaypointReached(m_ParentEntity, subComponent->mNextWaypointIndex);
script->OnWaypointReached(m_Parent, subComponent->mNextWaypointIndex);
}
}); });
m_Parent->AddCallbackTimer(travelNext, [this] { m_ParentEntity->AddCallbackTimer(travelNext, [this] {
ContinuePathing(); ContinuePathing();
}); });
//GameMessages::SendPlatformResync(m_Parent, UNASSIGNED_SYSTEM_ADDRESS); //GameMessages::SendPlatformResync(m_ParentEntity, UNASSIGNED_SYSTEM_ADDRESS);
EntityManager::Instance()->SerializeEntity(m_Parent); EntityManager::Instance()->SerializeEntity(m_ParentEntity);
} }
void MovingPlatformComponent::ContinuePathing() { void MovingPlatformComponent::ContinuePathing() {
@@ -222,17 +218,17 @@ void MovingPlatformComponent::ContinuePathing() {
targetPosition = nextWaypoint.position; targetPosition = nextWaypoint.position;
} else { } else {
subComponent->mPosition = m_Parent->GetPosition(); subComponent->mPosition = m_ParentEntity->GetPosition();
subComponent->mSpeed = 1.0f; subComponent->mSpeed = 1.0f;
subComponent->mWaitTime = 2.0f; subComponent->mWaitTime = 2.0f;
targetPosition = m_Parent->GetPosition() + NiPoint3(0.0f, 10.0f, 0.0f); targetPosition = m_ParentEntity->GetPosition() + NiPoint3(0.0f, 10.0f, 0.0f);
pathSize = 1; pathSize = 1;
behavior = PathBehavior::Loop; behavior = PathBehavior::Loop;
} }
if (m_Parent->GetLOT() == 9483) { if (m_ParentEntity->GetLOT() == 9483) {
behavior = PathBehavior::Bounce; behavior = PathBehavior::Bounce;
} else { } else {
return; return;
@@ -242,7 +238,7 @@ void MovingPlatformComponent::ContinuePathing() {
subComponent->mCurrentWaypointIndex = pathSize; subComponent->mCurrentWaypointIndex = pathSize;
switch (behavior) { switch (behavior) {
case PathBehavior::Once: case PathBehavior::Once:
EntityManager::Instance()->SerializeEntity(m_Parent); EntityManager::Instance()->SerializeEntity(m_ParentEntity);
return; return;
case PathBehavior::Bounce: case PathBehavior::Bounce:
@@ -271,7 +267,7 @@ void MovingPlatformComponent::ContinuePathing() {
subComponent->mCurrentWaypointIndex = 1; subComponent->mCurrentWaypointIndex = 1;
*/ */
//GameMessages::SendPlatformResync(m_Parent, UNASSIGNED_SYSTEM_ADDRESS); //GameMessages::SendPlatformResync(m_ParentEntity, UNASSIGNED_SYSTEM_ADDRESS);
if (subComponent->mCurrentWaypointIndex == subComponent->mDesiredWaypointIndex) { if (subComponent->mCurrentWaypointIndex == subComponent->mDesiredWaypointIndex) {
// TODO: Send event? // TODO: Send event?
@@ -280,35 +276,33 @@ void MovingPlatformComponent::ContinuePathing() {
return; return;
} }
m_Parent->CancelCallbackTimers(); m_ParentEntity->CancelCallbackTimers();
m_Parent->AddCallbackTimer(subComponent->mWaitTime, [this] { m_ParentEntity->AddCallbackTimer(subComponent->mWaitTime, [this] {
SetMovementState(eMovementPlatformState::Moving); SetMovementState(eMovementPlatformState::Moving);
}); });
auto travelTime = Vector3::Distance(targetPosition, subComponent->mPosition) / subComponent->mSpeed + 1.5; auto travelTime = Vector3::Distance(targetPosition, subComponent->mPosition) / subComponent->mSpeed + 1.5;
if (m_Parent->GetLOT() == 9483) { if (m_ParentEntity->GetLOT() == 9483) {
travelTime += 20; travelTime += 20;
} }
const auto travelNext = subComponent->mWaitTime + travelTime; const auto travelNext = subComponent->mWaitTime + travelTime;
m_Parent->AddCallbackTimer(travelTime, [subComponent, this] { m_ParentEntity->AddCallbackTimer(travelTime, [subComponent, this] {
for (CppScripts::Script* script : CppScripts::GetEntityScripts(m_Parent)) { m_ParentEntity->GetScript()->OnWaypointReached(m_ParentEntity, subComponent->mNextWaypointIndex);
script->OnWaypointReached(m_Parent, subComponent->mNextWaypointIndex);
}
}); });
m_Parent->AddCallbackTimer(travelNext, [this] { m_ParentEntity->AddCallbackTimer(travelNext, [this] {
ContinuePathing(); ContinuePathing();
}); });
EntityManager::Instance()->SerializeEntity(m_Parent); EntityManager::Instance()->SerializeEntity(m_ParentEntity);
} }
void MovingPlatformComponent::StopPathing() { void MovingPlatformComponent::StopPathing() {
//m_Parent->CancelCallbackTimers(); //m_ParentEntity->CancelCallbackTimers();
auto* subComponent = static_cast<MoverSubComponent*>(m_MoverSubComponent); auto* subComponent = static_cast<MoverSubComponent*>(m_MoverSubComponent);
@@ -318,9 +312,9 @@ void MovingPlatformComponent::StopPathing() {
subComponent->mDesiredWaypointIndex = -1; subComponent->mDesiredWaypointIndex = -1;
subComponent->mShouldStopAtDesiredWaypoint = false; subComponent->mShouldStopAtDesiredWaypoint = false;
EntityManager::Instance()->SerializeEntity(m_Parent); EntityManager::Instance()->SerializeEntity(m_ParentEntity);
//GameMessages::SendPlatformResync(m_Parent, UNASSIGNED_SYSTEM_ADDRESS); //GameMessages::SendPlatformResync(m_ParentEntity, UNASSIGNED_SYSTEM_ADDRESS);
} }
void MovingPlatformComponent::SetSerialized(bool value) { void MovingPlatformComponent::SetSerialized(bool value) {
@@ -338,10 +332,10 @@ void MovingPlatformComponent::SetNoAutoStart(const bool value) {
void MovingPlatformComponent::WarpToWaypoint(size_t index) { void MovingPlatformComponent::WarpToWaypoint(size_t index) {
const auto& waypoint = m_Path->pathWaypoints[index]; const auto& waypoint = m_Path->pathWaypoints[index];
m_Parent->SetPosition(waypoint.position); m_ParentEntity->SetPosition(waypoint.position);
m_Parent->SetRotation(waypoint.rotation); m_ParentEntity->SetRotation(waypoint.rotation);
EntityManager::Instance()->SerializeEntity(m_Parent); EntityManager::Instance()->SerializeEntity(m_ParentEntity);
} }
size_t MovingPlatformComponent::GetLastWaypointIndex() const { size_t MovingPlatformComponent::GetLastWaypointIndex() const {

View File

@@ -6,12 +6,10 @@
#ifndef MOVINGPLATFORMCOMPONENT_H #ifndef MOVINGPLATFORMCOMPONENT_H
#define MOVINGPLATFORMCOMPONENT_H #define MOVINGPLATFORMCOMPONENT_H
#include "RakNetTypes.h"
#include "NiPoint3.h" #include "NiPoint3.h"
#include <string> #include <string>
#include "dCommonVars.h" #include "dCommonVars.h"
#include "EntityManager.h"
#include "Component.h" #include "Component.h"
#include "eMovementPlatformState.h" #include "eMovementPlatformState.h"
#include "eReplicaComponentType.h" #include "eReplicaComponentType.h"
@@ -36,7 +34,6 @@ enum class eMoverSubComponentType : uint32_t {
class MoverSubComponent { class MoverSubComponent {
public: public:
MoverSubComponent(const NiPoint3& startPos); MoverSubComponent(const NiPoint3& startPos);
~MoverSubComponent();
void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags) const; void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags) const;
@@ -106,7 +103,7 @@ public:
*/ */
class MovingPlatformComponent : public Component { class MovingPlatformComponent : public Component {
public: public:
static const eReplicaComponentType ComponentType = eReplicaComponentType::MOVING_PLATFORM; inline static const eReplicaComponentType ComponentType = eReplicaComponentType::MOVING_PLATFORM;
MovingPlatformComponent(Entity* parent, const std::string& pathName); MovingPlatformComponent(Entity* parent, const std::string& pathName);
~MovingPlatformComponent() override; ~MovingPlatformComponent() override;

View File

@@ -0,0 +1,32 @@
#include "MultiZoneEntranceComponent.h"
#include "RocketLaunchpadControlComponent.h"
#include "InventoryComponent.h"
#include "CharacterComponent.h"
#include "GameMessages.h"
void MultiZoneEntranceComponent::LoadConfigData() {
std::string zoneString = GeneralUtils::UTF16ToWTF8(m_ParentEntity->GetVar<std::u16string>(u"MultiZoneIDs"));
const auto zoneSplitStr = GeneralUtils::SplitString(zoneString, ';');
for (const auto& zone : zoneSplitStr) {
uint32_t mapId;
if (GeneralUtils::TryParse(zone, mapId)) m_LUPWorlds.push_back(mapId);
}
}
void MultiZoneEntranceComponent::OnUse(Entity* originator) {
auto* characterComponent = originator->GetComponent<CharacterComponent>();
if (!characterComponent) return;
auto* rocket = characterComponent->RocketEquip(originator);
if (!rocket) return;
// The LUP world menu is just the property menu, the client handles this in flash
GameMessages::SendPropertyEntranceBegin(m_ParentEntity->GetObjectID(), m_ParentEntity->GetSystemAddress());
}
void MultiZoneEntranceComponent::OnSelectWorld(Entity* originator, const uint32_t index) const {
auto* rocketLaunchpadControlComponent = m_ParentEntity->GetComponent<RocketLaunchpadControlComponent>();
if (!rocketLaunchpadControlComponent || index >= m_LUPWorlds.size()) return;
rocketLaunchpadControlComponent->Launch(originator, m_LUPWorlds[index], 0);
}

View File

@@ -1,24 +1,23 @@
#pragma once #pragma once
#include "Entity.h" #include "Entity.h"
#include "GameMessages.h"
#include "Component.h" #include "Component.h"
#include "eReplicaComponentType.h" #include "eReplicaComponentType.h"
/** /**
* Component that handles the LUP/WBL rocket launchpad that can be interacted with to travel to WBL worlds. * Component that handles the LUP/WBL rocket launchpad that can be interacted with to travel to WBL worlds.
*
*/ */
class RocketLaunchLupComponent : public Component { class MultiZoneEntranceComponent final : public Component {
public: public:
static const eReplicaComponentType ComponentType = eReplicaComponentType::ROCKET_LAUNCH_LUP; inline static const eReplicaComponentType ComponentType = eReplicaComponentType::MULTI_ZONE_ENTRANCE;
/** /**
* Constructor for this component, builds the m_LUPWorlds vector * Constructor for this component, builds the m_LUPWorlds vector
* @param parent parent that contains this component * @param parent parent that contains this component
*/ */
RocketLaunchLupComponent(Entity* parent); MultiZoneEntranceComponent(Entity* parent) : Component(parent) {};
~RocketLaunchLupComponent() override;
void LoadConfigData() override;
/** /**
* Handles an OnUse event from some entity, preparing it for launch to some other world * Handles an OnUse event from some entity, preparing it for launch to some other world
@@ -31,10 +30,10 @@ public:
* @param originator the entity that triggered the event * @param originator the entity that triggered the event
* @param index index of the world that was selected * @param index index of the world that was selected
*/ */
void OnSelectWorld(Entity* originator, uint32_t index); void OnSelectWorld(Entity* originator, const uint32_t index) const;
private: private:
/** /**
* vector of the LUP World Zone IDs, built from CDServer's LUPZoneIDs table * vector of the LUP World Zone IDs, built from CDServer's LUPZoneIDs table
*/ */
std::vector<LWOMAPID> m_LUPWorlds{}; std::vector<LWOMAPID> m_LUPWorlds;
}; };

View File

@@ -0,0 +1,29 @@
#include "MutableModelBehaviorComponent.h"
#include "Entity.h"
MutableModelBehaviorComponent::MutableModelBehaviorComponent(Entity* parent) : Component(parent) {
m_DirtyModelBehaviorInfo = false;
m_BehaviorCount = 0;
m_IsPaused = true;
m_DirtyModelEditingInfo = false;
m_OldObjId = LWOOBJID_EMPTY;
m_Editor = LWOOBJID_EMPTY;
}
void MutableModelBehaviorComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags) {
outBitStream->Write(m_DirtyModelBehaviorInfo || bIsInitialUpdate);
if (m_DirtyModelBehaviorInfo || bIsInitialUpdate) {
outBitStream->Write(m_BehaviorCount);
outBitStream->Write(m_IsPaused);
outBitStream->Write(m_DirtyModelEditingInfo && bIsInitialUpdate);
if (m_DirtyModelEditingInfo && bIsInitialUpdate) {
outBitStream->Write(m_OldObjId);
outBitStream->Write(m_Editor);
if (!bIsInitialUpdate) m_DirtyModelEditingInfo = false;
}
if (!bIsInitialUpdate) m_DirtyModelBehaviorInfo = false;
}
}

View File

@@ -0,0 +1,53 @@
#pragma once
#include "dCommonVars.h"
#include "RakNetTypes.h"
#include "NiPoint3.h"
#include "NiQuaternion.h"
#include "Component.h"
#include "eReplicaComponentType.h"
class Entity;
/**
* Component that represents entities that are a model, e.g. collectible models and BBB models.
*/
class MutableModelBehaviorComponent final : public Component {
public:
static const eReplicaComponentType ComponentType = eReplicaComponentType::MODEL_BEHAVIOR;
MutableModelBehaviorComponent(Entity* parent);
void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags);
private:
/**
* if the behavior info has changed
*/
bool m_DirtyModelBehaviorInfo;
/**
* The number of behaviors on the model
*/
uint32_t m_BehaviorCount;
/**
* if the models behaviors are paused
*/
bool m_IsPaused;
/**
* if the editing info is dirty
*/
bool m_DirtyModelEditingInfo;
/**
* The old ID of the model
*/
LWOOBJID m_OldObjId;
/**
* The ID of the editor of the model
*/
LWOOBJID m_Editor;
};

View File

@@ -19,6 +19,8 @@
#include "ePetTamingNotifyType.h" #include "ePetTamingNotifyType.h"
#include "eUseItemResponse.h" #include "eUseItemResponse.h"
#include "ePlayerFlag.h" #include "ePlayerFlag.h"
#include "MovementAIComponent.h"
#include "Preconditions.h"
#include "Game.h" #include "Game.h"
#include "dConfig.h" #include "dConfig.h"
@@ -30,45 +32,45 @@
#include "eObjectBits.h" #include "eObjectBits.h"
#include "eGameMasterLevel.h" #include "eGameMasterLevel.h"
std::unordered_map<LOT, PetComponent::PetPuzzleData> PetComponent::buildCache{}; std::unordered_map<LOT, PetComponent::PetPuzzleData> PetComponent::buildCache;
std::unordered_map<LWOOBJID, LWOOBJID> PetComponent::currentActivities{}; std::unordered_map<LWOOBJID, LWOOBJID> PetComponent::currentActivities;
std::unordered_map<LWOOBJID, LWOOBJID> PetComponent::activePets{}; std::unordered_map<LWOOBJID, LWOOBJID> PetComponent::activePets;
/** /**
* Maps all the pet lots to a flag indicating that the player has caught it. All basic pets have been guessed by ObjID * Maps all the pet lots to a flag indicating that the player has caught it. All basic pets have been guessed by ObjID
* while the faction ones could be checked using their respective missions. * while the faction ones could be checked using their respective missions.
*/ */
std::map<LOT, int32_t> PetComponent::petFlags = { std::map<LOT, ePlayerFlag> PetComponent::petFlags = {
{ 3050, 801 }, // Elephant { 3050, ePlayerFlag::ELEPHANT_PET_3050 },
{ 3054, 803 }, // Cat { 3054, ePlayerFlag::CAT_PET_3054 },
{ 3195, 806 }, // Triceratops { 3195, ePlayerFlag::TRICERATOPS_PET_3195 },
{ 3254, 807 }, // Terrier { 3254, ePlayerFlag::TERRIER_PET_3254 },
{ 3261, 811 }, // Skunk { 3261, ePlayerFlag::SKUNK_PET_3261 },
{ 3672, 813 }, // Bunny { 3672, ePlayerFlag::BUNNY_PET_3672 },
{ 3994, 814 }, // Crocodile { 3994, ePlayerFlag::CROCODILE_PET_3994 },
{ 5635, 815 }, // Doberman { 5635, ePlayerFlag::DOBERMAN_PET_5635 },
{ 5636, 816 }, // Buffalo { 5636, ePlayerFlag::BUFFALO_PET_5636 },
{ 5637, 818 }, // Robot Dog { 5637, ePlayerFlag::ROBOT_DOG_PET_5637 },
{ 5639, 819 }, // Red Dragon { 5639, ePlayerFlag::RED_DRAGON_PET_5639 },
{ 5640, 820 }, // Tortoise { 5640, ePlayerFlag::TORTOISE_PET_5640 },
{ 5641, 821 }, // Green Dragon { 5641, ePlayerFlag::GREEN_DRAGON_PET_5641 },
{ 5643, 822 }, // Panda, see mission 786 { 5643, ePlayerFlag::PANDA_PET_5643 },
{ 5642, 823 }, // Mantis { 5642, ePlayerFlag::MANTIS_PET_5642 },
{ 6720, 824 }, // Warthog { 6720, ePlayerFlag::WARTHOG_PET_6720 },
{ 3520, 825 }, // Lion, see mission 1318 { 3520, ePlayerFlag::LION_PET_3520 },
{ 7638, 826 }, // Goat { 7638, ePlayerFlag::GOAT_PET_7638 },
{ 7694, 827 }, // Crab { 7694, ePlayerFlag::CRAB_PET_7694 },
{ 12294, 829 }, // Reindeer { 12294, ePlayerFlag::REINDEER_PET_12294 },
{ 12431, 830 }, // Stegosaurus, see mission 1386 { 12431, ePlayerFlag::STEGOSAURUS_PET_12431 },
{ 12432, 831 }, // Saber cat, see mission 1389 { 12432, ePlayerFlag::SABER_CAT_PET_12432 },
{ 12433, 832 }, // Gryphon, see mission 1392 { 12433, ePlayerFlag::GRYPHON_PET_12433 },
{ 12434, 833 }, // Alien, see mission 1188 { 12434, ePlayerFlag::ALINE_PET_12334 },
// 834: unknown?, see mission 506, 688 // 834: Bone dragon pet?, see mission 506, 688
{ 16210, 836 }, // Ninjago Earth Dragon, see mission 1836 { 16210, ePlayerFlag::EARTH_DRAGON_PET_16210 },
{ 13067, 838 }, // Skeleton dragon { 13067, ePlayerFlag::SKELETON_DRAGON_PET_13067 },
}; };
PetComponent::PetComponent(Entity* parent, uint32_t componentId): Component(parent) { PetComponent::PetComponent(Entity* parent, uint32_t componentId) : Component(parent) {
m_ComponentId = componentId; m_ComponentId = componentId;
m_Interaction = LWOOBJID_EMPTY; m_Interaction = LWOOBJID_EMPTY;
@@ -85,26 +87,24 @@ PetComponent::PetComponent(Entity* parent, uint32_t componentId): Component(pare
m_MovementAI = nullptr; m_MovementAI = nullptr;
m_TresureTime = 0; m_TresureTime = 0;
m_Preconditions = nullptr; m_Preconditions = nullptr;
m_ImaginationDrainRate = 60.0f;
std::string checkPreconditions = GeneralUtils::UTF16ToWTF8(parent->GetVar<std::u16string>(u"CheckPrecondition")); auto checkPreconditions = GeneralUtils::UTF16ToWTF8(parent->GetVar<std::u16string>(u"CheckPrecondition"));
if (!checkPreconditions.empty()) { if (!checkPreconditions.empty()) {
SetPreconditions(checkPreconditions); SetPreconditions(checkPreconditions);
} }
// Get the imagination drain rate from the CDClient // Get the imagination drain rate from the CDClient
auto query = CDClientDatabase::CreatePreppedStmt("SELECT imaginationDrainRate FROM PetComponent WHERE id = ?;"); auto query = CDClientDatabase::CreatePreppedStmt("SELECT m_ImaginationDrainRate FROM PetComponent WHERE id = ?;");
query.bind(1, static_cast<int>(componentId)); query.bind(1, static_cast<int>(componentId));
auto result = query.execQuery(); auto result = query.execQuery();
// Should a result not exist for this pet default to 60 seconds. // Should a result not exist for this pet default to 60 seconds.
if (!result.eof() && !result.fieldIsNull(0)) { if (result.eof()) return;
imaginationDrainRate = result.getFloatField(0, 60.0f);
} else { m_ImaginationDrainRate = result.getFloatField(0, 60.0f);
imaginationDrainRate = 60.0f;
}
result.finalize();
} }
void PetComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags) { void PetComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags) {
@@ -112,76 +112,63 @@ void PetComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpd
outBitStream->Write1(); // Always serialize as dirty for now outBitStream->Write1(); // Always serialize as dirty for now
outBitStream->Write<uint32_t>(static_cast<unsigned int>(m_Status)); outBitStream->Write<uint32_t>(m_Status);
outBitStream->Write<uint32_t>(static_cast<uint32_t>(tamed ? m_Ability : PetAbilityType::Invalid)); // Something with the overhead icon? outBitStream->Write(tamed ? m_Ability : PetAbilityType::Invalid); // Something with the overhead icon?
const bool interacting = m_Interaction != LWOOBJID_EMPTY; const bool interacting = m_Interaction != LWOOBJID_EMPTY;
outBitStream->Write(interacting); outBitStream->Write(interacting);
if (interacting) { if (interacting) outBitStream->Write(m_Interaction);
outBitStream->Write(m_Interaction);
} outBitStream->Write(tamed);
if (tamed) outBitStream->Write(m_Owner);
if (!bIsInitialUpdate) return;
outBitStream->Write(tamed); outBitStream->Write(tamed);
if (tamed) { if (tamed) {
outBitStream->Write(m_Owner); outBitStream->Write(m_ModerationStatus);
}
if (bIsInitialUpdate) { outBitStream->Write<uint8_t>(m_Name.size());
outBitStream->Write(tamed); for (const auto c : m_Name) {
if (tamed) { outBitStream->Write<char16_t>(c);
outBitStream->Write(m_ModerationStatus); }
const auto nameData = GeneralUtils::UTF8ToUTF16(m_Name); outBitStream->Write<uint8_t>(m_OwnerName.size());
const auto ownerNameData = GeneralUtils::UTF8ToUTF16(m_OwnerName); for (const auto c : m_OwnerName) {
outBitStream->Write<char16_t>(c);
outBitStream->Write(static_cast<uint8_t>(nameData.size()));
for (const auto c : nameData) {
outBitStream->Write(c);
}
outBitStream->Write(static_cast<uint8_t>(ownerNameData.size()));
for (const auto c : ownerNameData) {
outBitStream->Write(c);
}
} }
} }
} }
void PetComponent::OnUse(Entity* originator) { void PetComponent::OnUse(Entity* originator) {
if (m_Owner != LWOOBJID_EMPTY) { if (m_Owner != LWOOBJID_EMPTY) return;
return;
}
if (m_Tamer != LWOOBJID_EMPTY) { if (m_Tamer != LWOOBJID_EMPTY) {
auto* tamer = EntityManager::Instance()->GetEntity(m_Tamer); auto* tamer = EntityManager::Instance()->GetEntity(m_Tamer);
if (tamer != nullptr) { if (tamer) return;
return;
}
m_Tamer = LWOOBJID_EMPTY; m_Tamer = LWOOBJID_EMPTY;
} }
auto* inventoryComponent = originator->GetComponent<InventoryComponent>(); auto* inventoryComponent = originator->GetComponent<InventoryComponent>();
if (inventoryComponent == nullptr) { if (!inventoryComponent) return;
if (m_Preconditions && !m_Preconditions->Check(originator, true)) {
return; return;
} }
if (m_Preconditions != nullptr && !m_Preconditions->Check(originator, true)) { auto* movementAIComponent = m_ParentEntity->GetComponent<MovementAIComponent>();
return;
}
auto* movementAIComponent = m_Parent->GetComponent<MovementAIComponent>(); if (movementAIComponent) {
if (movementAIComponent != nullptr) {
movementAIComponent->Stop(); movementAIComponent->Stop();
} }
inventoryComponent->DespawnPet(); inventoryComponent->DespawnPet();
const auto& cached = buildCache.find(m_Parent->GetLOT()); const auto& cached = buildCache.find(m_ParentEntity->GetLOT());
int32_t imaginationCost = 0; int32_t imaginationCost = 0;
std::string buildFile; std::string buildFile;
@@ -189,7 +176,7 @@ void PetComponent::OnUse(Entity* originator) {
if (cached == buildCache.end()) { if (cached == buildCache.end()) {
auto query = CDClientDatabase::CreatePreppedStmt( auto query = CDClientDatabase::CreatePreppedStmt(
"SELECT ValidPiecesLXF, PuzzleModelLot, Timelimit, NumValidPieces, imagCostPerBuild FROM TamingBuildPuzzles WHERE NPCLot = ?;"); "SELECT ValidPiecesLXF, PuzzleModelLot, Timelimit, NumValidPieces, imagCostPerBuild FROM TamingBuildPuzzles WHERE NPCLot = ?;");
query.bind(1, (int)m_Parent->GetLOT()); query.bind(1, static_cast<int32_t>(m_ParentEntity->GetLOT()));
auto result = query.execQuery(); auto result = query.execQuery();
@@ -199,11 +186,7 @@ void PetComponent::OnUse(Entity* originator) {
return; return;
} }
if (result.fieldIsNull(0)) { if (result.fieldIsNull(0)) return;
result.finalize();
return;
}
buildFile = std::string(result.getStringField(0)); buildFile = std::string(result.getStringField(0));
@@ -216,9 +199,7 @@ void PetComponent::OnUse(Entity* originator) {
if (data.timeLimit <= 0) data.timeLimit = 60; if (data.timeLimit <= 0) data.timeLimit = 60;
imaginationCost = data.imaginationCost; imaginationCost = data.imaginationCost;
buildCache[m_Parent->GetLOT()] = data; buildCache[m_ParentEntity->GetLOT()] = data;
result.finalize();
} else { } else {
buildFile = cached->second.buildFile; buildFile = cached->second.buildFile;
imaginationCost = cached->second.imaginationCost; imaginationCost = cached->second.imaginationCost;
@@ -226,15 +207,11 @@ void PetComponent::OnUse(Entity* originator) {
auto* destroyableComponent = originator->GetComponent<DestroyableComponent>(); auto* destroyableComponent = originator->GetComponent<DestroyableComponent>();
if (destroyableComponent == nullptr) { if (!destroyableComponent) return;
return;
}
auto imagination = destroyableComponent->GetImagination(); auto imagination = destroyableComponent->GetImagination();
if (imagination < imaginationCost) { if (imagination < imaginationCost) return;
return;
}
auto& bricks = BrickDatabase::Instance()->GetBricks(buildFile); auto& bricks = BrickDatabase::Instance()->GetBricks(buildFile);
@@ -245,13 +222,13 @@ void PetComponent::OnUse(Entity* originator) {
return; return;
} }
auto petPosition = m_Parent->GetPosition(); auto petPosition = m_ParentEntity->GetPosition();
auto originatorPosition = originator->GetPosition(); auto originatorPosition = originator->GetPosition();
m_Parent->SetRotation(NiQuaternion::LookAt(petPosition, originatorPosition)); m_ParentEntity->SetRotation(NiQuaternion::LookAt(petPosition, originatorPosition));
float interactionDistance = m_Parent->GetVar<float>(u"interaction_distance"); float interactionDistance = m_ParentEntity->GetVar<float>(u"interaction_distance");
if (interactionDistance <= 0) { if (interactionDistance <= 0) {
interactionDistance = 15; interactionDistance = 15;
@@ -259,8 +236,8 @@ void PetComponent::OnUse(Entity* originator) {
auto position = originatorPosition; auto position = originatorPosition;
NiPoint3 forward = NiQuaternion::LookAt(m_Parent->GetPosition(), originator->GetPosition()).GetForwardVector(); NiPoint3 forward = NiQuaternion::LookAt(m_ParentEntity->GetPosition(), originator->GetPosition()).GetForwardVector();
forward.y = 0; forward.y = 0.0f;
if (dpWorld::Instance().IsLoaded()) { if (dpWorld::Instance().IsLoaded()) {
NiPoint3 attempt = petPosition + forward * interactionDistance; NiPoint3 attempt = petPosition + forward * interactionDistance;
@@ -268,7 +245,7 @@ void PetComponent::OnUse(Entity* originator) {
float y = dpWorld::Instance().GetNavMesh()->GetHeightAtPoint(attempt); float y = dpWorld::Instance().GetNavMesh()->GetHeightAtPoint(attempt);
while (std::abs(y - petPosition.y) > 4 && interactionDistance > 10) { while (std::abs(y - petPosition.y) > 4 && interactionDistance > 10) {
const NiPoint3 forward = m_Parent->GetRotation().GetForwardVector(); const NiPoint3 forward = m_ParentEntity->GetRotation().GetForwardVector();
attempt = originatorPosition + forward * interactionDistance; attempt = originatorPosition + forward * interactionDistance;
@@ -287,7 +264,7 @@ void PetComponent::OnUse(Entity* originator) {
GameMessages::SendNotifyPetTamingMinigame( GameMessages::SendNotifyPetTamingMinigame(
originator->GetObjectID(), originator->GetObjectID(),
m_Parent->GetObjectID(), m_ParentEntity->GetObjectID(),
LWOOBJID_EMPTY, LWOOBJID_EMPTY,
true, true,
ePetTamingNotifyType::BEGIN, ePetTamingNotifyType::BEGIN,
@@ -298,7 +275,7 @@ void PetComponent::OnUse(Entity* originator) {
); );
GameMessages::SendNotifyPetTamingMinigame( GameMessages::SendNotifyPetTamingMinigame(
m_Parent->GetObjectID(), m_ParentEntity->GetObjectID(),
LWOOBJID_EMPTY, LWOOBJID_EMPTY,
originator->GetObjectID(), originator->GetObjectID(),
true, true,
@@ -314,17 +291,16 @@ void PetComponent::OnUse(Entity* originator) {
m_Tamer = originator->GetObjectID(); m_Tamer = originator->GetObjectID();
SetStatus(5); SetStatus(5);
currentActivities.insert_or_assign(m_Tamer, m_Parent->GetObjectID()); currentActivities.insert_or_assign(m_Tamer, m_ParentEntity->GetObjectID());
// Notify the start of a pet taming minigame // Notify the start of a pet taming minigame
for (CppScripts::Script* script : CppScripts::GetEntityScripts(m_Parent)) { m_ParentEntity->GetScript()->OnNotifyPetTamingMinigame(m_ParentEntity, originator, ePetTamingNotifyType::BEGIN);
script->OnNotifyPetTamingMinigame(m_Parent, originator, ePetTamingNotifyType::BEGIN);
}
} }
void PetComponent::Update(float deltaTime) { void PetComponent::Update(float deltaTime) {
if (m_StartPosition == NiPoint3::ZERO) { if (m_StartPosition == NiPoint3::ZERO) {
m_StartPosition = m_Parent->GetPosition(); m_StartPosition = m_ParentEntity->GetPosition();
} }
if (m_Owner == LWOOBJID_EMPTY) { if (m_Owner == LWOOBJID_EMPTY) {
@@ -344,7 +320,7 @@ void PetComponent::Update(float deltaTime) {
if (m_Timer <= 0) { if (m_Timer <= 0) {
Wander(); Wander();
EntityManager::Instance()->SerializeEntity(m_Parent); EntityManager::Instance()->SerializeEntity(m_ParentEntity);
} }
} else { } else {
m_Timer = 5; m_Timer = 5;
@@ -356,22 +332,21 @@ void PetComponent::Update(float deltaTime) {
auto* owner = GetOwner(); auto* owner = GetOwner();
if (owner == nullptr) { if (!owner) {
m_Parent->Kill(); m_ParentEntity->Kill();
return; return;
} }
m_MovementAI = m_Parent->GetComponent<MovementAIComponent>(); if (!m_MovementAI) {
m_MovementAI = m_ParentEntity->GetComponent<MovementAIComponent>();
if (m_MovementAI == nullptr) { if (!m_MovementAI) return;
return;
} }
if (m_TresureTime > 0) { if (m_TresureTime > 0) {
auto* tresure = EntityManager::Instance()->GetEntity(m_Interaction); auto* tresure = EntityManager::Instance()->GetEntity(m_Interaction);
if (tresure == nullptr) { if (!tresure) {
m_TresureTime = 0; m_TresureTime = 0;
return; return;
@@ -382,9 +357,9 @@ void PetComponent::Update(float deltaTime) {
m_MovementAI->Stop(); m_MovementAI->Stop();
if (m_TresureTime <= 0) { if (m_TresureTime <= 0) {
m_Parent->SetOwnerOverride(m_Owner); m_ParentEntity->SetOwnerOverride(m_Owner);
tresure->Smash(m_Parent->GetObjectID()); tresure->Smash(m_ParentEntity->GetObjectID());
m_Interaction = LWOOBJID_EMPTY; m_Interaction = LWOOBJID_EMPTY;
@@ -420,17 +395,17 @@ void PetComponent::Update(float deltaTime) {
return; return;
} }
SwitchComponent* closestSwitch = SwitchComponent::GetClosestSwitch(position); auto* closestSwitch = SwitchComponent::GetClosestSwitch(position);
float haltDistance = 5; float haltDistance = 5;
if (closestSwitch != nullptr) { if (closestSwitch) {
if (!closestSwitch->GetActive()) { if (!closestSwitch->GetActive()) {
NiPoint3 switchPosition = closestSwitch->GetParentEntity()->GetPosition(); NiPoint3 switchPosition = closestSwitch->GetParentEntity()->GetPosition();
float distance = Vector3::DistanceSquared(position, switchPosition); float distance = Vector3::DistanceSquared(position, switchPosition);
if (distance < 3 * 3) { if (distance < 3 * 3) {
m_Interaction = closestSwitch->GetParentEntity()->GetObjectID(); m_Interaction = closestSwitch->GetParentEntity()->GetObjectID();
closestSwitch->EntityEnter(m_Parent); closestSwitch->EntityEnter(m_ParentEntity);
} else if (distance < 20 * 20) { } else if (distance < 20 * 20) {
haltDistance = 1; haltDistance = 1;
@@ -441,9 +416,9 @@ void PetComponent::Update(float deltaTime) {
Entity* closestTresure = PetDigServer::GetClosestTresure(position); Entity* closestTresure = PetDigServer::GetClosestTresure(position);
if (closestTresure != nullptr) { if (closestTresure) {
// Skeleton Dragon Pat special case for bone digging // Skeleton Dragon Pat special case for bone digging
if (closestTresure->GetLOT() == 12192 && m_Parent->GetLOT() != 13067) { if (closestTresure->GetLOT() == 12192 && m_ParentEntity->GetLOT() != 13067) {
goto skipTresure; goto skipTresure;
} }
@@ -470,7 +445,7 @@ skipTresure:
m_MovementAI->SetDestination(destination); m_MovementAI->SetDestination(destination);
m_Timer = 1; m_Timer = 1.0f;
} }
void PetComponent::TryBuild(uint32_t numBricks, bool clientFailed) { void PetComponent::TryBuild(uint32_t numBricks, bool clientFailed) {
@@ -478,19 +453,19 @@ void PetComponent::TryBuild(uint32_t numBricks, bool clientFailed) {
auto* tamer = EntityManager::Instance()->GetEntity(m_Tamer); auto* tamer = EntityManager::Instance()->GetEntity(m_Tamer);
if (tamer == nullptr) { if (!tamer) {
m_Tamer = LWOOBJID_EMPTY; m_Tamer = LWOOBJID_EMPTY;
return; return;
} }
const auto& cached = buildCache.find(m_Parent->GetLOT()); const auto& cached = buildCache.find(m_ParentEntity->GetLOT());
if (cached == buildCache.end()) return; if (cached == buildCache.end()) return;
auto* destroyableComponent = tamer->GetComponent<DestroyableComponent>(); auto* destroyableComponent = tamer->GetComponent<DestroyableComponent>();
if (destroyableComponent == nullptr) return; if (!destroyableComponent) return;
auto imagination = destroyableComponent->GetImagination(); auto imagination = destroyableComponent->GetImagination();
@@ -518,13 +493,13 @@ void PetComponent::NotifyTamingBuildSuccess(NiPoint3 position) {
auto* tamer = EntityManager::Instance()->GetEntity(m_Tamer); auto* tamer = EntityManager::Instance()->GetEntity(m_Tamer);
if (tamer == nullptr) { if (!tamer) {
m_Tamer = LWOOBJID_EMPTY; m_Tamer = LWOOBJID_EMPTY;
return; return;
} }
const auto& cached = buildCache.find(m_Parent->GetLOT()); const auto& cached = buildCache.find(m_ParentEntity->GetLOT());
if (cached == buildCache.end()) { if (cached == buildCache.end()) {
return; return;
@@ -547,13 +522,11 @@ void PetComponent::NotifyTamingBuildSuccess(NiPoint3 position) {
GameMessages::SendNotifyTamingModelLoadedOnServer(m_Tamer, tamer->GetSystemAddress()); GameMessages::SendNotifyTamingModelLoadedOnServer(m_Tamer, tamer->GetSystemAddress());
GameMessages::SendPetResponse(m_Tamer, m_Parent->GetObjectID(), 0, 10, 0, tamer->GetSystemAddress()); GameMessages::SendPetResponse(m_Tamer, m_ParentEntity->GetObjectID(), 0, 10, 0, tamer->GetSystemAddress());
auto* inventoryComponent = tamer->GetComponent<InventoryComponent>(); auto* inventoryComponent = tamer->GetComponent<InventoryComponent>();
if (inventoryComponent == nullptr) { if (!inventoryComponent) return;
return;
}
LWOOBJID petSubKey = ObjectIDManager::Instance()->GenerateRandomObjectID(); LWOOBJID petSubKey = ObjectIDManager::Instance()->GenerateRandomObjectID();
@@ -565,22 +538,20 @@ void PetComponent::NotifyTamingBuildSuccess(NiPoint3 position) {
std::string petName = tamer->GetCharacter()->GetName(); std::string petName = tamer->GetCharacter()->GetName();
petName += "'s Pet"; petName += "'s Pet";
GameMessages::SendAddPetToPlayer(m_Tamer, 0, GeneralUtils::UTF8ToUTF16(petName), petSubKey, m_Parent->GetLOT(), tamer->GetSystemAddress()); GameMessages::SendAddPetToPlayer(m_Tamer, 0, GeneralUtils::UTF8ToUTF16(petName), petSubKey, m_ParentEntity->GetLOT(), tamer->GetSystemAddress());
GameMessages::SendRegisterPetID(m_Tamer, m_Parent->GetObjectID(), tamer->GetSystemAddress()); GameMessages::SendRegisterPetID(m_Tamer, m_ParentEntity->GetObjectID(), tamer->GetSystemAddress());
GameMessages::SendRegisterPetDBID(m_Tamer, petSubKey, tamer->GetSystemAddress()); GameMessages::SendRegisterPetDBID(m_Tamer, petSubKey, tamer->GetSystemAddress());
inventoryComponent->AddItem(m_Parent->GetLOT(), 1, eLootSourceType::ACTIVITY, eInventoryType::MODELS, {}, LWOOBJID_EMPTY, true, false, petSubKey); inventoryComponent->AddItem(m_ParentEntity->GetLOT(), 1, eLootSourceType::ACTIVITY, eInventoryType::MODELS, {}, LWOOBJID_EMPTY, true, false, petSubKey);
auto* item = inventoryComponent->FindItemBySubKey(petSubKey, MODELS); auto* item = inventoryComponent->FindItemBySubKey(petSubKey, eInventoryType::MODELS);
if (item == nullptr) { if (!item) return;
return;
}
DatabasePet databasePet{}; DatabasePet databasePet{};
databasePet.lot = m_Parent->GetLOT(); databasePet.lot = m_ParentEntity->GetLOT();
databasePet.moderationState = 1; databasePet.moderationState = 1;
databasePet.name = petName; databasePet.name = petName;
@@ -602,21 +573,22 @@ void PetComponent::NotifyTamingBuildSuccess(NiPoint3 position) {
UNASSIGNED_SYSTEM_ADDRESS UNASSIGNED_SYSTEM_ADDRESS
); );
auto petFlag = petFlags.find(m_ParentEntity->GetLOT());
// Triggers the catch a pet missions // Triggers the catch a pet missions
if (petFlags.find(m_Parent->GetLOT()) != petFlags.end()) { if (petFlag != petFlags.end()) {
tamer->GetCharacter()->SetPlayerFlag(petFlags.at(m_Parent->GetLOT()), true); tamer->GetCharacter()->SetPlayerFlag(petFlag->second, true);
} }
auto* missionComponent = tamer->GetComponent<MissionComponent>(); auto* missionComponent = tamer->GetComponent<MissionComponent>();
if (missionComponent != nullptr) { if (missionComponent) {
missionComponent->Progress(eMissionTaskType::PET_TAMING, m_Parent->GetLOT()); missionComponent->Progress(eMissionTaskType::PET_TAMING, m_ParentEntity->GetLOT());
} }
SetStatus(1); SetStatus(1);
auto* characterComponent = tamer->GetComponent<CharacterComponent>(); auto* characterComponent = tamer->GetComponent<CharacterComponent>();
if (characterComponent != nullptr) { if (characterComponent) {
characterComponent->UpdatePlayerStatistic(PetsTamed); characterComponent->UpdatePlayerStatistic(PetsTamed);
} }
} }
@@ -641,7 +613,7 @@ void PetComponent::RequestSetPetName(std::u16string name) {
auto* tamer = EntityManager::Instance()->GetEntity(m_Tamer); auto* tamer = EntityManager::Instance()->GetEntity(m_Tamer);
if (tamer == nullptr) { if (!tamer) {
m_Tamer = LWOOBJID_EMPTY; m_Tamer = LWOOBJID_EMPTY;
return; return;
@@ -651,9 +623,7 @@ void PetComponent::RequestSetPetName(std::u16string name) {
auto* inventoryComponent = tamer->GetComponent<InventoryComponent>(); auto* inventoryComponent = tamer->GetComponent<InventoryComponent>();
if (inventoryComponent == nullptr) { if (!inventoryComponent) return;
return;
}
m_ModerationStatus = 1; // Pending m_ModerationStatus = 1; // Pending
m_Name = ""; m_Name = "";
@@ -661,18 +631,18 @@ void PetComponent::RequestSetPetName(std::u16string name) {
//Save our pet's new name to the db: //Save our pet's new name to the db:
SetPetNameForModeration(GeneralUtils::UTF16ToWTF8(name)); SetPetNameForModeration(GeneralUtils::UTF16ToWTF8(name));
EntityManager::Instance()->SerializeEntity(m_Parent); EntityManager::Instance()->SerializeEntity(m_ParentEntity);
std::u16string u16name = GeneralUtils::UTF8ToUTF16(m_Name); std::u16string u16name = GeneralUtils::UTF8ToUTF16(m_Name);
std::u16string u16ownerName = GeneralUtils::UTF8ToUTF16(m_OwnerName); std::u16string u16ownerName = GeneralUtils::UTF8ToUTF16(m_OwnerName);
GameMessages::SendSetPetName(m_Tamer, u16name, m_DatabaseId, tamer->GetSystemAddress()); GameMessages::SendSetPetName(m_Tamer, u16name, m_DatabaseId, tamer->GetSystemAddress());
GameMessages::SendSetPetName(m_Tamer, u16name, LWOOBJID_EMPTY, tamer->GetSystemAddress()); GameMessages::SendSetPetName(m_Tamer, u16name, LWOOBJID_EMPTY, tamer->GetSystemAddress());
GameMessages::SendPetNameChanged(m_Parent->GetObjectID(), m_ModerationStatus, u16name, u16ownerName, UNASSIGNED_SYSTEM_ADDRESS); GameMessages::SendPetNameChanged(m_ParentEntity->GetObjectID(), m_ModerationStatus, u16name, u16ownerName, UNASSIGNED_SYSTEM_ADDRESS);
GameMessages::SendSetPetNameModerated(m_Tamer, m_DatabaseId, m_ModerationStatus, tamer->GetSystemAddress()); GameMessages::SendSetPetNameModerated(m_Tamer, m_DatabaseId, m_ModerationStatus, tamer->GetSystemAddress());
GameMessages::SendNotifyPetTamingMinigame( GameMessages::SendNotifyPetTamingMinigame(
m_Tamer, m_Tamer,
m_Parent->GetObjectID(), m_ParentEntity->GetObjectID(),
m_Tamer, m_Tamer,
false, false,
ePetTamingNotifyType::SUCCESS, ePetTamingNotifyType::SUCCESS,
@@ -682,22 +652,19 @@ void PetComponent::RequestSetPetName(std::u16string name) {
UNASSIGNED_SYSTEM_ADDRESS UNASSIGNED_SYSTEM_ADDRESS
); );
GameMessages::SendTerminateInteraction(m_Tamer, eTerminateType::FROM_INTERACTION, m_Parent->GetObjectID()); GameMessages::SendTerminateInteraction(m_Tamer, eTerminateType::FROM_INTERACTION, m_ParentEntity->GetObjectID());
auto* modelEntity = EntityManager::Instance()->GetEntity(m_ModelId); auto* modelEntity = EntityManager::Instance()->GetEntity(m_ModelId);
if (modelEntity != nullptr) { if (modelEntity) modelEntity->Smash(m_Tamer);
modelEntity->Smash(m_Tamer);
}
currentActivities.erase(m_Tamer); currentActivities.erase(m_Tamer);
m_Tamer = LWOOBJID_EMPTY; m_Tamer = LWOOBJID_EMPTY;
// Notify the end of a pet taming minigame // Notify the end of a pet taming minigame
for (CppScripts::Script* script : CppScripts::GetEntityScripts(m_Parent)) { m_ParentEntity->GetScript()->OnNotifyPetTamingMinigame(m_ParentEntity, tamer, ePetTamingNotifyType::SUCCESS);
script->OnNotifyPetTamingMinigame(m_Parent, tamer, ePetTamingNotifyType::SUCCESS);
}
} }
void PetComponent::ClientExitTamingMinigame(bool voluntaryExit) { void PetComponent::ClientExitTamingMinigame(bool voluntaryExit) {
@@ -705,7 +672,7 @@ void PetComponent::ClientExitTamingMinigame(bool voluntaryExit) {
auto* tamer = EntityManager::Instance()->GetEntity(m_Tamer); auto* tamer = EntityManager::Instance()->GetEntity(m_Tamer);
if (tamer == nullptr) { if (!tamer) {
m_Tamer = LWOOBJID_EMPTY; m_Tamer = LWOOBJID_EMPTY;
return; return;
@@ -713,7 +680,7 @@ void PetComponent::ClientExitTamingMinigame(bool voluntaryExit) {
GameMessages::SendNotifyPetTamingMinigame( GameMessages::SendNotifyPetTamingMinigame(
m_Tamer, m_Tamer,
m_Parent->GetObjectID(), m_ParentEntity->GetObjectID(),
m_Tamer, m_Tamer,
false, false,
ePetTamingNotifyType::QUIT, ePetTamingNotifyType::QUIT,
@@ -725,7 +692,7 @@ void PetComponent::ClientExitTamingMinigame(bool voluntaryExit) {
GameMessages::SendNotifyTamingModelLoadedOnServer(m_Tamer, tamer->GetSystemAddress()); GameMessages::SendNotifyTamingModelLoadedOnServer(m_Tamer, tamer->GetSystemAddress());
GameMessages::SendTerminateInteraction(m_Tamer, eTerminateType::FROM_INTERACTION, m_Parent->GetObjectID()); GameMessages::SendTerminateInteraction(m_Tamer, eTerminateType::FROM_INTERACTION, m_ParentEntity->GetObjectID());
currentActivities.erase(m_Tamer); currentActivities.erase(m_Tamer);
@@ -733,22 +700,17 @@ void PetComponent::ClientExitTamingMinigame(bool voluntaryExit) {
m_Tamer = LWOOBJID_EMPTY; m_Tamer = LWOOBJID_EMPTY;
m_Timer = 0; m_Timer = 0;
EntityManager::Instance()->SerializeEntity(m_Parent); EntityManager::Instance()->SerializeEntity(m_ParentEntity);
// Notify the end of a pet taming minigame // Notify the end of a pet taming minigame
for (CppScripts::Script* script : CppScripts::GetEntityScripts(m_Parent)) { m_ParentEntity->GetScript()->OnNotifyPetTamingMinigame(m_ParentEntity, tamer, ePetTamingNotifyType::QUIT);
script->OnNotifyPetTamingMinigame(m_Parent, tamer, ePetTamingNotifyType::QUIT);
}
} }
void PetComponent::StartTimer() { void PetComponent::StartTimer() {
const auto& cached = buildCache.find(m_Parent->GetLOT()); const auto& cached = buildCache.find(m_ParentEntity->GetLOT());
if (cached == buildCache.end()) { if (cached != buildCache.end()) m_Timer = cached->second.timeLimit;
return;
}
m_Timer = cached->second.timeLimit;
} }
void PetComponent::ClientFailTamingMinigame() { void PetComponent::ClientFailTamingMinigame() {
@@ -756,7 +718,7 @@ void PetComponent::ClientFailTamingMinigame() {
auto* tamer = EntityManager::Instance()->GetEntity(m_Tamer); auto* tamer = EntityManager::Instance()->GetEntity(m_Tamer);
if (tamer == nullptr) { if (!tamer) {
m_Tamer = LWOOBJID_EMPTY; m_Tamer = LWOOBJID_EMPTY;
return; return;
@@ -764,7 +726,7 @@ void PetComponent::ClientFailTamingMinigame() {
GameMessages::SendNotifyPetTamingMinigame( GameMessages::SendNotifyPetTamingMinigame(
m_Tamer, m_Tamer,
m_Parent->GetObjectID(), m_ParentEntity->GetObjectID(),
m_Tamer, m_Tamer,
false, false,
ePetTamingNotifyType::FAILED, ePetTamingNotifyType::FAILED,
@@ -776,7 +738,7 @@ void PetComponent::ClientFailTamingMinigame() {
GameMessages::SendNotifyTamingModelLoadedOnServer(m_Tamer, tamer->GetSystemAddress()); GameMessages::SendNotifyTamingModelLoadedOnServer(m_Tamer, tamer->GetSystemAddress());
GameMessages::SendTerminateInteraction(m_Tamer, eTerminateType::FROM_INTERACTION, m_Parent->GetObjectID()); GameMessages::SendTerminateInteraction(m_Tamer, eTerminateType::FROM_INTERACTION, m_ParentEntity->GetObjectID());
currentActivities.erase(m_Tamer); currentActivities.erase(m_Tamer);
@@ -784,18 +746,17 @@ void PetComponent::ClientFailTamingMinigame() {
m_Tamer = LWOOBJID_EMPTY; m_Tamer = LWOOBJID_EMPTY;
m_Timer = 0; m_Timer = 0;
EntityManager::Instance()->SerializeEntity(m_Parent); EntityManager::Instance()->SerializeEntity(m_ParentEntity);
// Notify the end of a pet taming minigame // Notify the end of a pet taming minigame
for (CppScripts::Script* script : CppScripts::GetEntityScripts(m_Parent)) { m_ParentEntity->GetScript()->OnNotifyPetTamingMinigame(m_ParentEntity, tamer, ePetTamingNotifyType::FAILED);
script->OnNotifyPetTamingMinigame(m_Parent, tamer, ePetTamingNotifyType::FAILED);
}
} }
void PetComponent::Wander() { void PetComponent::Wander() {
m_MovementAI = m_Parent->GetComponent<MovementAIComponent>(); if (!m_MovementAI) m_MovementAI = m_ParentEntity->GetComponent<MovementAIComponent>();
if (m_MovementAI == nullptr || !m_MovementAI->AtFinalWaypoint()) { if (!m_MovementAI || !m_MovementAI->AtFinalWaypoint()) {
return; return;
} }
@@ -803,18 +764,17 @@ void PetComponent::Wander() {
const auto& info = m_MovementAI->GetInfo(); const auto& info = m_MovementAI->GetInfo();
const auto div = static_cast<int>(info.wanderDelayMax); const auto div = static_cast<int32_t>(info.wanderDelayMax);
m_Timer = (div == 0 ? 0 : GeneralUtils::GenerateRandomNumber<int>(0, div)) + info.wanderDelayMin; //set a random timer to stay put. m_Timer = (div == 0 ? 0 : GeneralUtils::GenerateRandomNumber<int32_t>(0, div)) + info.wanderDelayMin; //set a random timer to stay put.
const float radius = info.wanderRadius * sqrt(static_cast<double>(GeneralUtils::GenerateRandomNumber<float>(0, 1))); //our wander radius + a bit of random range const float radius = info.wanderRadius * sqrt(GeneralUtils::GenerateRandomNumber<float>(0.0f, 1.0f)); //our wander radius + a bit of random range
const float theta = ((static_cast<double>(GeneralUtils::GenerateRandomNumber<float>(0, 1)) * 2 * PI)); const float theta = GeneralUtils::GenerateRandomNumber<float>(0.0f, 1.0f) * 2.0f * PI;
const NiPoint3 delta = const NiPoint3 delta = NiPoint3(
{
radius * cos(theta), radius * cos(theta),
0, 0,
radius * sin(theta) radius * sin(theta)
}; );
auto destination = m_StartPosition + delta; auto destination = m_StartPosition + delta;
@@ -841,17 +801,17 @@ void PetComponent::Activate(Item* item, bool registerPet, bool fromTaming) {
m_ItemId = item->GetId(); m_ItemId = item->GetId();
m_DatabaseId = item->GetSubKey(); m_DatabaseId = item->GetSubKey();
auto* inventoryComponent = item->GetInventory()->GetComponent(); auto inventoryComponent = item->GetInventory()->GetComponent();
if (inventoryComponent == nullptr) return; if (!inventoryComponent) return;
inventoryComponent->DespawnPet(); inventoryComponent->DespawnPet();
m_Owner = inventoryComponent->GetParent()->GetObjectID(); m_Owner = inventoryComponent->GetParentEntity()->GetObjectID();
auto* owner = GetOwner(); auto* owner = GetOwner();
if (owner == nullptr) return; if (!owner) return;
SetStatus(1); SetStatus(1);
auto databaseData = inventoryComponent->GetDatabasePet(m_DatabaseId); auto databaseData = inventoryComponent->GetDatabasePet(m_DatabaseId);
@@ -883,18 +843,18 @@ void PetComponent::Activate(Item* item, bool registerPet, bool fromTaming) {
GameMessages::SendMarkInventoryItemAsActive(m_Owner, true, eUnequippableActiveType::PET, m_ItemId, GetOwner()->GetSystemAddress()); GameMessages::SendMarkInventoryItemAsActive(m_Owner, true, eUnequippableActiveType::PET, m_ItemId, GetOwner()->GetSystemAddress());
activePets[m_Owner] = m_Parent->GetObjectID(); activePets[m_Owner] = m_ParentEntity->GetObjectID();
m_Timer = 3; m_Timer = 3;
EntityManager::Instance()->SerializeEntity(m_Parent); EntityManager::Instance()->SerializeEntity(m_ParentEntity);
owner->GetCharacter()->SetPlayerFlag(ePlayerFlag::FIRST_MANUAL_PET_HIBERNATE, true); owner->GetCharacter()->SetPlayerFlag(ePlayerFlag::FIRST_MANUAL_PET_HIBERNATE, true);
if (registerPet) { if (registerPet) {
GameMessages::SendAddPetToPlayer(m_Owner, 0, GeneralUtils::UTF8ToUTF16(m_Name), m_DatabaseId, m_Parent->GetLOT(), owner->GetSystemAddress()); GameMessages::SendAddPetToPlayer(m_Owner, 0, GeneralUtils::UTF8ToUTF16(m_Name), m_DatabaseId, m_ParentEntity->GetLOT(), owner->GetSystemAddress());
GameMessages::SendRegisterPetID(m_Owner, m_Parent->GetObjectID(), owner->GetSystemAddress()); GameMessages::SendRegisterPetID(m_Owner, m_ParentEntity->GetObjectID(), owner->GetSystemAddress());
GameMessages::SendRegisterPetDBID(m_Owner, m_DatabaseId, owner->GetSystemAddress()); GameMessages::SendRegisterPetDBID(m_Owner, m_DatabaseId, owner->GetSystemAddress());
} }
@@ -911,47 +871,47 @@ void PetComponent::AddDrainImaginationTimer(Item* item, bool fromTaming) {
auto playerInventoryComponent = playerInventory->GetComponent(); auto playerInventoryComponent = playerInventory->GetComponent();
if (!playerInventoryComponent) return; if (!playerInventoryComponent) return;
auto playerEntity = playerInventoryComponent->GetParent(); auto playerEntity = playerInventoryComponent->GetParentEntity();
if (!playerEntity) return; if (!playerEntity) return;
auto playerDestroyableComponent = playerEntity->GetComponent<DestroyableComponent>(); auto* playerDestroyableComponent = playerEntity->GetComponent<DestroyableComponent>();
if (!playerDestroyableComponent) return; if (!playerDestroyableComponent) return;
// Drain by 1 when you summon pet or when this method is called, but not when we have just tamed this pet. // Drain by 1 when you summon pet or when this method is called, but not when we have just tamed this pet.
if (!fromTaming) playerDestroyableComponent->Imagine(-1); if (!fromTaming) playerDestroyableComponent->Imagine(-1);
// Set this to a variable so when this is called back from the player the timer doesn't fire off. // Set this to a variable so when this is called back from the player the timer doesn't fire off.
m_Parent->AddCallbackTimer(imaginationDrainRate, [playerDestroyableComponent, this, item]() { m_ParentEntity->AddCallbackTimer(m_ImaginationDrainRate, [playerDestroyableComponent, this, item]() {
if (!playerDestroyableComponent) { if (!playerDestroyableComponent) {
Game::logger->Log("PetComponent", "No petComponent and/or no playerDestroyableComponent"); Game::logger->Log("PetComponent", "No petComponent and/or no playerDestroyableComponent");
return; return;
} }
// If we are out of imagination despawn the pet. // If we are out of imagination despawn the pet.
if (playerDestroyableComponent->GetImagination() == 0) { if (playerDestroyableComponent->GetImagination() == 0) {
this->Deactivate(); this->Deactivate();
auto playerEntity = playerDestroyableComponent->GetParent(); auto playerEntity = playerDestroyableComponent->GetParentEntity();
if (!playerEntity) return; if (!playerEntity) return;
GameMessages::SendUseItemRequirementsResponse(playerEntity->GetObjectID(), playerEntity->GetSystemAddress(), eUseItemResponse::NoImaginationForPet); GameMessages::SendUseItemRequirementsResponse(playerEntity->GetObjectID(), playerEntity->GetSystemAddress(), eUseItemResponse::NoImaginationForPet);
} }
this->AddDrainImaginationTimer(item); this->AddDrainImaginationTimer(item);
}); });
} }
void PetComponent::Deactivate() { void PetComponent::Deactivate() {
GameMessages::SendPlayFXEffect(m_Parent->GetObjectID(), -1, u"despawn", "", LWOOBJID_EMPTY, 1, 1, true); GameMessages::SendPlayFXEffect(m_ParentEntity->GetObjectID(), -1, u"despawn", "", LWOOBJID_EMPTY, 1, 1, true);
GameMessages::SendMarkInventoryItemAsActive(m_Owner, false, eUnequippableActiveType::PET, m_ItemId, GetOwner()->GetSystemAddress()); GameMessages::SendMarkInventoryItemAsActive(m_Owner, false, eUnequippableActiveType::PET, m_ItemId, GetOwner()->GetSystemAddress());
activePets.erase(m_Owner); activePets.erase(m_Owner);
m_Parent->Kill(); m_ParentEntity->Kill();
auto* owner = GetOwner(); auto* owner = GetOwner();
if (owner == nullptr) return; if (!owner) return;
GameMessages::SendAddPetToPlayer(m_Owner, 0, u"", LWOOBJID_EMPTY, LOT_NULL, owner->GetSystemAddress()); GameMessages::SendAddPetToPlayer(m_Owner, 0, u"", LWOOBJID_EMPTY, LOT_NULL, owner->GetSystemAddress());
@@ -965,9 +925,7 @@ void PetComponent::Deactivate() {
void PetComponent::Release() { void PetComponent::Release() {
auto* inventoryComponent = GetOwner()->GetComponent<InventoryComponent>(); auto* inventoryComponent = GetOwner()->GetComponent<InventoryComponent>();
if (inventoryComponent == nullptr) { if (!inventoryComponent) return;
return;
}
Deactivate(); Deactivate();
@@ -981,13 +939,11 @@ void PetComponent::Release() {
void PetComponent::Command(NiPoint3 position, LWOOBJID source, int32_t commandType, int32_t typeId, bool overrideObey) { void PetComponent::Command(NiPoint3 position, LWOOBJID source, int32_t commandType, int32_t typeId, bool overrideObey) {
auto* owner = GetOwner(); auto* owner = GetOwner();
if (owner == nullptr) { if (!owner) return;
return;
}
if (commandType == 1) { if (commandType == 1) {
// Emotes // Emotes
GameMessages::SendPlayEmote(m_Parent->GetObjectID(), typeId, owner->GetObjectID(), UNASSIGNED_SYSTEM_ADDRESS); GameMessages::SendPlayEmote(m_ParentEntity->GetObjectID(), typeId, owner->GetObjectID(), UNASSIGNED_SYSTEM_ADDRESS);
} else if (commandType == 3) { } else if (commandType == 3) {
// Follow me, ??? // Follow me, ???
} else if (commandType == 6) { } else if (commandType == 6) {
@@ -995,51 +951,15 @@ void PetComponent::Command(NiPoint3 position, LWOOBJID source, int32_t commandTy
} }
if (owner->GetGMLevel() >= eGameMasterLevel::DEVELOPER) { if (owner->GetGMLevel() >= eGameMasterLevel::DEVELOPER) {
ChatPackets::SendSystemMessage(owner->GetSystemAddress(), u"Commmand Type: " + (GeneralUtils::to_u16string(commandType)) + u" - Type Id: " + (GeneralUtils::to_u16string(typeId))); ChatPackets::SendSystemMessage(owner->GetSystemAddress(), u"Command Type: " + (GeneralUtils::to_u16string(commandType)) + u" - Type Id: " + (GeneralUtils::to_u16string(typeId)));
} }
} }
LWOOBJID PetComponent::GetOwnerId() const {
return m_Owner;
}
Entity* PetComponent::GetOwner() const { Entity* PetComponent::GetOwner() const {
return EntityManager::Instance()->GetEntity(m_Owner); return EntityManager::Instance()->GetEntity(m_Owner);
} }
LWOOBJID PetComponent::GetDatabaseId() const { PetComponent* PetComponent::GetTamingPet(const LWOOBJID& tamer) {
return m_DatabaseId;
}
LWOOBJID PetComponent::GetInteraction() const {
return m_Interaction;
}
LWOOBJID PetComponent::GetItemId() const {
return m_ItemId;
}
uint32_t PetComponent::GetStatus() const {
return m_Status;
}
PetAbilityType PetComponent::GetAbility() const {
return m_Ability;
}
void PetComponent::SetInteraction(LWOOBJID value) {
m_Interaction = value;
}
void PetComponent::SetStatus(uint32_t value) {
m_Status = value;
}
void PetComponent::SetAbility(PetAbilityType value) {
m_Ability = value;
}
PetComponent* PetComponent::GetTamingPet(LWOOBJID tamer) {
const auto& pair = currentActivities.find(tamer); const auto& pair = currentActivities.find(tamer);
if (pair == currentActivities.end()) { if (pair == currentActivities.end()) {
@@ -1048,7 +968,7 @@ PetComponent* PetComponent::GetTamingPet(LWOOBJID tamer) {
auto* entity = EntityManager::Instance()->GetEntity(pair->second); auto* entity = EntityManager::Instance()->GetEntity(pair->second);
if (entity == nullptr) { if (!entity) {
currentActivities.erase(tamer); currentActivities.erase(tamer);
return nullptr; return nullptr;
@@ -1057,7 +977,7 @@ PetComponent* PetComponent::GetTamingPet(LWOOBJID tamer) {
return entity->GetComponent<PetComponent>(); return entity->GetComponent<PetComponent>();
} }
PetComponent* PetComponent::GetActivePet(LWOOBJID owner) { PetComponent* PetComponent::GetActivePet(const LWOOBJID& owner) {
const auto& pair = activePets.find(owner); const auto& pair = activePets.find(owner);
if (pair == activePets.end()) { if (pair == activePets.end()) {
@@ -1066,7 +986,7 @@ PetComponent* PetComponent::GetActivePet(LWOOBJID owner) {
auto* entity = EntityManager::Instance()->GetEntity(pair->second); auto* entity = EntityManager::Instance()->GetEntity(pair->second);
if (entity == nullptr) { if (!entity) {
activePets.erase(owner); activePets.erase(owner);
return nullptr; return nullptr;
@@ -1075,13 +995,6 @@ PetComponent* PetComponent::GetActivePet(LWOOBJID owner) {
return entity->GetComponent<PetComponent>(); return entity->GetComponent<PetComponent>();
} }
Entity* PetComponent::GetParentEntity() const {
return m_Parent;
}
PetComponent::~PetComponent() {
}
void PetComponent::SetPetNameForModeration(const std::string& petName) { void PetComponent::SetPetNameForModeration(const std::string& petName) {
int approved = 1; //default, in mod int approved = 1; //default, in mod
@@ -1090,39 +1003,33 @@ void PetComponent::SetPetNameForModeration(const std::string& petName) {
approved = 2; //approved approved = 2; //approved
} }
auto deleteStmt = Database::CreatePreppedStmt("DELETE FROM pet_names WHERE id = ? LIMIT 1;"); std::unique_ptr<sql::PreparedStatement> deleteStmt(Database::CreatePreppedStmt("DELETE FROM pet_names WHERE id = ? LIMIT 1;"));
deleteStmt->setUInt64(1, m_DatabaseId); deleteStmt->setUInt64(1, m_DatabaseId);
deleteStmt->execute(); deleteStmt->execute();
delete deleteStmt;
//Save to db: //Save to db:
auto stmt = Database::CreatePreppedStmt("INSERT INTO `pet_names` (`id`, `pet_name`, `approved`) VALUES (?, ?, ?);"); std::unique_ptr<sql::PreparedStatement> stmt(Database::CreatePreppedStmt("INSERT INTO `pet_names` (`id`, `pet_name`, `approved`) VALUES (?, ?, ?);"));
stmt->setUInt64(1, m_DatabaseId); stmt->setUInt64(1, m_DatabaseId);
stmt->setString(2, petName); stmt->setString(2, petName);
stmt->setInt(3, approved); stmt->setInt(3, approved);
stmt->execute(); stmt->execute();
delete stmt;
} }
void PetComponent::LoadPetNameFromModeration() { void PetComponent::LoadPetNameFromModeration() {
auto stmt = Database::CreatePreppedStmt("SELECT pet_name, approved FROM pet_names WHERE id = ? LIMIT 1;"); std::unique_ptr<sql::PreparedStatement> stmt(Database::CreatePreppedStmt("SELECT pet_name, approved FROM pet_names WHERE id = ? LIMIT 1;"));
stmt->setUInt64(1, m_DatabaseId); stmt->setUInt64(1, m_DatabaseId);
auto res = stmt->executeQuery(); std::unique_ptr<sql::ResultSet> res(stmt->executeQuery());
while (res->next()) { if (!res->next()) return;
m_ModerationStatus = res->getInt(2); m_ModerationStatus = res->getInt("approved");
if (m_ModerationStatus == 2) { if (m_ModerationStatus == 2) {
m_Name = res->getString(1); m_Name = res->getString("pet_name");
}
} }
delete res;
delete stmt;
} }
void PetComponent::SetPreconditions(std::string& preconditions) { void PetComponent::SetPreconditions(std::string& preconditions) {
if (m_Preconditions) delete m_Preconditions;
m_Preconditions = new PreconditionExpression(preconditions); m_Preconditions = new PreconditionExpression(preconditions);
} }

View File

@@ -1,19 +1,21 @@
#pragma once #pragma once
#include "Entity.h" #include "Entity.h"
#include "MovementAIComponent.h"
#include "Component.h" #include "Component.h"
#include "Preconditions.h"
#include "eReplicaComponentType.h" #include "eReplicaComponentType.h"
enum class PetAbilityType class PreconditionExpression;
{ class MovementAIComponent;
enum class PetAbilityType : uint32_t {
Invalid, Invalid,
GoToObject, GoToObject,
JumpOnObject, JumpOnObject,
DigAtPosition DigAtPosition
}; };
enum ePlayerFlag : int32_t;
/** /**
* Represents an entity that is a pet. This pet can be tamed and consequently follows the tamer around, allowing it * Represents an entity that is a pet. This pet can be tamed and consequently follows the tamer around, allowing it
* to dig for treasure and activate pet bouncers. * to dig for treasure and activate pet bouncers.
@@ -21,10 +23,9 @@ enum class PetAbilityType
class PetComponent : public Component class PetComponent : public Component
{ {
public: public:
static const eReplicaComponentType ComponentType = eReplicaComponentType::PET; inline static const eReplicaComponentType ComponentType = eReplicaComponentType::PET;
explicit PetComponent(Entity* parentEntity, uint32_t componentId); explicit PetComponent(Entity* parentEntity, uint32_t componentId);
~PetComponent() override;
void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags); void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags);
void Update(float deltaTime) override; void Update(float deltaTime) override;
@@ -109,7 +110,7 @@ public:
* Returns the ID of the owner of this pet (if any) * Returns the ID of the owner of this pet (if any)
* @return the ID of the owner of this pet * @return the ID of the owner of this pet
*/ */
LWOOBJID GetOwnerId() const; LWOOBJID GetOwnerId() const { return m_Owner; }
/** /**
* Returns the entity that owns this pet (if any) * Returns the entity that owns this pet (if any)
@@ -121,44 +122,44 @@ public:
* Returns the ID that is stored in the database with regards to this pet, only set for pets that are tamed * Returns the ID that is stored in the database with regards to this pet, only set for pets that are tamed
* @return the ID that is stored in the database with regards to this pet * @return the ID that is stored in the database with regards to this pet
*/ */
LWOOBJID GetDatabaseId() const; LWOOBJID GetDatabaseId() const { return m_DatabaseId; }
/** /**
* Returns the ID of the object that the pet is currently interacting with, could be a treasure chest or a switch * Returns the ID of the object that the pet is currently interacting with, could be a treasure chest or a switch
* @return the ID of the object that the pet is currently interacting with * @return the ID of the object that the pet is currently interacting with
*/ */
LWOOBJID GetInteraction() const; LWOOBJID GetInteraction() const { return m_Interaction; }
/** /**
* Sets the ID that the pet is interacting with * Sets the ID that the pet is interacting with
* @param value the ID that the pet is interacting with * @param value the ID that the pet is interacting with
*/ */
void SetInteraction(LWOOBJID value); void SetInteraction(const LWOOBJID& value) { m_Interaction = value; }
/** /**
* Returns the ID that this pet was spawned from, only set for tamed pets * Returns the ID that this pet was spawned from, only set for tamed pets
* @return the ID that this pet was spawned from * @return the ID that this pet was spawned from
*/ */
LWOOBJID GetItemId() const; LWOOBJID GetItemId() const { return m_ItemId; }
/** /**
* Returns the status of this pet, e.g. tamable or tamed. The values here are still a bit of mystery and likely a * Returns the status of this pet, e.g. tamable or tamed. The values here are still a bit of mystery and likely a
* bit map * bit map
* @return the status of this pet * @return the status of this pet
*/ */
uint32_t GetStatus() const; uint32_t GetStatus() const { return m_Status; }
/** /**
* Sets the current status of the pet * Sets the current status of the pet
* @param value the current status of the pet to set * @param value the current status of the pet to set
*/ */
void SetStatus(uint32_t value); void SetStatus(const uint32_t value) { m_Status = value; }
/** /**
* Returns an ability the pet may perform, currently unused * Returns an ability the pet may perform, currently unused
* @return an ability the pet may perform * @return an ability the pet may perform
*/ */
PetAbilityType GetAbility() const; PetAbilityType GetAbility() const { return m_Ability; }
/** /**
* Sets the ability of the pet, currently unused * Sets the ability of the pet, currently unused
@@ -172,12 +173,6 @@ public:
*/ */
void SetPreconditions(std::string& conditions); void SetPreconditions(std::string& conditions);
/**
* Returns the entity that this component belongs to
* @return the entity that this component belongs to
*/
Entity* GetParentEntity() const;
/** /**
* Sets the name of the pet to be moderated * Sets the name of the pet to be moderated
* @param petName the name of the pet to set * @param petName the name of the pet to set
@@ -195,14 +190,14 @@ public:
* @param tamer the entity that's currently taming * @param tamer the entity that's currently taming
* @return the pet component of the entity that's being tamed * @return the pet component of the entity that's being tamed
*/ */
static PetComponent* GetTamingPet(LWOOBJID tamer); static PetComponent* GetTamingPet(const LWOOBJID& tamer);
/** /**
* Returns the pet that's currently spawned for some entity (if any) * Returns the pet that's currently spawned for some entity (if any)
* @param owner the owner of the pet that's spawned * @param owner the owner of the pet that's spawned
* @return the pet component of the entity that was spawned by the owner * @return the pet component of the entity that was spawned by the owner
*/ */
static PetComponent* GetActivePet(LWOOBJID owner); static PetComponent* GetActivePet(const LWOOBJID& owner);
/** /**
* Adds the timer to the owner of this pet to drain imagination at the rate * Adds the timer to the owner of this pet to drain imagination at the rate
@@ -263,7 +258,7 @@ private:
/** /**
* Flags that indicate that a player has tamed a pet, indexed by the LOT of the pet * Flags that indicate that a player has tamed a pet, indexed by the LOT of the pet
*/ */
static std::map<LOT, int32_t> petFlags; static std::map<LOT, ePlayerFlag> petFlags;
/** /**
* The ID of the component in the pet component table * The ID of the component in the pet component table
@@ -359,5 +354,5 @@ private:
/** /**
* The rate at which imagination is drained from the user for having the pet out. * The rate at which imagination is drained from the user for having the pet out.
*/ */
float imaginationDrainRate; float m_ImaginationDrainRate;
}; };

View File

@@ -26,11 +26,13 @@
#include "dpEntity.h" #include "dpEntity.h"
#include "dpShapeBox.h" #include "dpShapeBox.h"
#include "dpShapeSphere.h" #include "dpShapeSphere.h"
#include "NiPoint3.h"
#include "NiQuaternion.h"
PhantomPhysicsComponent::PhantomPhysicsComponent(Entity* parent) : Component(parent) { PhantomPhysicsComponent::PhantomPhysicsComponent(Entity* parent) : Component(parent) {
m_Position = m_Parent->GetDefaultPosition(); m_Position = m_ParentEntity->GetDefaultPosition();
m_Rotation = m_Parent->GetDefaultRotation(); m_Rotation = m_ParentEntity->GetDefaultRotation();
m_Scale = m_Parent->GetDefaultScale(); m_Scale = m_ParentEntity->GetDefaultScale();
m_dpEntity = nullptr; m_dpEntity = nullptr;
m_EffectInfoDirty = false; m_EffectInfoDirty = false;
@@ -45,232 +47,148 @@ PhantomPhysicsComponent::PhantomPhysicsComponent(Entity* parent) : Component(par
m_Max = 1; m_Max = 1;
m_IsDirectional = false; m_IsDirectional = false;
m_Direction = NiPoint3(); // * m_DirectionalMultiplier m_Direction = NiPoint3::ZERO;
}
if (m_Parent->GetVar<bool>(u"create_physics")) { PhantomPhysicsComponent::~PhantomPhysicsComponent() {
CreatePhysics(); if (m_dpEntity) dpWorld::Instance().RemoveEntity(m_dpEntity);
} }
if (m_Parent->GetVar<bool>(u"respawnVol")) {
m_IsRespawnVolume = true;
}
if (m_IsRespawnVolume) {
{
auto respawnString = std::stringstream(m_Parent->GetVarAsString(u"rspPos"));
std::string segment;
std::vector<std::string> seglist;
while (std::getline(respawnString, segment, '\x1f')) {
seglist.push_back(segment);
}
m_RespawnPos = NiPoint3(std::stof(seglist[0]), std::stof(seglist[1]), std::stof(seglist[2]));
}
{
auto respawnString = std::stringstream(m_Parent->GetVarAsString(u"rspRot"));
std::string segment;
std::vector<std::string> seglist;
while (std::getline(respawnString, segment, '\x1f')) {
seglist.push_back(segment);
}
m_RespawnRot = NiQuaternion(std::stof(seglist[0]), std::stof(seglist[1]), std::stof(seglist[2]), std::stof(seglist[3]));
}
}
void PhantomPhysicsComponent::LoadTemplateData() {
// HF - RespawnPoints. Legacy respawn entity. // HF - RespawnPoints. Legacy respawn entity.
if (m_Parent->GetLOT() == 4945) { if (m_ParentEntity->GetLOT() == LOT_LEGACY_RESPAWN_POINT) {
m_IsRespawnVolume = true; m_IsRespawnVolume = true;
m_RespawnPos = m_Position; m_RespawnPos = m_Position;
m_RespawnRot = m_Rotation; m_RespawnRot = m_Rotation;
} }
/* auto* compRegistryTable = CDClientManager::Instance().GetTable<CDComponentsRegistryTable>();
for (LDFBaseData* data : settings) { auto componentID = compRegistryTable->GetByIDAndType(m_ParentEntity->GetLOT(), eReplicaComponentType::PHANTOM_PHYSICS);
if (data) {
if (data->GetKey() == u"create_physics") {
if (bool(std::stoi(data->GetValueAsString()))) {
CreatePhysics(settings);
}
}
if (data->GetKey() == u"respawnVol") { auto* physCompTable = CDClientManager::Instance().GetTable<CDPhysicsComponentTable>();
if (bool(std::stoi(data->GetValueAsString()))) {
m_IsRespawnVolume = true;
}
}
if (m_IsRespawnVolume) { if (!physCompTable) return;
if (data->GetKey() == u"rspPos") {
//Joy, we get to split strings!
std::stringstream test(data->GetValueAsString());
std::string segment;
std::vector<std::string> seglist;
while (std::getline(test, segment, '\x1f')) { auto* info = physCompTable->GetByID(componentID);
seglist.push_back(segment); if (!info || info->physicsAsset.empty() || info->physicsAsset == "NO_PHYSICS") return;
}
m_RespawnPos = NiPoint3(std::stof(seglist[0]), std::stof(seglist[1]), std::stof(seglist[2])); if (info->physicsAsset == "miscellaneous\\misc_phys_10x1x5.hkx") {
} m_dpEntity = new dpEntity(m_ParentEntity->GetObjectID(), 10.0f, 5.0f, 1.0f);
m_dpEntity->SetScale(m_Scale);
if (data->GetKey() == u"rspRot") { m_dpEntity->SetRotation(m_Rotation);
//Joy, we get to split strings! m_dpEntity->SetPosition(m_Position);
std::stringstream test(data->GetValueAsString()); } else if (info->physicsAsset == "miscellaneous\\misc_phys_640x640.hkx") {
std::string segment; // Move this down by 13.521004 units so it is still effectively at the same height as before
std::vector<std::string> seglist; m_Position = m_Position - NiPoint3::UNIT_Y * 13.521004f;
// TODO Fix physics simulation to do simulation at high velocities due to bullet through paper problem...
while (std::getline(test, segment, '\x1f')) { m_dpEntity = new dpEntity(m_ParentEntity->GetObjectID(), 1638.4f, 13.521004f * 2.0f, 1638.4f);
seglist.push_back(segment); m_dpEntity->SetScale(m_Scale);
} m_dpEntity->SetRotation(m_Rotation);
m_dpEntity->SetPosition(m_Position);
m_RespawnRot = NiQuaternion(std::stof(seglist[0]), std::stof(seglist[1]), std::stof(seglist[2]), std::stof(seglist[3])); } else if (info->physicsAsset == "env\\trigger_wall_tall.hkx") {
} m_dpEntity = new dpEntity(m_ParentEntity->GetObjectID(), 10.0f, 25.0f, 1.0f);
} m_dpEntity->SetScale(m_Scale);
m_dpEntity->SetRotation(m_Rotation);
if (m_Parent->GetLOT() == 4945) // HF - RespawnPoints m_dpEntity->SetPosition(m_Position);
{ } else if (info->physicsAsset == "env\\env_gen_placeholderphysics.hkx") {
m_IsRespawnVolume = true; m_dpEntity = new dpEntity(m_ParentEntity->GetObjectID(), 20.0f, 20.0f, 20.0f);
m_RespawnPos = m_Position; m_dpEntity->SetScale(m_Scale);
m_RespawnRot = m_Rotation; m_dpEntity->SetRotation(m_Rotation);
} m_dpEntity->SetPosition(m_Position);
} } else if (info->physicsAsset == "env\\POI_trigger_wall.hkx") {
m_dpEntity = new dpEntity(m_ParentEntity->GetObjectID(), 1.0f, 12.5f, 20.0f); // Not sure what the real size is
m_dpEntity->SetScale(m_Scale);
m_dpEntity->SetRotation(m_Rotation);
m_dpEntity->SetPosition(m_Position);
} else if (info->physicsAsset == "env\\NG_NinjaGo\\env_ng_gen_gate_chamber_puzzle_ceiling_tile_falling_phantom.hkx") {
m_dpEntity = new dpEntity(m_ParentEntity->GetObjectID(), 18.0f, 5.0f, 15.0f);
m_dpEntity->SetScale(m_Scale);
m_dpEntity->SetRotation(m_Rotation);
m_dpEntity->SetPosition(m_Position + m_Rotation.GetForwardVector() * 7.5f);
} else if (info->physicsAsset == "env\\NG_NinjaGo\\ng_flamejet_brick_phantom.HKX") {
m_dpEntity = new dpEntity(m_ParentEntity->GetObjectID(), 1.0f, 1.0f, 12.0f);
m_dpEntity->SetScale(m_Scale);
m_dpEntity->SetRotation(m_Rotation);
m_dpEntity->SetPosition(m_Position + m_Rotation.GetForwardVector() * 6.0f);
} else if (info->physicsAsset == "env\\Ring_Trigger.hkx") {
m_dpEntity = new dpEntity(m_ParentEntity->GetObjectID(), 6.0f, 6.0f, 6.0f);
m_dpEntity->SetScale(m_Scale);
m_dpEntity->SetRotation(m_Rotation);
m_dpEntity->SetPosition(m_Position);
} else if (info->physicsAsset == "env\\vfx_propertyImaginationBall.hkx") {
m_dpEntity = new dpEntity(m_ParentEntity->GetObjectID(), 4.5f);
m_dpEntity->SetScale(m_Scale);
m_dpEntity->SetRotation(m_Rotation);
m_dpEntity->SetPosition(m_Position);
} else if (info->physicsAsset == "env\\env_won_fv_gas-blocking-volume.hkx") {
m_dpEntity = new dpEntity(m_ParentEntity->GetObjectID(), 390.496826f, 111.467964f, 600.821534f, true);
m_dpEntity->SetScale(m_Scale);
m_dpEntity->SetRotation(m_Rotation);
m_Position.y -= (111.467964f * m_Scale) / 2;
m_dpEntity->SetPosition(m_Position);
} else {
Game::logger->LogDebug("PhantomPhysicsComponent", "This component is supposed to have asset %s but is defaulting to fallback cube.", info->physicsAsset.c_str());
//add fallback cube:
m_dpEntity = new dpEntity(m_ParentEntity->GetObjectID(), 2.0f, 2.0f, 2.0f);
m_dpEntity->SetScale(m_Scale);
m_dpEntity->SetRotation(m_Rotation);
m_dpEntity->SetPosition(m_Position);
} }
*/ dpWorld::Instance().AddEntity(m_dpEntity);
if (!m_HasCreatedPhysics) {
CDComponentsRegistryTable* compRegistryTable = CDClientManager::Instance().GetTable<CDComponentsRegistryTable>();
auto componentID = compRegistryTable->GetByIDAndType(m_Parent->GetLOT(), eReplicaComponentType::PHANTOM_PHYSICS);
CDPhysicsComponentTable* physComp = CDClientManager::Instance().GetTable<CDPhysicsComponentTable>();
if (physComp == nullptr) return;
auto* info = physComp->GetByID(componentID);
if (info == nullptr || info->physicsAsset == "" || info->physicsAsset == "NO_PHYSICS") return;
//temp test
if (info->physicsAsset == "miscellaneous\\misc_phys_10x1x5.hkx") {
m_dpEntity = new dpEntity(m_Parent->GetObjectID(), 10.0f, 5.0f, 1.0f);
m_dpEntity->SetScale(m_Scale);
m_dpEntity->SetRotation(m_Rotation);
m_dpEntity->SetPosition(m_Position);
dpWorld::Instance().AddEntity(m_dpEntity);
} else if (info->physicsAsset == "miscellaneous\\misc_phys_640x640.hkx") {
// Move this down by 13.521004 units so it is still effectively at the same height as before
m_Position = m_Position - NiPoint3::UNIT_Y * 13.521004f;
// TODO Fix physics simulation to do simulation at high velocities due to bullet through paper problem...
m_dpEntity = new dpEntity(m_Parent->GetObjectID(), 1638.4f, 13.521004f * 2.0f, 1638.4f);
m_dpEntity->SetScale(m_Scale);
m_dpEntity->SetRotation(m_Rotation);
m_dpEntity->SetPosition(m_Position);
dpWorld::Instance().AddEntity(m_dpEntity);
} else if (info->physicsAsset == "env\\trigger_wall_tall.hkx") {
m_dpEntity = new dpEntity(m_Parent->GetObjectID(), 10.0f, 25.0f, 1.0f);
m_dpEntity->SetScale(m_Scale);
m_dpEntity->SetRotation(m_Rotation);
m_dpEntity->SetPosition(m_Position);
dpWorld::Instance().AddEntity(m_dpEntity);
} else if (info->physicsAsset == "env\\env_gen_placeholderphysics.hkx") {
m_dpEntity = new dpEntity(m_Parent->GetObjectID(), 20.0f, 20.0f, 20.0f);
m_dpEntity->SetScale(m_Scale);
m_dpEntity->SetRotation(m_Rotation);
m_dpEntity->SetPosition(m_Position);
dpWorld::Instance().AddEntity(m_dpEntity);
} else if (info->physicsAsset == "env\\POI_trigger_wall.hkx") {
m_dpEntity = new dpEntity(m_Parent->GetObjectID(), 1.0f, 12.5f, 20.0f); // Not sure what the real size is
m_dpEntity->SetScale(m_Scale);
m_dpEntity->SetRotation(m_Rotation);
m_dpEntity->SetPosition(m_Position);
dpWorld::Instance().AddEntity(m_dpEntity);
} else if (info->physicsAsset == "env\\NG_NinjaGo\\env_ng_gen_gate_chamber_puzzle_ceiling_tile_falling_phantom.hkx") {
m_dpEntity = new dpEntity(m_Parent->GetObjectID(), 18.0f, 5.0f, 15.0f);
m_dpEntity->SetScale(m_Scale);
m_dpEntity->SetRotation(m_Rotation);
m_dpEntity->SetPosition(m_Position + m_Rotation.GetForwardVector() * 7.5f);
dpWorld::Instance().AddEntity(m_dpEntity);
} else if (info->physicsAsset == "env\\NG_NinjaGo\\ng_flamejet_brick_phantom.HKX") {
m_dpEntity = new dpEntity(m_Parent->GetObjectID(), 1.0f, 1.0f, 12.0f);
m_dpEntity->SetScale(m_Scale);
m_dpEntity->SetRotation(m_Rotation);
m_dpEntity->SetPosition(m_Position + m_Rotation.GetForwardVector() * 6.0f);
dpWorld::Instance().AddEntity(m_dpEntity);
} else if (info->physicsAsset == "env\\Ring_Trigger.hkx") {
m_dpEntity = new dpEntity(m_Parent->GetObjectID(), 6.0f, 6.0f, 6.0f);
m_dpEntity->SetScale(m_Scale);
m_dpEntity->SetRotation(m_Rotation);
m_dpEntity->SetPosition(m_Position);
dpWorld::Instance().AddEntity(m_dpEntity);
} else if (info->physicsAsset == "env\\vfx_propertyImaginationBall.hkx") {
m_dpEntity = new dpEntity(m_Parent->GetObjectID(), 4.5f);
m_dpEntity->SetScale(m_Scale);
m_dpEntity->SetRotation(m_Rotation);
m_dpEntity->SetPosition(m_Position);
dpWorld::Instance().AddEntity(m_dpEntity);
} else if (info->physicsAsset == "env\\env_won_fv_gas-blocking-volume.hkx"){
m_dpEntity = new dpEntity(m_Parent->GetObjectID(), 390.496826f, 111.467964f, 600.821534f, true);
m_dpEntity->SetScale(m_Scale);
m_dpEntity->SetRotation(m_Rotation);
m_Position.y -= (111.467964f * m_Scale) / 2;
m_dpEntity->SetPosition(m_Position);
dpWorld::Instance().AddEntity(m_dpEntity);
} else {
//Game::logger->Log("PhantomPhysicsComponent", "This one is supposed to have %s", info->physicsAsset.c_str());
//add fallback cube:
m_dpEntity = new dpEntity(m_Parent->GetObjectID(), 2.0f, 2.0f, 2.0f);
m_dpEntity->SetScale(m_Scale);
m_dpEntity->SetRotation(m_Rotation);
m_dpEntity->SetPosition(m_Position);
dpWorld::Instance().AddEntity(m_dpEntity);
}
}
} }
PhantomPhysicsComponent::~PhantomPhysicsComponent() { void PhantomPhysicsComponent::LoadConfigData() {
if (m_dpEntity) { if (m_ParentEntity->GetVar<bool>(u"create_physics")) {
dpWorld::Instance().RemoveEntity(m_dpEntity); CreatePhysics();
}
if (m_ParentEntity->GetVar<bool>(u"respawnVol")) {
m_IsRespawnVolume = true;
}
if (m_IsRespawnVolume) {
auto respawnPosSplit = GeneralUtils::SplitString(m_ParentEntity->GetVarAsString(u"rspPos"), '\x1f');
m_RespawnPos = NiPoint3::ZERO;
if (respawnPosSplit.size() >= 3) {
GeneralUtils::TryParse(respawnPosSplit[0], m_RespawnPos.x);
GeneralUtils::TryParse(respawnPosSplit[1], m_RespawnPos.y);
GeneralUtils::TryParse(respawnPosSplit[2], m_RespawnPos.z);
}
auto respawnRotSplit = GeneralUtils::SplitString(m_ParentEntity->GetVarAsString(u"rspRot"), '\x1f');
m_RespawnRot = NiQuaternion::IDENTITY;
if (respawnRotSplit.size() >= 4) {
GeneralUtils::TryParse(respawnRotSplit[0], m_RespawnRot.w);
GeneralUtils::TryParse(respawnRotSplit[1], m_RespawnRot.x);
GeneralUtils::TryParse(respawnRotSplit[2], m_RespawnRot.y);
GeneralUtils::TryParse(respawnRotSplit[3], m_RespawnRot.z);
}
} }
} }
void PhantomPhysicsComponent::CreatePhysics() { void PhantomPhysicsComponent::CreatePhysics() {
unsigned char alpha; int32_t type = -1;
unsigned char red; NiPoint3 pos;
unsigned char green;
unsigned char blue;
int type = -1;
float x = 0.0f;
float y = 0.0f;
float z = 0.0f;
float width = 0.0f; //aka "radius" float width = 0.0f; //aka "radius"
float height = 0.0f; float height = 0.0f;
if (m_Parent->HasVar(u"primitiveModelType")) { if (m_ParentEntity->HasVar(u"primitiveModelType")) {
type = m_Parent->GetVar<int32_t>(u"primitiveModelType"); type = m_ParentEntity->GetVar<int32_t>(u"primitiveModelType");
x = m_Parent->GetVar<float>(u"primitiveModelValueX"); pos.x = m_ParentEntity->GetVar<float>(u"primitiveModelValueX");
y = m_Parent->GetVar<float>(u"primitiveModelValueY"); pos.y = m_ParentEntity->GetVar<float>(u"primitiveModelValueY");
z = m_Parent->GetVar<float>(u"primitiveModelValueZ"); pos.z = m_ParentEntity->GetVar<float>(u"primitiveModelValueZ");
} else { } else {
CDComponentsRegistryTable* compRegistryTable = CDClientManager::Instance().GetTable<CDComponentsRegistryTable>(); auto* compRegistryTable = CDClientManager::Instance().GetTable<CDComponentsRegistryTable>();
auto componentID = compRegistryTable->GetByIDAndType(m_Parent->GetLOT(), eReplicaComponentType::PHANTOM_PHYSICS); auto componentID = compRegistryTable->GetByIDAndType(m_ParentEntity->GetLOT(), eReplicaComponentType::PHANTOM_PHYSICS);
CDPhysicsComponentTable* physComp = CDClientManager::Instance().GetTable<CDPhysicsComponentTable>(); auto* physCompTable = CDClientManager::Instance().GetTable<CDPhysicsComponentTable>();
if (physComp == nullptr) return; if (!physCompTable) return;
auto info = physComp->GetByID(componentID); auto info = physCompTable->GetByID(componentID);
if (info == nullptr) return; if (!info) return;
type = info->pcShapeType; type = info->pcShapeType;
width = info->playerRadius; width = info->playerRadius;
@@ -279,8 +197,8 @@ void PhantomPhysicsComponent::CreatePhysics() {
switch (type) { switch (type) {
case 1: { //Make a new box shape case 1: { //Make a new box shape
NiPoint3 boxSize(x, y, z); BoxDimensions boxSize(pos.x, pos.y, pos.z);
if (x == 0.0f) { if (pos.x == 0.0f) {
//LU has some weird values, so I think it's best to scale them down a bit //LU has some weird values, so I think it's best to scale them down a bit
if (height < 0.5f) height = 2.0f; if (height < 0.5f) height = 2.0f;
if (width < 0.5f) width = 2.0f; if (width < 0.5f) width = 2.0f;
@@ -289,17 +207,21 @@ void PhantomPhysicsComponent::CreatePhysics() {
width = width * m_Scale; width = width * m_Scale;
height = height * m_Scale; height = height * m_Scale;
boxSize = NiPoint3(width, height, width); boxSize = BoxDimensions(width, height, width);
} }
if (m_dpEntity) delete m_dpEntity;
m_dpEntity = new dpEntity(m_Parent->GetObjectID(), boxSize); m_dpEntity = new dpEntity(m_ParentEntity->GetObjectID(), boxSize);
break;
}
default: {
Game::logger->Log("PhantomPhysicsComponent", "Unknown shape type: %d", type);
break; break;
} }
} }
if (!m_dpEntity) return; if (!m_dpEntity) return;
m_dpEntity->SetPosition({ m_Position.x, m_Position.y - (height / 2), m_Position.z }); m_dpEntity->SetPosition(NiPoint3(m_Position.x, m_Position.y - (height / 2), m_Position.z));
dpWorld::Instance().AddEntity(m_dpEntity); dpWorld::Instance().AddEntity(m_dpEntity);
@@ -312,12 +234,13 @@ void PhantomPhysicsComponent::Serialize(RakNet::BitStream* outBitStream, bool bI
outBitStream->Write(m_Position.x); outBitStream->Write(m_Position.x);
outBitStream->Write(m_Position.y); outBitStream->Write(m_Position.y);
outBitStream->Write(m_Position.z); outBitStream->Write(m_Position.z);
outBitStream->Write(m_Rotation.x); outBitStream->Write(m_Rotation.x);
outBitStream->Write(m_Rotation.y); outBitStream->Write(m_Rotation.y);
outBitStream->Write(m_Rotation.z); outBitStream->Write(m_Rotation.z);
outBitStream->Write(m_Rotation.w); outBitStream->Write(m_Rotation.w);
m_PositionInfoDirty = false; if (!bIsInitialUpdate) m_PositionInfoDirty = false;
} }
outBitStream->Write(m_EffectInfoDirty || bIsInitialUpdate); outBitStream->Write(m_EffectInfoDirty || bIsInitialUpdate);
@@ -328,7 +251,7 @@ void PhantomPhysicsComponent::Serialize(RakNet::BitStream* outBitStream, bool bI
outBitStream->Write(m_EffectType); outBitStream->Write(m_EffectType);
outBitStream->Write(m_DirectionalMultiplier); outBitStream->Write(m_DirectionalMultiplier);
// forgive me father for i have sinned // distance info. Option.
outBitStream->Write0(); outBitStream->Write0();
//outBitStream->Write(m_MinMax); //outBitStream->Write(m_MinMax);
//if (m_MinMax) { //if (m_MinMax) {
@@ -344,7 +267,7 @@ void PhantomPhysicsComponent::Serialize(RakNet::BitStream* outBitStream, bool bI
} }
} }
m_EffectInfoDirty = false; if (!bIsInitialUpdate) m_EffectInfoDirty = false;
} }
} }
@@ -357,28 +280,28 @@ void PhantomPhysicsComponent::Update(float deltaTime) {
if (!m_dpEntity) return; if (!m_dpEntity) return;
//Process enter events //Process enter events
for (auto en : m_dpEntity->GetNewObjects()) { for (auto* en : m_dpEntity->GetNewObjects()) {
m_Parent->OnCollisionPhantom(en->GetObjectID()); m_ParentEntity->OnCollisionPhantom(en->GetObjectID());
//If we are a respawn volume, inform the client: //If we are a respawn volume, inform the client:
if (m_IsRespawnVolume) { if (!m_IsRespawnVolume) continue;
auto entity = EntityManager::Instance()->GetEntity(en->GetObjectID()); auto entity = EntityManager::Instance()->GetEntity(en->GetObjectID());
if (entity) { if (!entity) continue;
GameMessages::SendPlayerReachedRespawnCheckpoint(entity, m_RespawnPos, m_RespawnRot);
entity->SetRespawnPos(m_RespawnPos); GameMessages::SendPlayerReachedRespawnCheckpoint(entity, m_RespawnPos, m_RespawnRot);
entity->SetRespawnRot(m_RespawnRot); entity->SetRespawnPosition(m_RespawnPos);
} entity->SetRespawnRotation(m_RespawnRot);
}
} }
//Process exit events //Process exit events
for (auto en : m_dpEntity->GetRemovedObjects()) { for (auto* en : m_dpEntity->GetRemovedObjects()) {
m_Parent->OnCollisionLeavePhantom(en->GetObjectID()); m_ParentEntity->OnCollisionLeavePhantom(en->GetObjectID());
} }
} }
void PhantomPhysicsComponent::SetDirection(const NiPoint3& pos) { void PhantomPhysicsComponent::SetDirection(const NiPoint3& pos) {
if (m_Direction == pos) return;
m_Direction = pos; m_Direction = pos;
m_Direction.x *= m_DirectionalMultiplier; m_Direction.x *= m_DirectionalMultiplier;
m_Direction.y *= m_DirectionalMultiplier; m_Direction.y *= m_DirectionalMultiplier;
@@ -391,53 +314,60 @@ void PhantomPhysicsComponent::SetDirection(const NiPoint3& pos) {
void PhantomPhysicsComponent::SpawnVertices() { void PhantomPhysicsComponent::SpawnVertices() {
if (!m_dpEntity) return; if (!m_dpEntity) return;
std::cout << m_Parent->GetObjectID() << std::endl; Game::logger->Log("PhantomPhysicsComponent", "objectId is %llu", m_ParentEntity->GetObjectID());
auto box = static_cast<dpShapeBox*>(m_dpEntity->GetShape()); auto box = dynamic_cast<dpShapeBox*>(m_dpEntity->GetShape());
if (!box) return;
for (auto vert : box->GetVertices()) { for (auto vert : box->GetVertices()) {
std::cout << vert.x << ", " << vert.y << ", " << vert.z << std::endl; Game::logger->Log("PhantomPhysicsComponent", "%f, %f, %f", vert.x, vert.y, vert.z);
EntityInfo info; EntityInfo info;
info.lot = 33; info.lot = 33;
info.pos = vert; info.pos = vert;
info.spawner = nullptr; info.spawner = nullptr;
info.spawnerID = m_Parent->GetObjectID(); info.spawnerID = m_ParentEntity->GetObjectID();
info.spawnerNodeID = 0; info.spawnerNodeID = 0;
Entity* newEntity = EntityManager::Instance()->CreateEntity(info, nullptr); Entity* newEntity = EntityManager::Instance()->CreateEntity(info);
EntityManager::Instance()->ConstructEntity(newEntity); EntityManager::Instance()->ConstructEntity(newEntity);
} }
} }
void PhantomPhysicsComponent::SetDirectionalMultiplier(float mul) { void PhantomPhysicsComponent::SetDirectionalMultiplier(float mul) {
if (mul == m_DirectionalMultiplier) return;
m_DirectionalMultiplier = mul; m_DirectionalMultiplier = mul;
m_EffectInfoDirty = true; m_EffectInfoDirty = true;
} }
void PhantomPhysicsComponent::SetEffectType(ePhysicsEffectType type) { void PhantomPhysicsComponent::SetEffectType(const ePhysicsEffectType type) {
if (type == m_EffectType) return;
m_EffectType = type; m_EffectType = type;
m_EffectInfoDirty = true; m_EffectInfoDirty = true;
} }
void PhantomPhysicsComponent::SetMin(uint32_t min) { void PhantomPhysicsComponent::SetMin(const uint32_t min) {
if (min == m_Min) return;
m_Min = min; m_Min = min;
m_MinMax = true; m_MinMax = true;
m_EffectInfoDirty = true; m_EffectInfoDirty = true;
} }
void PhantomPhysicsComponent::SetMax(uint32_t max) { void PhantomPhysicsComponent::SetMax(const uint32_t max) {
if (max == m_Max) return;
m_Max = max; m_Max = max;
m_MinMax = true; m_MinMax = true;
m_EffectInfoDirty = true; m_EffectInfoDirty = true;
} }
void PhantomPhysicsComponent::SetPosition(const NiPoint3& pos) { void PhantomPhysicsComponent::SetPosition(const NiPoint3& pos) {
if (pos == m_Position) return;
m_Position = pos; m_Position = pos;
m_PositionInfoDirty = true;
if (m_dpEntity) m_dpEntity->SetPosition(pos); if (m_dpEntity) m_dpEntity->SetPosition(pos);
} }
void PhantomPhysicsComponent::SetRotation(const NiQuaternion& rot) { void PhantomPhysicsComponent::SetRotation(const NiQuaternion& rot) {
if (rot == m_Rotation) return;
m_Rotation = rot; m_Rotation = rot;
m_PositionInfoDirty = true;
if (m_dpEntity) m_dpEntity->SetRotation(rot); if (m_dpEntity) m_dpEntity->SetRotation(rot);
} }

View File

@@ -1,22 +1,18 @@
/* /*
* Darkflame Universe * Darkflame Universe
* Copyright 2018 * Copyright 2023
*/ */
#pragma once #pragma once
#include "NiPoint3.h"
#include "NiQuaternion.h"
#include "BitStream.h"
#include <vector>
#include "CppScripts.h"
#include "InvalidScript.h"
#include "Component.h" #include "Component.h"
#include "eReplicaComponentType.h" #include "eReplicaComponentType.h"
class LDFBaseData; class LDFBaseData;
class Entity; class Entity;
class dpEntity; class dpEntity;
class NiPoint3;
class NiQuaternion;
enum class ePhysicsEffectType : uint32_t ; enum class ePhysicsEffectType : uint32_t ;
/** /**
@@ -27,10 +23,13 @@ enum class ePhysicsEffectType : uint32_t ;
*/ */
class PhantomPhysicsComponent : public Component { class PhantomPhysicsComponent : public Component {
public: public:
static const eReplicaComponentType ComponentType = eReplicaComponentType::PHANTOM_PHYSICS; inline static const eReplicaComponentType ComponentType = eReplicaComponentType::PHANTOM_PHYSICS;
PhantomPhysicsComponent(Entity* parent); PhantomPhysicsComponent(Entity* parent);
~PhantomPhysicsComponent() override; ~PhantomPhysicsComponent() override;
void LoadTemplateData() override;
void LoadConfigData() override;
void Update(float deltaTime) override; void Update(float deltaTime) override;
void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags); void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags);
void ResetFlags(); void ResetFlags();
@@ -110,7 +109,7 @@ public:
* Sets the effect that's currently active * Sets the effect that's currently active
* @param type the effect to set * @param type the effect to set
*/ */
void SetEffectType(ePhysicsEffectType type); void SetEffectType(const ePhysicsEffectType type);
/** /**
* Returns the Physics entity for the component * Returns the Physics entity for the component
@@ -127,12 +126,12 @@ public:
/** /**
* Legacy stuff no clue what this does * Legacy stuff no clue what this does
*/ */
void SetMin(uint32_t min); void SetMin(const uint32_t min);
/** /**
* Legacy stuff no clue what this does * Legacy stuff no clue what this does
*/ */
void SetMax(uint32_t max); void SetMax(const uint32_t max);
private: private:

View File

@@ -1,16 +1,12 @@
#include "PlayerForcedMovementComponent.h" #include "PlayerForcedMovementComponent.h"
PlayerForcedMovementComponent::PlayerForcedMovementComponent(Entity* parent) : Component(parent) { #include "BitStream.h"
m_Parent = parent;
}
PlayerForcedMovementComponent::~PlayerForcedMovementComponent() {}
void PlayerForcedMovementComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags) { void PlayerForcedMovementComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags) {
outBitStream->Write(m_DirtyInfo || bIsInitialUpdate); outBitStream->Write(m_DirtyInfo || bIsInitialUpdate);
if (m_DirtyInfo || bIsInitialUpdate) { if (m_DirtyInfo || bIsInitialUpdate) {
outBitStream->Write(m_PlayerOnRail); outBitStream->Write(m_PlayerOnRail);
outBitStream->Write(m_ShowBillboard); outBitStream->Write(m_ShowBillboard);
if (!bIsInitialUpdate) m_DirtyInfo = false;
} }
m_DirtyInfo = false;
} }

View File

@@ -10,14 +10,13 @@
*/ */
class PlayerForcedMovementComponent : public Component { class PlayerForcedMovementComponent : public Component {
public: public:
static const eReplicaComponentType ComponentType = eReplicaComponentType::PLAYER_FORCED_MOVEMENT; inline static const eReplicaComponentType ComponentType = eReplicaComponentType::PLAYER_FORCED_MOVEMENT;
/** /**
* Constructor for this component * Constructor for this component
* @param parent parent that contains this component * @param parent parent that contains this component
*/ */
PlayerForcedMovementComponent(Entity* parent); PlayerForcedMovementComponent(Entity* parent) : Component(parent) {};
~PlayerForcedMovementComponent() override;
void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags); void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags);
@@ -26,28 +25,23 @@ public:
* *
* @param value if the player is on a rail * @param value if the player is on a rail
*/ */
void SetPlayerOnRail(bool value) { m_PlayerOnRail = value; m_DirtyInfo = true; } void SetPlayerOnRail(bool value) {
if (m_PlayerOnRail == value) return;
m_PlayerOnRail = value;
m_DirtyInfo = true;
}
/** /**
* @brief Set the Show Billboard object * @brief Set the Show Billboard object
* *
* @param value if the billboard should be shown * @param value if the billboard should be shown
*/ */
void SetShowBillboard(bool value) { m_ShowBillboard = value; m_DirtyInfo = true; } void SetShowBillboard(bool value) {
if (m_ShowBillboard == value) return;
m_ShowBillboard = value;
m_DirtyInfo = true;
}
/**
* @brief Get the Player On Rail object
*
* @return true
* @return false
*/
/**
* @brief Get the Player On Rail object
*
* @return true
* @return false
*/
bool GetPlayerOnRail() { return m_PlayerOnRail; } bool GetPlayerOnRail() { return m_PlayerOnRail; }
bool GetShowBillboard() { return m_ShowBillboard; } bool GetShowBillboard() { return m_ShowBillboard; }

View File

@@ -1,18 +1,22 @@
#include "PossessableComponent.h" #include "PossessableComponent.h"
#include "PossessorComponent.h" #include "PossessionComponent.h"
#include "EntityManager.h" #include "EntityManager.h"
#include "Inventory.h" #include "Inventory.h"
#include "Item.h" #include "Item.h"
PossessableComponent::PossessableComponent(Entity* parent, uint32_t componentId) : Component(parent) { PossessableComponent::PossessableComponent(Entity* parent, uint32_t componentId) : Component(parent) {
m_Possessor = LWOOBJID_EMPTY; m_Possessor = LWOOBJID_EMPTY;
CDItemComponent item = Inventory::FindItemComponent(m_Parent->GetLOT()); m_ComponentId = componentId;
}
void PossessableComponent::LoadTemplateData() {
auto item = Inventory::FindItemComponent(m_ParentEntity->GetLOT());
m_AnimationFlag = static_cast<eAnimationFlags>(item.animationFlag); m_AnimationFlag = static_cast<eAnimationFlags>(item.animationFlag);
// Get the possession Type from the CDClient // Get the possession Type from the CDClient
auto query = CDClientDatabase::CreatePreppedStmt("SELECT possessionType, depossessOnHit FROM PossessableComponent WHERE id = ?;"); auto query = CDClientDatabase::CreatePreppedStmt("SELECT possessionType, depossessOnHit FROM PossessableComponent WHERE id = ?;");
query.bind(1, static_cast<int>(componentId)); query.bind(1, static_cast<int32_t>(m_ComponentId));
auto result = query.execQuery(); auto result = query.execQuery();
@@ -24,13 +28,11 @@ PossessableComponent::PossessableComponent(Entity* parent, uint32_t componentId)
m_PossessionType = ePossessionType::ATTACHED_VISIBLE; m_PossessionType = ePossessionType::ATTACHED_VISIBLE;
m_DepossessOnHit = false; m_DepossessOnHit = false;
} }
result.finalize();
} }
void PossessableComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags) { void PossessableComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags) {
outBitStream->Write(m_DirtyPossessable || bIsInitialUpdate); outBitStream->Write(m_DirtyPossessable || bIsInitialUpdate);
if (m_DirtyPossessable || bIsInitialUpdate) { if (m_DirtyPossessable || bIsInitialUpdate) {
m_DirtyPossessable = false; // reset flag
outBitStream->Write(m_Possessor != LWOOBJID_EMPTY); outBitStream->Write(m_Possessor != LWOOBJID_EMPTY);
if (m_Possessor != LWOOBJID_EMPTY) outBitStream->Write(m_Possessor); if (m_Possessor != LWOOBJID_EMPTY) outBitStream->Write(m_Possessor);
@@ -38,18 +40,21 @@ void PossessableComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsIn
if (m_AnimationFlag != eAnimationFlags::IDLE_NONE) outBitStream->Write(m_AnimationFlag); if (m_AnimationFlag != eAnimationFlags::IDLE_NONE) outBitStream->Write(m_AnimationFlag);
outBitStream->Write(m_ImmediatelyDepossess); outBitStream->Write(m_ImmediatelyDepossess);
m_ImmediatelyDepossess = false; // reset flag if (!bIsInitialUpdate) {
m_DirtyPossessable = false;
m_ImmediatelyDepossess = false;
}
} }
} }
void PossessableComponent::Dismount() { void PossessableComponent::Dismount() {
SetPossessor(LWOOBJID_EMPTY); SetPossessor(LWOOBJID_EMPTY);
if (m_ItemSpawned) m_Parent->ScheduleKillAfterUpdate(); if (m_ItemSpawned) m_ParentEntity->ScheduleKillAfterUpdate();
} }
void PossessableComponent::OnUse(Entity* originator) { void PossessableComponent::OnUse(Entity* originator) {
auto* possessor = originator->GetComponent<PossessorComponent>(); auto* possessor = originator->GetComponent<PossessionComponent>();
if (possessor) { if (possessor) {
possessor->Mount(m_Parent); possessor->Mount(m_ParentEntity);
} }
} }

View File

@@ -4,7 +4,7 @@
#include "Entity.h" #include "Entity.h"
#include "Component.h" #include "Component.h"
#include "Item.h" #include "Item.h"
#include "PossessorComponent.h" #include "PossessionComponent.h"
#include "eAninmationFlags.h" #include "eAninmationFlags.h"
#include "eReplicaComponentType.h" #include "eReplicaComponentType.h"
@@ -14,16 +14,13 @@
*/ */
class PossessableComponent : public Component { class PossessableComponent : public Component {
public: public:
static const eReplicaComponentType ComponentType = eReplicaComponentType::POSSESSABLE; inline static const eReplicaComponentType ComponentType = eReplicaComponentType::POSSESSABLE;
PossessableComponent(Entity* parentEntity, uint32_t componentId); PossessableComponent(Entity* parentEntity, uint32_t componentId);
void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags); void LoadTemplateData() override;
/** void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags);
* @brief mounts the Entity
*/
void Mount();
/** /**
* @brief dismounts the Entity * @brief dismounts the Entity
@@ -34,7 +31,11 @@ public:
* Sets the possessor of this Entity * Sets the possessor of this Entity
* @param value the ID of the possessor to set * @param value the ID of the possessor to set
*/ */
void SetPossessor(LWOOBJID value) { m_Possessor = value; m_DirtyPossessable = true; }; void SetPossessor(const LWOOBJID& value) {
if (m_Possessor == value) return;
m_Possessor = value;
m_DirtyPossessable = true;
}
/** /**
* Returns the possessor of this Entity * Returns the possessor of this Entity
@@ -46,7 +47,11 @@ public:
* Sets the animation Flag of the possessable * Sets the animation Flag of the possessable
* @param value the animation flag to set to * @param value the animation flag to set to
*/ */
void SetAnimationFlag(eAnimationFlags value) { m_AnimationFlag = value; m_DirtyPossessable = true; }; void SetAnimationFlag(eAnimationFlags value) {
if (m_AnimationFlag == value) return;
m_AnimationFlag = value;
m_DirtyPossessable = true;
}
/** /**
* Returns the possession type of this Entity * Returns the possession type of this Entity
@@ -63,7 +68,10 @@ public:
/** /**
* Forcibly depossess the Entity * Forcibly depossess the Entity
*/ */
void ForceDepossess() { m_ImmediatelyDepossess = true; m_DirtyPossessable = true; }; void ForceDepossess() {
m_ImmediatelyDepossess = true;
m_DirtyPossessable = true;
}
/** /**
* Set if the parent entity was spawned from an item * Set if the parent entity was spawned from an item
@@ -123,4 +131,6 @@ private:
* *
*/ */
bool m_ItemSpawned = false; bool m_ItemSpawned = false;
int32_t m_ComponentId = -1;
}; };

View File

@@ -0,0 +1,82 @@
#include "PossessionComponent.h"
#include "PossessableComponent.h"
#include "CharacterComponent.h"
#include "EntityManager.h"
#include "GameMessages.h"
#include "eUnequippableActiveType.h"
#include "eControlScheme.h"
#include "eStateChangeType.h"
PossessionComponent::PossessionComponent(Entity* parent) : Component(parent) {
m_Possessable = LWOOBJID_EMPTY;
}
PossessionComponent::~PossessionComponent() {
if (m_Possessable == LWOOBJID_EMPTY) return;
auto* mount = EntityManager::Instance()->GetEntity(m_Possessable);
if (!mount) return;
auto* possessable = mount->GetComponent<PossessableComponent>();
if (!possessable) return;
if (possessable->GetIsItemSpawned()) {
GameMessages::SendMarkInventoryItemAsActive(m_ParentEntity->GetObjectID(), false, eUnequippableActiveType::MOUNT, GetMountItemID(), m_ParentEntity->GetSystemAddress());
}
possessable->Dismount();
}
void PossessionComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags) {
outBitStream->Write(m_DirtyPossesor || bIsInitialUpdate);
if (m_DirtyPossesor || bIsInitialUpdate) {
outBitStream->Write(m_Possessable != LWOOBJID_EMPTY);
if (m_Possessable != LWOOBJID_EMPTY) outBitStream->Write(m_Possessable);
outBitStream->Write(m_PossessableType);
if (!bIsInitialUpdate) m_DirtyPossesor = false;
}
}
void PossessionComponent::Mount(Entity* mount) {
// Don't do anything if we are busy dismounting
if (GetIsDismounting() || !mount) return;
GameMessages::SendSetMountInventoryID(m_ParentEntity, mount->GetObjectID(), UNASSIGNED_SYSTEM_ADDRESS);
auto* possessableComponent = mount->GetComponent<PossessableComponent>();
if (possessableComponent) {
possessableComponent->SetPossessor(m_ParentEntity->GetObjectID());
SetPossessable(mount->GetObjectID());
SetPossessableType(possessableComponent->GetPossessionType());
}
auto* characterComponent = m_ParentEntity->GetComponent<CharacterComponent>();
if (characterComponent) characterComponent->SetIsRacing(true);
// GM's to send
GameMessages::SendSetJetPackMode(m_ParentEntity, false);
GameMessages::SendVehicleUnlockInput(mount->GetObjectID(), false, m_ParentEntity->GetSystemAddress());
GameMessages::SendSetStunned(m_ParentEntity->GetObjectID(), eStateChangeType::PUSH, m_ParentEntity->GetSystemAddress(), LWOOBJID_EMPTY, true, false, true, false, false, false, false, true, true, true, true, true, true, true, true, true);
EntityManager::Instance()->SerializeEntity(m_ParentEntity);
EntityManager::Instance()->SerializeEntity(mount);
}
void PossessionComponent::Dismount(Entity* mount, bool forceDismount) {
// Don't do anything if we are busy dismounting
if (GetIsDismounting() || !mount) return;
SetIsDismounting(true);
// Make sure we don't have wacky controls
GameMessages::SendSetPlayerControlScheme(m_ParentEntity, eControlScheme::SCHEME_A);
if (!mount) return;
auto* possessableComponent = mount->GetComponent<PossessableComponent>();
if (possessableComponent) {
possessableComponent->SetPossessor(LWOOBJID_EMPTY);
if (forceDismount) possessableComponent->ForceDepossess();
}
EntityManager::Instance()->SerializeEntity(m_ParentEntity);
EntityManager::Instance()->SerializeEntity(mount);
auto* characterComponent = m_ParentEntity->GetComponent<CharacterComponent>();
if (characterComponent) characterComponent->SetIsRacing(false);
}

View File

@@ -16,12 +16,12 @@ enum class ePossessionType : uint8_t {
/** /**
* Represents an entity that can posess other entities. Generally used by players to drive a car. * Represents an entity that can posess other entities. Generally used by players to drive a car.
*/ */
class PossessorComponent : public Component { class PossessionComponent : public Component {
public: public:
static const eReplicaComponentType ComponentType = eReplicaComponentType::POSSESSOR; inline static const eReplicaComponentType ComponentType = eReplicaComponentType::POSSESSION;
PossessorComponent(Entity* parent); PossessionComponent(Entity* parent);
~PossessorComponent() override; ~PossessionComponent() override;
void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags); void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags);
@@ -44,7 +44,11 @@ public:
* Sets the ID that this entity is possessing * Sets the ID that this entity is possessing
* @param value The ID that this entity is possessing * @param value The ID that this entity is possessing
*/ */
void SetPossessable(LWOOBJID value) { m_Possessable = value; m_DirtyPossesor = true; } void SetPossessable(const LWOOBJID& value) {
if (m_Possessable == value) return;
m_Possessable = value;
m_DirtyPossesor = true;
}
/** /**
* Returns the entity that this entity is currently posessing * Returns the entity that this entity is currently posessing
@@ -68,7 +72,11 @@ public:
* Sets the possesible type that's currently used, merely used by the shooting gallery if it's 0 * Sets the possesible type that's currently used, merely used by the shooting gallery if it's 0
* @param value The possesible type to set * @param value The possesible type to set
*/ */
void SetPossessableType(ePossessionType value) { m_PossessableType = value; m_DirtyPossesor = true; } void SetPossessableType(ePossessionType value) {
if (m_PossessableType == value) return;
m_PossessableType = value;
m_DirtyPossesor = true;
}
/** /**
@@ -97,13 +105,13 @@ private:
ePossessionType m_PossessableType = ePossessionType::NO_POSSESSION; ePossessionType m_PossessableType = ePossessionType::NO_POSSESSION;
/** /**
* @brief If the possessor is dirty * @brief If the possession is dirty
* *
*/ */
bool m_DirtyPossesor = false; bool m_DirtyPossesor = false;
/** /**
* @brief If the possessor is busy dismounting * @brief If the possession is busy dismounting
* *
*/ */
bool m_IsDismounting = false; bool m_IsDismounting = false;

Some files were not shown because too many files have changed in this diff Show More