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
insert_final_newline=true
[*.{c++,cc,cpp,cxx,h,h++,hh,hpp,hxx,inl,ipp,tlh,tli}]
[*.{c++,cc,cpp,cxx,h,h++,hh,hpp,hxx,inl,ipp,tlh,tli,tcc,tpp}]
vc_generate_documentation_comments=doxygen_slash_star

View File

@@ -305,7 +305,7 @@ file(
file(
GLOB HEADERS_DGAME
LIST_DIRECTORIES false
${PROJECT_SOURCE_DIR}/dGame/Entity.h
${PROJECT_SOURCE_DIR}/dGame/dEntity/Entity.h
${PROJECT_SOURCE_DIR}/dGame/dGameMessages/GameMessages.h
${PROJECT_SOURCE_DIR}/dGame/EntityManager.h
${PROJECT_SOURCE_DIR}/dScripts/CppScripts.h

View File

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

View File

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

View File

@@ -56,6 +56,13 @@ const uint32_t LWOCLONEID_INVALID = -1; //!< Invalid LWOCLONEID
const uint16_t LWOINSTANCEID_INVALID = -1; //!< Invalid LWOINSTANCEID
const uint16_t LWOMAPID_INVALID = -1; //!< Invalid LWOMAPID
const uint64_t LWOZONEID_INVALID = 0; //!< Invalid LWOZONEID
const LOT LOT_ZONE_CONTROL = 2365;
const LOT LOT_3D_AMBIENT_SOUND = 6368;
const LOT LOT_MODEL_IN_WORLD = 14;
const LOT LOT_THINKING_CAP = 6086;
const LOT LOT_ROCKET = 6416;
const LOT LOT_LEGACY_RESPAWN_POINT = 4945;
const float PI = 3.14159f;

View File

@@ -0,0 +1,14 @@
#ifndef __EPHYSICSBEHAVIORTYPE__H__
#define __EPHYSICSBEHAVIORTYPE__H__
#include <cstdint>
enum class ePhysicsBehaviorType : int32_t {
INVALID = -1,
GROUND,
FLYING,
STANDARD,
DYNAMIC
};
#endif //!__EPHYSICSBEHAVIORTYPE__H__

View File

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

View File

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

View File

@@ -90,4 +90,11 @@ int32_t CDComponentsRegistryTable::GetByIDAndType(uint32_t id, eReplicaComponent
return defaultValue;
#endif
}
std::vector<std::pair<eReplicaComponentType, uint32_t>> CDComponentsRegistryTable::GetTemplateComponents(LOT templateId) {
std::vector<std::pair<eReplicaComponentType, uint32_t>> components;
for (int8_t i = 0; static_cast<eReplicaComponentType>(i) < eReplicaComponentType::NUMBER_OF_COMPONENTS; i++) {
auto compId = GetByIDAndType(templateId, static_cast<eReplicaComponentType>(i), -1);
if (compId != -1) components.push_back(std::make_pair(static_cast<eReplicaComponentType>(i), compId));
}
return components;
}

View File

@@ -2,6 +2,7 @@
// Custom Classes
#include "CDTable.h"
#include "dCommonVars.h"
enum class eReplicaComponentType : uint32_t;
struct CDComponentsRegistry {
@@ -18,4 +19,5 @@ private:
public:
CDComponentsRegistryTable();
int32_t GetByIDAndType(uint32_t id, eReplicaComponentType componentType, int32_t defaultValue = 0);
std::vector<std::pair<eReplicaComponentType, uint32_t>> GetTemplateComponents(LOT templateId);
};

View File

@@ -1,6 +1,8 @@
#include "CDItemComponentTable.h"
#include "GeneralUtils.h"
#include "eItemType.h"
CDItemComponent CDItemComponentTable::Default = {};
//! Constructor
@@ -74,8 +76,8 @@ CDItemComponentTable::CDItemComponentTable(void) {
#endif
}
const CDItemComponent& CDItemComponentTable::GetItemComponentByID(unsigned int skillID) {
const auto& it = this->entries.find(skillID);
const CDItemComponent& CDItemComponentTable::GetItemComponentByID(unsigned int id) {
const auto& it = this->entries.find(id);
if (it != this->entries.end()) {
return it->second;
}
@@ -83,11 +85,11 @@ const CDItemComponent& CDItemComponentTable::GetItemComponentByID(unsigned int s
#ifndef CDCLIENT_CACHE_ALL
std::stringstream query;
query << "SELECT * FROM ItemComponent WHERE id = " << std::to_string(skillID);
query << "SELECT * FROM ItemComponent WHERE id = " << std::to_string(id);
auto tableData = CDClientDatabase::ExecuteQuery(query.str());
if (tableData.eof()) {
entries.insert(std::make_pair(skillID, Default));
entries.insert(std::make_pair(id, Default));
return Default;
}
@@ -98,7 +100,7 @@ const CDItemComponent& CDItemComponentTable::GetItemComponentByID(unsigned int s
entry.baseValue = tableData.getIntField("baseValue", -1);
entry.isKitPiece = tableData.getIntField("isKitPiece", -1) == 1 ? true : false;
entry.rarity = tableData.getIntField("rarity", 0);
entry.itemType = tableData.getIntField("itemType", -1);
entry.itemType = static_cast<eItemType>(tableData.getIntField("itemType", -1));
entry.itemInfo = tableData.getInt64Field("itemInfo", -1);
entry.inLootTable = tableData.getIntField("inLootTable", -1) == 1 ? true : false;
entry.inVendor = tableData.getIntField("inVendor", -1) == 1 ? true : false;
@@ -140,7 +142,7 @@ const CDItemComponent& CDItemComponentTable::GetItemComponentByID(unsigned int s
tableData.nextRow();
}
const auto& it2 = this->entries.find(skillID);
const auto& it2 = this->entries.find(id);
if (it2 != this->entries.end()) {
return it2->second;
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -33,7 +33,7 @@ void StunBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bitStream
* If our target is an enemy we can go ahead and stun it.
*/
auto* combatAiComponent = static_cast<BaseCombatAIComponent*>(target->GetComponent(eReplicaComponentType::BASE_COMBAT_AI));
auto* combatAiComponent = target->GetComponent<BaseCombatAIComponent>();
if (combatAiComponent == nullptr) {
return;
@@ -56,7 +56,7 @@ void StunBehavior::Calculate(BehaviorContext* context, RakNet::BitStream* bitStr
* See if we can stun ourselves
*/
auto* combatAiComponent = static_cast<BaseCombatAIComponent*>(self->GetComponent(eReplicaComponentType::BASE_COMBAT_AI));
auto* combatAiComponent = self->GetComponent<BaseCombatAIComponent>();
if (combatAiComponent == nullptr) {
return;
@@ -91,7 +91,7 @@ void StunBehavior::Calculate(BehaviorContext* context, RakNet::BitStream* bitStr
* If our target is an enemy we can go ahead and stun it.
*/
auto* combatAiComponent = static_cast<BaseCombatAIComponent*>(target->GetComponent(eReplicaComponentType::BASE_COMBAT_AI));
auto* combatAiComponent = target->GetComponent<BaseCombatAIComponent>();
if (combatAiComponent == nullptr) {
return;

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,16 @@
#ifndef __ACHIEVEMENTVENDORCOMPONENT__H__
#define __ACHIEVEMENTVENDORCOMPONENT__H__
#include "VendorComponent.h"
#include "eReplicaComponentType.h"
class Entity;
class AchievementVendorComponent final : public VendorComponent {
public:
inline static const eReplicaComponentType ComponentType = eReplicaComponentType::ACHIEVEMENT_VENDOR;
AchievementVendorComponent(Entity* parent);
};
#endif //!__ACHIEVEMENTVENDORCOMPONENT__H__

View File

@@ -0,0 +1,621 @@
#include "ActivityComponent.h"
#include "GameMessages.h"
#include "CDClientManager.h"
#include "MissionComponent.h"
#include "Character.h"
#include "dZoneManager.h"
#include "ZoneInstanceManager.h"
#include "Game.h"
#include "dLogger.h"
#include <WorldPackets.h>
#include "EntityManager.h"
#include "ChatPackets.h"
#include "Player.h"
#include "PacketUtils.h"
#include "dServer.h"
#include "GeneralUtils.h"
#include "dZoneManager.h"
#include "dConfig.h"
#include "InventoryComponent.h"
#include "DestroyableComponent.h"
#include "Loot.h"
#include "eMissionTaskType.h"
#include "eMatchUpdate.h"
#include "eConnectionType.h"
#include "eChatInternalMessageType.h"
#include "CDCurrencyTableTable.h"
#include "CDActivityRewardsTable.h"
#include "CDActivitiesTable.h"
#include "LeaderboardManager.h"
ActivityComponent::ActivityComponent(Entity* parent, int32_t componentId) : Component(parent) {
m_ActivityID = componentId;
CDActivitiesTable* activitiesTable = CDClientManager::Instance().GetTable<CDActivitiesTable>();
std::vector<CDActivities> activities = activitiesTable->Query([=](CDActivities entry) {return (entry.ActivityID == m_ActivityID); });
for (CDActivities activity : activities) {
m_ActivityInfo = activity;
if (static_cast<LeaderboardType>(activity.leaderboardType) == LeaderboardType::Racing && Game::config->GetValue("solo_racing") == "1") {
m_ActivityInfo.minTeamSize = 1;
m_ActivityInfo.minTeams = 1;
}
const auto& transferOverride = parent->GetVar<std::u16string>(u"transferZoneID");
if (!transferOverride.empty()) {
m_ActivityInfo.instanceMapID = std::stoi(GeneralUtils::UTF16ToWTF8(transferOverride));
// TODO: LU devs made me do it (for some reason cannon cove instancer is marked to go to GF survival)
// NOTE: 1301 is GF survival
if (m_ActivityInfo.instanceMapID == 1301) {
m_ActivityInfo.instanceMapID = 1302;
}
}
}
auto* destroyableComponent = m_ParentEntity->GetComponent<DestroyableComponent>();
if (destroyableComponent) {
// check for LMIs and set the loot LMIs
CDActivityRewardsTable* activityRewardsTable = CDClientManager::Instance().GetTable<CDActivityRewardsTable>();
std::vector<CDActivityRewards> activityRewards = activityRewardsTable->Query([=](CDActivityRewards entry) {return (entry.LootMatrixIndex == destroyableComponent->GetLootMatrixID()); });
uint32_t startingLMI = 0;
if (activityRewards.size() > 0) {
startingLMI = activityRewards[0].LootMatrixIndex;
}
if (startingLMI > 0) {
// now time for bodge :)
std::vector<CDActivityRewards> objectTemplateActivities = activityRewardsTable->Query([=](CDActivityRewards entry) {return (activityRewards[0].objectTemplate == entry.objectTemplate); });
for (const auto& item : objectTemplateActivities) {
if (item.activityRating > 0 && item.activityRating < 5) {
m_ActivityLootMatrices.insert({ item.activityRating, item.LootMatrixIndex });
}
}
}
}
}
ActivityComponent::~ActivityComponent()
= default;
void ActivityComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags) const {
outBitStream->Write(true);
outBitStream->Write<uint32_t>(m_ActivityPlayers.size());
if (!m_ActivityPlayers.empty()) {
for (const auto& activityPlayer : m_ActivityPlayers) {
outBitStream->Write<LWOOBJID>(activityPlayer->playerID);
for (const auto& activityValue : activityPlayer->values) {
outBitStream->Write<float_t>(activityValue);
}
}
}
}
void ActivityComponent::ReloadConfig() {
CDActivitiesTable* activitiesTable = CDClientManager::Instance().GetTable<CDActivitiesTable>();
std::vector<CDActivities> activities = activitiesTable->Query([=](CDActivities entry) {return (entry.ActivityID == m_ActivityID); });
for (auto activity : activities) {
auto mapID = m_ActivityInfo.instanceMapID;
if ((mapID == 1203 || mapID == 1261 || mapID == 1303 || mapID == 1403) && Game::config->GetValue("solo_racing") == "1") {
m_ActivityInfo.minTeamSize = 1;
m_ActivityInfo.minTeams = 1;
} else {
m_ActivityInfo.minTeamSize = activity.minTeamSize;
m_ActivityInfo.minTeams = activity.minTeams;
}
}
}
void ActivityComponent::HandleMessageBoxResponse(Entity* player, const std::string& id) {
if (m_ActivityInfo.ActivityID == 103) {
return;
}
if (id == "LobbyExit") {
PlayerLeave(player->GetObjectID());
} else if (id == "PlayButton") {
PlayerJoin(player);
}
}
void ActivityComponent::PlayerJoin(Entity* player) {
if (m_ActivityInfo.ActivityID == 103 || PlayerIsInQueue(player) || !IsValidActivity(player)) {
return;
}
// If we have a lobby, queue the player and allow others to join, otherwise spin up an instance on the spot
if (HasLobby()) {
PlayerJoinLobby(player);
} else if (!IsPlayedBy(player)) {
auto* instance = NewInstance();
instance->AddParticipant(player);
}
EntityManager::Instance()->SerializeEntity(m_ParentEntity);
}
void ActivityComponent::PlayerJoinLobby(Entity* player) {
if (!m_ParentEntity->HasComponent(eReplicaComponentType::QUICK_BUILD))
GameMessages::SendMatchResponse(player, player->GetSystemAddress(), 0); // tell the client they joined a lobby
LobbyPlayer* newLobbyPlayer = new LobbyPlayer();
newLobbyPlayer->entityID = player->GetObjectID();
Lobby* playerLobby = nullptr;
auto* character = player->GetCharacter();
if (character != nullptr)
character->SetLastNonInstanceZoneID(dZoneManager::Instance()->GetZone()->GetWorldID());
for (Lobby* lobby : m_Queue) {
if (lobby->players.size() < m_ActivityInfo.maxTeamSize || m_ActivityInfo.maxTeamSize == 1 && lobby->players.size() < m_ActivityInfo.maxTeams) {
// If an empty slot in an existing lobby is found
lobby->players.push_back(newLobbyPlayer);
playerLobby = lobby;
// Update the joining player on players already in the lobby, and update players already in the lobby on the joining player
std::string matchUpdateJoined = "player=9:" + std::to_string(player->GetObjectID()) + "\nplayerName=0:" + player->GetCharacter()->GetName();
for (LobbyPlayer* joinedPlayer : lobby->players) {
auto* entity = joinedPlayer->GetEntity();
if (entity == nullptr) {
continue;
}
std::string matchUpdate = "player=9:" + std::to_string(entity->GetObjectID()) + "\nplayerName=0:" + entity->GetCharacter()->GetName();
GameMessages::SendMatchUpdate(player, player->GetSystemAddress(), matchUpdate, eMatchUpdate::PLAYER_ADDED);
PlayerReady(entity, joinedPlayer->ready);
GameMessages::SendMatchUpdate(entity, entity->GetSystemAddress(), matchUpdateJoined, eMatchUpdate::PLAYER_ADDED);
}
}
}
if (!playerLobby) {
// If all lobbies are full
playerLobby = new Lobby();
playerLobby->players.push_back(newLobbyPlayer);
playerLobby->timer = m_ActivityInfo.waitTime / 1000;
m_Queue.push_back(playerLobby);
}
if (m_ActivityInfo.maxTeamSize != 1 && playerLobby->players.size() >= m_ActivityInfo.minTeamSize || m_ActivityInfo.maxTeamSize == 1 && playerLobby->players.size() >= m_ActivityInfo.minTeams) {
// Update the joining player on the match timer
std::string matchTimerUpdate = "time=3:" + std::to_string(playerLobby->timer);
GameMessages::SendMatchUpdate(player, player->GetSystemAddress(), matchTimerUpdate, eMatchUpdate::PHASE_WAIT_READY);
}
}
void ActivityComponent::PlayerLeave(LWOOBJID playerID) {
// Removes the player from a lobby and notifies the others, not applicable for non-lobby instances
for (Lobby* lobby : m_Queue) {
for (int i = 0; i < lobby->players.size(); ++i) {
if (lobby->players[i]->entityID == playerID) {
std::string matchUpdateLeft = "player=9:" + std::to_string(playerID);
for (LobbyPlayer* lobbyPlayer : lobby->players) {
auto* entity = lobbyPlayer->GetEntity();
if (entity == nullptr)
continue;
GameMessages::SendMatchUpdate(entity, entity->GetSystemAddress(), matchUpdateLeft, eMatchUpdate::PLAYER_REMOVED);
}
delete lobby->players[i];
lobby->players[i] = nullptr;
lobby->players.erase(lobby->players.begin() + i);
return;
}
}
}
}
void ActivityComponent::Update(float deltaTime) {
std::vector<Lobby*> lobbiesToRemove{};
// Ticks all the lobbies, not applicable for non-instance activities
for (Lobby* lobby : m_Queue) {
for (LobbyPlayer* player : lobby->players) {
auto* entity = player->GetEntity();
if (entity == nullptr) {
PlayerLeave(player->entityID);
return;
}
}
if (lobby->players.empty()) {
lobbiesToRemove.push_back(lobby);
continue;
}
// Update the match time for all players
if (m_ActivityInfo.maxTeamSize != 1 && lobby->players.size() >= m_ActivityInfo.minTeamSize
|| m_ActivityInfo.maxTeamSize == 1 && lobby->players.size() >= m_ActivityInfo.minTeams) {
if (lobby->timer == m_ActivityInfo.waitTime / 1000) {
for (LobbyPlayer* joinedPlayer : lobby->players) {
auto* entity = joinedPlayer->GetEntity();
if (entity == nullptr)
continue;
std::string matchTimerUpdate = "time=3:" + std::to_string(lobby->timer);
GameMessages::SendMatchUpdate(entity, entity->GetSystemAddress(), matchTimerUpdate, eMatchUpdate::PHASE_WAIT_READY);
}
}
lobby->timer -= deltaTime;
}
bool lobbyReady = true;
for (LobbyPlayer* player : lobby->players) {
if (player->ready) continue;
lobbyReady = false;
}
// If everyone's ready, jump the timer
if (lobbyReady && lobby->timer > m_ActivityInfo.startDelay / 1000) {
lobby->timer = m_ActivityInfo.startDelay / 1000;
// Update players in lobby on switch to start delay
std::string matchTimerUpdate = "time=3:" + std::to_string(lobby->timer);
for (LobbyPlayer* player : lobby->players) {
auto* entity = player->GetEntity();
if (entity == nullptr)
continue;
GameMessages::SendMatchUpdate(entity, entity->GetSystemAddress(), matchTimerUpdate, eMatchUpdate::PHASE_WAIT_START);
}
}
// The timer has elapsed, start the instance
if (lobby->timer <= 0.0f) {
Game::logger->Log("ActivityComponent", "Setting up instance.");
ActivityInstance* instance = NewInstance();
LoadPlayersIntoInstance(instance, lobby->players);
instance->StartZone();
lobbiesToRemove.push_back(lobby);
}
}
while (!lobbiesToRemove.empty()) {
RemoveLobby(lobbiesToRemove.front());
lobbiesToRemove.erase(lobbiesToRemove.begin());
}
}
void ActivityComponent::RemoveLobby(Lobby* lobby) {
for (int i = 0; i < m_Queue.size(); ++i) {
if (m_Queue[i] == lobby) {
m_Queue.erase(m_Queue.begin() + i);
return;
}
}
}
bool ActivityComponent::HasLobby() const {
// If the player is not in the world he has to be, create a lobby for the transfer
return m_ActivityInfo.instanceMapID != UINT_MAX && m_ActivityInfo.instanceMapID != Game::server->GetZoneID();
}
bool ActivityComponent::IsValidActivity(Entity* player) {
// Makes it so that scripted activities with an unimplemented map cannot be joined
/*if (player->GetGMLevel() < eGameMasterLevel::DEVELOPER && (m_ActivityInfo.instanceMapID == 1302 || m_ActivityInfo.instanceMapID == 1301)) {
if (m_ParentEntity->GetLOT() == 4860) {
auto* missionComponent = player->GetComponent<MissionComponent>();
missionComponent->CompleteMission(229);
}
ChatPackets::SendSystemMessage(player->GetSystemAddress(), u"Sorry, this activity is not ready.");
static_cast<Player*>(player)->SendToZone(dZoneManager::Instance()->GetZone()->GetWorldID()); // Gets them out of this stuck state
return false;
}*/
return true;
}
bool ActivityComponent::PlayerIsInQueue(Entity* player) {
for (Lobby* lobby : m_Queue) {
for (LobbyPlayer* lobbyPlayer : lobby->players) {
if (player->GetObjectID() == lobbyPlayer->entityID) return true;
}
}
return false;
}
bool ActivityComponent::IsPlayedBy(Entity* player) const {
for (const auto* instance : this->m_Instances) {
for (const auto* instancePlayer : instance->GetParticipants()) {
if (instancePlayer != nullptr && instancePlayer->GetObjectID() == player->GetObjectID())
return true;
}
}
return false;
}
bool ActivityComponent::IsPlayedBy(LWOOBJID playerID) const {
for (const auto* instance : this->m_Instances) {
for (const auto* instancePlayer : instance->GetParticipants()) {
if (instancePlayer != nullptr && instancePlayer->GetObjectID() == playerID)
return true;
}
}
return false;
}
bool ActivityComponent::TakeCost(Entity* player) const {
if (m_ActivityInfo.optionalCostLOT <= 0 || m_ActivityInfo.optionalCostCount <= 0)
return true;
auto* inventoryComponent = player->GetComponent<InventoryComponent>();
if (inventoryComponent == nullptr)
return false;
if (inventoryComponent->GetLotCount(m_ActivityInfo.optionalCostLOT) < m_ActivityInfo.optionalCostCount)
return false;
inventoryComponent->RemoveItem(m_ActivityInfo.optionalCostLOT, m_ActivityInfo.optionalCostCount);
return true;
}
void ActivityComponent::PlayerReady(Entity* player, bool bReady) {
for (Lobby* lobby : m_Queue) {
for (LobbyPlayer* lobbyPlayer : lobby->players) {
if (lobbyPlayer->entityID == player->GetObjectID()) {
lobbyPlayer->ready = bReady;
// Update players in lobby on player being ready
std::string matchReadyUpdate = "player=9:" + std::to_string(player->GetObjectID());
eMatchUpdate readyStatus = eMatchUpdate::PLAYER_READY;
if (!bReady) readyStatus = eMatchUpdate::PLAYER_NOT_READY;
for (LobbyPlayer* otherPlayer : lobby->players) {
auto* entity = otherPlayer->GetEntity();
if (entity == nullptr)
continue;
GameMessages::SendMatchUpdate(entity, entity->GetSystemAddress(), matchReadyUpdate, readyStatus);
}
}
}
}
}
ActivityInstance* ActivityComponent::NewInstance() {
auto* instance = new ActivityInstance(m_ParentEntity, m_ActivityInfo);
m_Instances.push_back(instance);
return instance;
}
void ActivityComponent::LoadPlayersIntoInstance(ActivityInstance* instance, const std::vector<LobbyPlayer*>& lobby) const {
for (LobbyPlayer* player : lobby) {
auto* entity = player->GetEntity();
if (entity == nullptr || !TakeCost(entity)) {
continue;
}
instance->AddParticipant(entity);
}
}
const std::vector<ActivityInstance*>& ActivityComponent::GetInstances() const {
return m_Instances;
}
ActivityInstance* ActivityComponent::GetInstance(const LWOOBJID playerID) {
for (const auto* instance : GetInstances()) {
for (const auto* participant : instance->GetParticipants()) {
if (participant->GetObjectID() == playerID)
return const_cast<ActivityInstance*>(instance);
}
}
return nullptr;
}
void ActivityComponent::ClearInstances() {
for (ActivityInstance* instance : m_Instances) {
delete instance;
}
m_Instances.clear();
}
ActivityPlayer* ActivityComponent::GetActivityPlayerData(LWOOBJID playerID) {
for (auto* activityData : m_ActivityPlayers) {
if (activityData->playerID == playerID) {
return activityData;
}
}
return nullptr;
}
void ActivityComponent::RemoveActivityPlayerData(LWOOBJID playerID) {
for (size_t i = 0; i < m_ActivityPlayers.size(); i++) {
if (m_ActivityPlayers[i]->playerID == playerID) {
delete m_ActivityPlayers[i];
m_ActivityPlayers[i] = nullptr;
m_ActivityPlayers.erase(m_ActivityPlayers.begin() + i);
EntityManager::Instance()->SerializeEntity(m_ParentEntity);
return;
}
}
}
ActivityPlayer* ActivityComponent::AddActivityPlayerData(LWOOBJID playerID) {
auto* data = GetActivityPlayerData(playerID);
if (data != nullptr)
return data;
m_ActivityPlayers.push_back(new ActivityPlayer{ playerID, {} });
EntityManager::Instance()->SerializeEntity(m_ParentEntity);
return GetActivityPlayerData(playerID);
}
float_t ActivityComponent::GetActivityValue(LWOOBJID playerID, uint32_t index) {
auto value = -1.0f;
auto* data = GetActivityPlayerData(playerID);
if (data != nullptr) {
value = data->values[std::min(index, (uint32_t)9)];
}
return value;
}
void ActivityComponent::SetActivityValue(LWOOBJID playerID, uint32_t index, float_t value) {
auto* data = AddActivityPlayerData(playerID);
if (data != nullptr) {
data->values[std::min(index, (uint32_t)9)] = value;
}
EntityManager::Instance()->SerializeEntity(m_ParentEntity);
}
void ActivityComponent::PlayerRemove(LWOOBJID playerID) {
for (auto* instance : GetInstances()) {
auto participants = instance->GetParticipants();
for (const auto* participant : participants) {
if (participant != nullptr && participant->GetObjectID() == playerID) {
instance->RemoveParticipant(participant);
RemoveActivityPlayerData(playerID);
// If the instance is empty after the delete of the participant, delete the instance too
if (instance->GetParticipants().empty()) {
m_Instances.erase(std::find(m_Instances.begin(), m_Instances.end(), instance));
delete instance;
}
return;
}
}
}
}
void ActivityInstance::StartZone() {
if (m_Participants.empty())
return;
const auto& participants = GetParticipants();
if (participants.empty())
return;
auto* leader = participants[0];
LWOZONEID zoneId = LWOZONEID(m_ActivityInfo.instanceMapID, 0, leader->GetCharacter()->GetPropertyCloneID());
// only make a team if we have more than one participant
if (participants.size() > 1) {
CBITSTREAM;
PacketUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::CREATE_TEAM);
bitStream.Write(leader->GetObjectID());
bitStream.Write(m_Participants.size());
for (const auto& participant : m_Participants) {
bitStream.Write(participant);
}
bitStream.Write(zoneId);
Game::chatServer->Send(&bitStream, SYSTEM_PRIORITY, RELIABLE, 0, Game::chatSysAddr, false);
}
const auto cloneId = GeneralUtils::GenerateRandomNumber<uint32_t>(1, UINT32_MAX);
for (Entity* player : participants) {
const auto objid = player->GetObjectID();
ZoneInstanceManager::Instance()->RequestZoneTransfer(Game::server, m_ActivityInfo.instanceMapID, cloneId, false, [objid](bool mythranShift, uint32_t zoneID, uint32_t zoneInstance, uint32_t zoneClone, std::string serverIP, uint16_t serverPort) {
auto* player = EntityManager::Instance()->GetEntity(objid);
if (player == nullptr)
return;
Game::logger->Log("UserManager", "Transferring %s to Zone %i (Instance %i | Clone %i | Mythran Shift: %s) with IP %s and Port %i", player->GetCharacter()->GetName().c_str(), zoneID, zoneInstance, zoneClone, mythranShift == true ? "true" : "false", serverIP.c_str(), serverPort);
if (player->GetCharacter()) {
player->GetCharacter()->SetZoneID(zoneID);
player->GetCharacter()->SetZoneInstance(zoneInstance);
player->GetCharacter()->SetZoneClone(zoneClone);
}
WorldPackets::SendTransferToWorld(player->GetSystemAddress(), serverIP, serverPort, mythranShift);
return;
});
}
m_NextZoneCloneID++;
}
void ActivityInstance::RewardParticipant(Entity* participant) {
auto* missionComponent = participant->GetComponent<MissionComponent>();
if (missionComponent) {
missionComponent->Progress(eMissionTaskType::ACTIVITY, m_ActivityInfo.ActivityID);
}
// First, get the activity data
auto* activityRewardsTable = CDClientManager::Instance().GetTable<CDActivityRewardsTable>();
std::vector<CDActivityRewards> activityRewards = activityRewardsTable->Query([=](CDActivityRewards entry) { return (entry.objectTemplate == m_ActivityInfo.ActivityID); });
if (!activityRewards.empty()) {
uint32_t minCoins = 0;
uint32_t maxCoins = 0;
auto* currencyTableTable = CDClientManager::Instance().GetTable<CDCurrencyTableTable>();
std::vector<CDCurrencyTable> currencyTable = currencyTableTable->Query([=](CDCurrencyTable entry) { return (entry.currencyIndex == activityRewards[0].CurrencyIndex && entry.npcminlevel == 1); });
if (!currencyTable.empty()) {
minCoins = currencyTable[0].minvalue;
maxCoins = currencyTable[0].maxvalue;
}
LootGenerator::Instance().DropLoot(participant, m_ParentEntity, activityRewards[0].LootMatrixIndex, minCoins, maxCoins);
}
}
std::vector<Entity*> ActivityInstance::GetParticipants() const {
std::vector<Entity*> entities;
entities.reserve(m_Participants.size());
for (const auto& id : m_Participants) {
auto* entity = EntityManager::Instance()->GetEntity(id);
if (entity != nullptr)
entities.push_back(entity);
}
return entities;
}
void ActivityInstance::AddParticipant(Entity* participant) {
const auto id = participant->GetObjectID();
if (std::count(m_Participants.begin(), m_Participants.end(), id))
return;
m_Participants.push_back(id);
}
void ActivityInstance::RemoveParticipant(const Entity* participant) {
const auto loadedParticipant = std::find(m_Participants.begin(), m_Participants.end(), participant->GetObjectID());
if (loadedParticipant != m_Participants.end()) {
m_Participants.erase(loadedParticipant);
}
}
uint32_t ActivityInstance::GetScore() const {
return score;
}
void ActivityInstance::SetScore(uint32_t score) {
this->score = score;
}
Entity* LobbyPlayer::GetEntity() const {
return EntityManager::Instance()->GetEntity(entityID);
}

View File

@@ -0,0 +1,379 @@
/*
* Darkflame Universe
* Copyright 2018
*/
#include "CDClientManager.h"
#ifndef ACTIVITYCOMPONENT_H
#define ACTIVITYCOMPONENT_H
#include "BitStream.h"
#include "Entity.h"
#include "Component.h"
#include "eReplicaComponentType.h"
#include "CDActivitiesTable.h"
/**
* Represents an instance of an activity, having participants and score
*/
class ActivityInstance {
public:
ActivityInstance(Entity* parent, CDActivities activityInfo) { m_ParentEntity = parent; m_ActivityInfo = activityInfo; };
//~ActivityInstance();
/**
* Adds an entity to this activity
* @param participant the entity to add
*/
void AddParticipant(Entity* participant);
/**
* Removes all the participants from this activity
*/
void ClearParticipants() { m_Participants.clear(); };
/**
* Starts the instance world for this activity and sends all participants there
*/
void StartZone();
/**
* Gives the rewards for completing this activity to some participant
* @param participant the participant to give rewards to
*/
void RewardParticipant(Entity* participant);
/**
* Removes a participant from this activity
* @param participant the participant to remove
*/
void RemoveParticipant(const Entity* participant);
/**
* Returns all the participants of this activity
* @return all the participants of this activity
*/
std::vector<Entity*> GetParticipants() const;
/**
* Currently unused
*/
uint32_t GetScore() const;
/**
* Currently unused
*/
void SetScore(uint32_t score);
private:
/**
* Currently unused
*/
uint32_t score = 0;
/**
* The instance ID of this activity
*/
uint32_t m_NextZoneCloneID = 0;
/**
* The database information for this activity
*/
CDActivities m_ActivityInfo;
/**
* The entity that owns this activity (the entity that has the ActivityComponent)
*/
Entity* m_ParentEntity;
/**
* All the participants of this activity
*/
std::vector<LWOOBJID> m_Participants;
};
/**
* Represents an entity in a lobby
*/
struct LobbyPlayer {
/**
* The ID of the entity that is in the lobby
*/
LWOOBJID entityID;
/**
* Whether or not the entity is ready
*/
bool ready = false;
/**
* Returns the entity that is in the lobby
* @return the entity that is in the lobby
*/
Entity* GetEntity() const;
};
/**
* Represents a lobby of players with a timer until it should start the activity
*/
struct Lobby {
/**
* The lobby of players
*/
std::vector<LobbyPlayer*> players;
/**
* The timer that determines when the activity should start
*/
float timer;
};
/**
* Represents the score for the player in an activity, one index might represent score, another one time, etc.
*/
struct ActivityPlayer {
/**
* The entity that the score is tracked for
*/
LWOOBJID playerID;
/**
* The list of score for this entity
*/
float values[10];
};
/**
* Welcome to the absolute behemoth that is the activity component. I have now clue how this was managed in
* live but I figure somewhat similarly and it's terrible. In a nutshell, this components handles any activity that
* can be done in the game from quick builds to boss fights to races. On top of that, this component handles instancing
* and lobbying.
*/
class ActivityComponent : public Component {
public:
ActivityComponent(Entity* parent, int32_t componentId);
~ActivityComponent() override;
void Update(float deltaTime) override;
void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags) const;
/**
* Makes some entity join the minigame, if it's a lobbied one, the entity will be placed in the lobby
* @param player the entity to join the game
*/
void PlayerJoin(Entity* player);
/**
* Makes an entity join the lobby for this minigame, if it exists
* @param player the entity to join
*/
void PlayerJoinLobby(Entity* player);
/**
* Makes the player leave the lobby
* @param playerID the entity to leave the lobby
*/
void PlayerLeave(LWOOBJID playerID);
/**
* Removes the entity from the minigame (and its score)
* @param playerID the entity to remove from the minigame
*/
void PlayerRemove(LWOOBJID playerID);
/**
* Adds all the players to an instance of some activity
* @param instance the instance to load the players into
* @param lobby the players to load into the instance
*/
void LoadPlayersIntoInstance(ActivityInstance* instance, const std::vector<LobbyPlayer*>& lobby) const;
/**
* Removes a lobby from the activity manager
* @param lobby the lobby to remove
*/
void RemoveLobby(Lobby* lobby);
/**
* Marks a player as (un)ready in a lobby
* @param player the entity to mark
* @param bReady true if the entity is ready, false otherwise
*/
void PlayerReady(Entity* player, bool bReady);
/**
* Returns the ID of this activity
* @return the ID of this activity
*/
int GetActivityID() { return m_ActivityInfo.ActivityID; }
/**
* Returns if this activity has a lobby, e.g. if it needs to instance players to some other map
* @return true if this activity has a lobby, false otherwise
*/
bool HasLobby() const;
/**
* Checks if a player is currently waiting in a lobby
* @param player the entity to check for
* @return true if the entity is waiting in a lobby, false otherwise
*/
bool PlayerIsInQueue(Entity* player);
/**
* Checks if an entity is currently playing this activity
* @param player the entity to check
* @return true if the entity is playing this lobby, false otherwise
*/
bool IsPlayedBy(Entity* player) const;
/**
* Checks if an entity is currently playing this activity
* @param playerID the entity to check
* @return true if the entity is playing this lobby, false otherwise
*/
bool IsPlayedBy(LWOOBJID playerID) const;
/**
* Legacy: used to check for unimplemented maps, gladly, this now just returns true :)
*/
bool IsValidActivity(Entity* player);
/**
* Removes the cost of the activity (e.g. green imaginate) for the entity that plays this activity
* @param player the entity to take cost for
* @return true if the cost was successfully deducted, false otherwise
*/
bool TakeCost(Entity* player) const;
/**
* Handles any response from a player clicking on a lobby / instance menu
* @param player the entity that clicked
* @param id the message that was passed
*/
void HandleMessageBoxResponse(Entity* player, const std::string& id);
/**
* Creates a new instance for this activity
* @return a new instance for this activity
*/
ActivityInstance* NewInstance();
/**
* Returns all the currently active instances of this activity
* @return all the currently active instances of this activity
*/
const std::vector<ActivityInstance*>& GetInstances() const;
/**
* Returns the instance that some entity is currently playing in
* @param playerID the entity to check for
* @return if any, the instance that the entity is currently in
*/
ActivityInstance* GetInstance(const LWOOBJID playerID);
/**
* @brief Reloads the config settings for this component
*
*/
void ReloadConfig();
/**
* Removes all the instances
*/
void ClearInstances();
/**
* Returns all the score for the players that are currently playing this activity
* @return
*/
std::vector<ActivityPlayer*> GetActivityPlayers() { return m_ActivityPlayers; };
/**
* Returns activity data for a specific entity (e.g. score and such).
* @param playerID the entity to get data for
* @return the activity data (score) for the passed player in this activity, if it exists
*/
ActivityPlayer* GetActivityPlayerData(LWOOBJID playerID);
/**
* Sets some score value for an entity
* @param playerID the entity to set score for
* @param index the score index to set
* @param value the value to set in for that index
*/
void SetActivityValue(LWOOBJID playerID, uint32_t index, float_t value);
/**
* Returns activity score for the passed parameters
* @param playerID the entity to get score for
* @param index the index to get score for
* @return activity score for the passed parameters
*/
float_t GetActivityValue(LWOOBJID playerID, uint32_t index);
/**
* Removes activity score tracking for some entity
* @param playerID the entity to remove score for
*/
void RemoveActivityPlayerData(LWOOBJID playerID);
/**
* Adds activity score tracking for some entity
* @param playerID the entity to add the activity score for
* @return the created entry
*/
ActivityPlayer* AddActivityPlayerData(LWOOBJID playerID);
/**
* Sets the mapID that this activity points to
* @param mapID the map ID to set
*/
void SetInstanceMapID(uint32_t mapID) { m_ActivityInfo.instanceMapID = mapID; };
/**
* Returns the LMI that this activity points to for a team size
* @param teamSize the team size to get the LMI for
* @return the LMI that this activity points to for a team size
*/
uint32_t GetLootMatrixForTeamSize(uint32_t teamSize) { return m_ActivityLootMatrices[teamSize]; }
private:
/**
* The database information for this activity
*/
CDActivities m_ActivityInfo;
/**
* All the active instances of this activity
*/
std::vector<ActivityInstance*> m_Instances;
/**
* The current lobbies for this activity
*/
std::vector<Lobby*> m_Queue;
/**
* All the activity score for the players in this activity
*/
std::vector<ActivityPlayer*> m_ActivityPlayers;
/**
* LMIs for team sizes
*/
std::unordered_map<uint32_t, uint32_t> m_ActivityLootMatrices;
/**
* The activity id
*
*/
int32_t m_ActivityID;
};
#endif // ACTIVITYCOMPONENT_H

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -14,20 +14,24 @@ class Entity;
/**
* Extra information on effects to apply after applying a buff, for example whether to buff armor, imag or health and by how much
*/
struct BuffParameter
{
int32_t buffId;
struct BuffParameter {
struct ParameterValues {
int32_t skillId = 0;
int32_t stacks = 0;
float tick = 0.0f;
int32_t unknown2 = 0;
};
int32_t buffId = 0;
std::string name;
float value;
std::vector<float> values;
int32_t effectId;
float value = 0.0f;
ParameterValues values;
int32_t effectId = 0;
};
/**
* Meta information about a buff that can be applied, e.g. how long it's applied, who applied it, etc.
*/
struct Buff
{
struct Buff {
int32_t id = 0;
float time = 0;
float tick = 0;
@@ -40,15 +44,11 @@ struct Buff
/**
* Allows for the application of buffs to the parent entity, altering health, armor and imagination.
*/
class BuffComponent : public Component {
class BuffComponent final : public Component {
public:
static const eReplicaComponentType ComponentType = eReplicaComponentType::BUFF;
inline static const eReplicaComponentType ComponentType = eReplicaComponentType::BUFF;
explicit BuffComponent(Entity* parent);
~BuffComponent();
Entity* GetParent() const;
explicit BuffComponent(Entity* parent) : Component(parent) {};
void LoadFromXml(tinyxml2::XMLDocument* doc) override;
@@ -88,7 +88,7 @@ public:
* @param id the id of the buff to find
* @return whether or not the entity has a buff with the specified id active
*/
bool HasBuff(int32_t id);
bool HasBuff(int32_t id) { return m_Buffs.find(id) != m_Buffs.end(); };
/**
* Applies the effects of the buffs on the entity, e.g.: changing armor, health, imag, etc.
@@ -110,7 +110,7 @@ public:
/**
* Removes all buffs for the entity and reverses all of their effects
*/
void Reset();
void Reset() { RemoveAllBuffs(); };
/**
* Applies all effects for all buffs, active or not, again

View File

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

View File

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

View File

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

View File

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

View File

@@ -60,12 +60,11 @@ enum StatisticID {
/**
* Represents a character, including their rockets and stats
*/
class CharacterComponent : public Component {
class CharacterComponent final : public Component {
public:
static const eReplicaComponentType ComponentType = eReplicaComponentType::CHARACTER;
inline static const eReplicaComponentType ComponentType = eReplicaComponentType::CHARACTER;
CharacterComponent(Entity* parent, Character* character);
~CharacterComponent() override;
void LoadFromXml(tinyxml2::XMLDocument* doc) override;
void UpdateXml(tinyxml2::XMLDocument* doc) override;

View File

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

View File

@@ -0,0 +1,22 @@
#ifndef __COLLECTIBLECOMPONENT__H__
#define __COLLECTIBLECOMPONENT__H__
#include "Component.h"
#include "eReplicaComponentType.h"
#include <cstdint>
class CollectibleComponent : public Component {
public:
inline static const eReplicaComponentType ComponentType = eReplicaComponentType::COLLECTIBLE;
CollectibleComponent(Entity* parent) : Component(parent) { };
void Startup() override;
uint32_t GetCollectibleId() const { return m_CollectibleId; }
private:
uint32_t m_CollectibleId;
};
#endif //!__COLLECTIBLECOMPONENT__H__

View File

@@ -1,30 +1,7 @@
#include "Component.h"
#include "DluAssert.h"
Component::Component(Entity* parent) {
m_Parent = parent;
}
Component::~Component() {
}
Entity* Component::GetParent() const {
return m_Parent;
}
void Component::Update(float deltaTime) {
}
void Component::OnUse(Entity* originator) {
}
void Component::UpdateXml(tinyxml2::XMLDocument* doc) {
}
void Component::LoadFromXml(tinyxml2::XMLDocument* doc) {
Component::Component(Entity* owningEntity) {
DluAssert(owningEntity != nullptr);
m_ParentEntity = owningEntity;
}

View File

@@ -1,52 +1,74 @@
#pragma once
#include "../thirdparty/tinyxml2/tinyxml2.h"
#include "tinyxml2.h"
class Entity;
namespace RakNet {
class BitStream;
}
/**
* Component base class, provides methods for game loop updates, usage events and loading and saving to XML.
*/
class Component
{
class Component {
public:
Component(Entity* parent);
virtual ~Component();
Component(Entity* owningEntity);
virtual ~Component() {};
/**
* Gets the owner of this component
* @return the owner of this component
*/
Entity* GetParent() const;
/**
* Updates the component in the game loop
* @param deltaTime time passed since last update
*/
virtual void Update(float deltaTime);
Entity* GetParentEntity() const { return m_ParentEntity; };
/**
* Event called when this component is being used, e.g. when some entity interacted with it
* @param originator
*/
virtual void OnUse(Entity* originator);
virtual void OnUse(Entity* originator) {};
/**
* Save data from this componennt to character XML
* @param doc the document to write data to
*/
virtual void UpdateXml(tinyxml2::XMLDocument* doc);
virtual void UpdateXml(tinyxml2::XMLDocument* doc) {};
/**
* Load base data for this component from character XML
* @param doc the document to read data from
*/
virtual void LoadFromXml(tinyxml2::XMLDocument* doc);
virtual void LoadFromXml(tinyxml2::XMLDocument* doc) {};
/**
* Call after you have newed the component to initialize it
*/
virtual void Startup() {};
/**
* Updates the component in the game loop
* @param deltaTime time passed since last update
*/
virtual void Update(float deltaTime) {};
/**
* Loads the data of this component from the luz/lvl configuration
*/
virtual void LoadConfigData() {};
/**
* Loads the data of this component from the cdclient database
*/
virtual void LoadTemplateData() {};
/**
* Serializes the component for delivery to the client(s)
*/
virtual void Serialize(RakNet::BitStream* bitStream, bool isConstruction = false) {};
protected:
/**
* The entity that owns this component
*/
Entity* m_Parent;
Entity* m_ParentEntity;
};

View File

@@ -0,0 +1,153 @@
Legend
```
├── Explicit inheritance
├~~ Loaded with Parent
├-> SubComponent
├-? idk lol, but related
originalName -> newName
```
```tree
LWOActivityComponent
├── LWOQuickBuildComponent
├── LWOMiniGameControlComponent
├── LWOShootingGalleryComponent
├── LWOScriptedActivityComponent
| └── LWOBaseRacingControlComponent -> RacingControlComponent
| ├── LWORacingControlComponent -> RacingComponent
| └── LWOGateRushControlComponent -> GateRushComponent
LWOBaseCombatAIComponent
├~~ LWOPathfindingControlComponent
├~~ LWOProximityMonitorComponent
LWOProjectilePhysComponent
LWOBasePhysComponent
├── LWORigidBodyPhantomComponent
├── LWOPhantomPhysComponent
├── LWOVehiclePhysicsComponent
├── LWOSimplePhysComponent
| ├~~ LWOPhantomPhysicsComponent
├── LWOPhysicsSystemComponent
├── LWOHavokVehiclePhysicsComponent
├── LWOControllablePhysComponent
LWOBaseRenderComponent
├── LWOSkinnedRenderComponent
├~~ LWOSkinnedRenderComponent
├~~ LWOFXComponent
LWOBaseVendorComponent
├── LWOVendorComponent
| ├~~ LWOProximityMonitorComponent
├── LWODonationVendorComponent
| ├~~ LWOProximityMonitorComponent
├── LWOAchievementVendorComponent
| ├~~ LWOProximityMonitorComponent
LWOBBBComponent_Common
├── LWOBBBComponent_Client
LWOBlueprintComponent
LWOBouncerComponent
LWOBuildControllerComponentCommon
├── LWOBuildControllerComponent
LWOChangelingBuildComponent
LWOCharacterComponent
├── LWOMinifigComponent
├~~ LWOPossessionControlComponent
├~~ LWOMountControlComponent
├~~ LWOPetCreatorComponent
├~~ LWOLevelProgressionComponent
├~~ LWOPlayerForcedMovementComponent
| ├~? LWOPathfindingControlComponent
LWOChestComponent
LWOChoiceBuildComponent
LWOCollectibleComponent
LWOCustomBuildAssemblyComponent
LWODestroyableComponent
├~~ LWOBuffComponent
├~~ LWOStatusEffectComponent
LWODropEffectComponent
LWODroppedLootComponent
LWOExhibitComponent
LWOGenericActivatorComponent
LWOGhostComponent
LWOHFLightDirectionGadgetComponent
LWOInventoryComponent_Common
├── LWOInventoryComponent_Client
| └── LWOInventoryComponent_EquippedItem
LWOItemComponent
LWOLUPExhibitComponent
LWOMissionOfferComponent
LWOModelBehaviorComponent
├~~ LWOSimplePhysComponent
├~~ LWOControllablePhysComponent
├~~ LWOPathfindingControlComponent
├~~ LWOMutableModelBehaviorComponent
LWOModelBuilderComponent
LWOModularBuildComponentCommon
├── LWOModularBuildComponent
LWOModuleAssemblyComponent
├── LWOModuleAssemblyComponentCommon
LWOModuleComponentCommon
├── LWOModuleComponent
LWOMovementAIComponent
LWOMultiZoneEntranceComponent
LWOOverheadIconComponent
LWOPetComponent
├~~ LWOPathfindingControlComponent
├~? LWOItemComponent
├~? LWOModelBehaviorComponent
| ├~~ ...
LWOPlatformBoundaryComponent
LWOPlatformComponent
├-> LWOMoverPlatformSubComponent
├-> LWOSimpleMoverPlatformSubComponent
├-> LWORotaterPlatformSubComponent
LWOPropertyComponent
LWOPropertyEntranceComponent
LWOPropertyManagementComponent
LWOPropertyVendorComponent
LWOProximityMonitorComponent
LWOSoundTriggerComponent
├── LWORacingSoundTriggerComponent
LWORacingStatsComponentCommon
├── LWORacingStatsComponent
LWORocketAnimationControlComponentCommon
├── LWORocketAnimationControlComponent
LWORocketLaunchpadControlComponentCommon
├── LWORocketLaunchpadControlComponent
LWOScriptComponent
├~~ LWOPathfindingControlComponent
├~~ LWOProximityMonitorComponent
LWOShowcaseModelHandlerComponent
LWOSkillComponent
LWOSoundAmbient2DComponent
LWOSoundAmbient3DComponent
LWOSoundRepeaterComponent
LWOSpawnComponent
LWOSpringpadComponent
LWOSwitchComponent
LWOTriggerComponent
LocalPlayer - This is a function call in the client which, if the generated Entity is a player, the below components are added. This **must** be done for all players.
├~~ LWOInteractionManagerComponent
├~~ LWOUserControlComponent
├~~ LWOFriendsListComponent
├~~ LWOIgnoreListComponent
├~~ LWOTextEffectComponent
├~~ LWOChatBubbleComponent
├~~ LWOGuildComponent
├~~ LWOPlayerPetTamingComponent
├~~ LWOLocalSystemsComponent
├~~ LWOSlashCommandComponent
├~~ LWOMissionComponent
├~~ LWOPropertyEditorComponent
├~~ LWOComponent115
├~~ LWOTeamsComponent
├~~ LWOChatComponent
├~~ LWOPetControlComponent
├~~ LWOTradeComponent
├~~ LWOPreconditionComponent
├~~ LWOFlagComponent
├~~ LWOFactionTriggerComponent
```

View File

@@ -3,6 +3,7 @@
#include "BitStream.h"
#include "dLogger.h"
#include "Game.h"
#include "dServer.h"
#include "dpWorld.h"
#include "dpEntity.h"
@@ -51,34 +52,30 @@ ControllablePhysicsComponent::ControllablePhysicsComponent(Entity* entity) : Com
m_ImmuneToStunTurnCount = 0;
m_ImmuneToStunUseItemCount = 0;
if (entity->GetLOT() != 1) // Other physics entities we care about will be added by BaseCombatAI
return;
// Other physics entities we care about will be added by BaseCombatAI
if (entity->GetLOT() != 1) return;
if (entity->GetLOT() == 1) {
Game::logger->Log("ControllablePhysicsComponent", "Using patch to load minifig physics");
Game::logger->Log("ControllablePhysicsComponent", "Using patch to load minifig physics");
float radius = 1.5f;
m_dpEntity = new dpEntity(m_Parent->GetObjectID(), radius, false);
m_dpEntity->SetCollisionGroup(COLLISION_GROUP_DYNAMIC | COLLISION_GROUP_FRIENDLY);
dpWorld::Instance().AddEntity(m_dpEntity);
}
float radius = 1.5f;
m_dpEntity = new dpEntity(m_ParentEntity->GetObjectID(), radius, false);
m_dpEntity->SetCollisionGroup(COLLISION_GROUP_DYNAMIC | COLLISION_GROUP_FRIENDLY);
dpWorld::Instance().AddEntity(m_dpEntity);
}
ControllablePhysicsComponent::~ControllablePhysicsComponent() {
if (m_dpEntity) {
dpWorld::Instance().RemoveEntity(m_dpEntity);
}
if (!m_dpEntity) return;
dpWorld::Instance().RemoveEntity(m_dpEntity);
}
void ControllablePhysicsComponent::Update(float deltaTime) {
void ControllablePhysicsComponent::Startup() {
NiPoint3 pos = m_ParentEntity->GetDefaultPosition();
NiQuaternion rot = m_ParentEntity->GetDefaultRotation();
SetPosition(pos);
SetRotation(rot);
}
void ControllablePhysicsComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags) {
//If this is a creation, then we assume the position is dirty, even when it isn't.
//This is because new clients will still need to receive the position.
//if (bIsInitialUpdate) m_DirtyPosition = true;
if (bIsInitialUpdate) {
outBitStream->Write(m_InJetpackMode);
if (m_InJetpackMode) {
@@ -99,33 +96,32 @@ void ControllablePhysicsComponent::Serialize(RakNet::BitStream* outBitStream, bo
if (m_IgnoreMultipliers) m_DirtyCheats = false;
outBitStream->Write(m_DirtyCheats);
if (m_DirtyCheats) {
outBitStream->Write(bIsInitialUpdate || m_DirtyCheats);
if (bIsInitialUpdate || m_DirtyCheats) {
outBitStream->Write(m_GravityScale);
outBitStream->Write(m_SpeedMultiplier);
m_DirtyCheats = false;
if (!bIsInitialUpdate) m_DirtyCheats = false;
}
outBitStream->Write(m_DirtyEquippedItemInfo);
if (m_DirtyEquippedItemInfo) {
outBitStream->Write(bIsInitialUpdate || m_DirtyEquippedItemInfo);
if (bIsInitialUpdate || m_DirtyEquippedItemInfo) {
outBitStream->Write(m_PickupRadius);
outBitStream->Write(m_InJetpackMode);
m_DirtyEquippedItemInfo = false;
if (!bIsInitialUpdate) m_DirtyEquippedItemInfo = false;
}
outBitStream->Write(m_DirtyBubble);
if (m_DirtyBubble) {
outBitStream->Write(bIsInitialUpdate || m_DirtyBubble);
if (bIsInitialUpdate || m_DirtyBubble) {
outBitStream->Write(m_IsInBubble);
if (m_IsInBubble) {
outBitStream->Write(m_BubbleType);
outBitStream->Write(m_SpecialAnims);
}
m_DirtyBubble = false;
if (!bIsInitialUpdate) m_DirtyBubble = false;
}
outBitStream->Write(m_DirtyPosition || bIsInitialUpdate);
if (m_DirtyPosition || bIsInitialUpdate) {
outBitStream->Write(bIsInitialUpdate || m_DirtyPosition);
if (bIsInitialUpdate || m_DirtyPosition) {
outBitStream->Write(m_Position.x);
outBitStream->Write(m_Position.y);
outBitStream->Write(m_Position.z);
@@ -138,20 +134,22 @@ void ControllablePhysicsComponent::Serialize(RakNet::BitStream* outBitStream, bo
outBitStream->Write(m_IsOnGround);
outBitStream->Write(m_IsOnRail);
outBitStream->Write(m_DirtyVelocity);
if (m_DirtyVelocity) {
outBitStream->Write(bIsInitialUpdate || m_DirtyVelocity);
if (bIsInitialUpdate || m_DirtyVelocity) {
outBitStream->Write(m_Velocity.x);
outBitStream->Write(m_Velocity.y);
outBitStream->Write(m_Velocity.z);
if (!bIsInitialUpdate) m_DirtyVelocity = false;
}
outBitStream->Write(m_DirtyAngularVelocity);
if (m_DirtyAngularVelocity) {
outBitStream->Write(bIsInitialUpdate || m_DirtyAngularVelocity);
if (bIsInitialUpdate || m_DirtyAngularVelocity) {
outBitStream->Write(m_AngularVelocity.x);
outBitStream->Write(m_AngularVelocity.y);
outBitStream->Write(m_AngularVelocity.z);
if (!bIsInitialUpdate) m_DirtyAngularVelocity = false;
}
if (!bIsInitialUpdate) m_DirtyPosition = false;
outBitStream->Write0();
}
@@ -162,22 +160,45 @@ void ControllablePhysicsComponent::Serialize(RakNet::BitStream* outBitStream, bo
}
void ControllablePhysicsComponent::LoadFromXml(tinyxml2::XMLDocument* doc) {
tinyxml2::XMLElement* character = doc->FirstChildElement("obj")->FirstChildElement("char");
if (!character) {
tinyxml2::XMLElement* characterElem = doc->FirstChildElement("obj")->FirstChildElement("char");
if (!characterElem) {
Game::logger->Log("ControllablePhysicsComponent", "Failed to find char tag!");
return;
}
m_Parent->GetCharacter()->LoadXmlRespawnCheckpoints();
m_ParentEntity->GetCharacter()->LoadXmlRespawnCheckpoints();
character->QueryAttribute("lzx", &m_Position.x);
character->QueryAttribute("lzy", &m_Position.y);
character->QueryAttribute("lzz", &m_Position.z);
character->QueryAttribute("lzrx", &m_Rotation.x);
character->QueryAttribute("lzry", &m_Rotation.y);
character->QueryAttribute("lzrz", &m_Rotation.z);
character->QueryAttribute("lzrw", &m_Rotation.w);
characterElem->QueryAttribute("lzx", &m_Position.x);
characterElem->QueryAttribute("lzy", &m_Position.y);
characterElem->QueryAttribute("lzz", &m_Position.z);
characterElem->QueryAttribute("lzrx", &m_Rotation.x);
characterElem->QueryAttribute("lzry", &m_Rotation.y);
characterElem->QueryAttribute("lzrz", &m_Rotation.z);
characterElem->QueryAttribute("lzrw", &m_Rotation.w);
auto* character = GetParentEntity()->GetCharacter();
const auto mapID = Game::server->GetZoneID();
//If we came from another zone, put us in the starting loc
if (character->GetZoneID() != Game::server->GetZoneID() || mapID == 1603) { // Exception for Moon Base as you tend to spawn on the roof.
const auto& targetSceneName = character->GetTargetScene();
auto* targetScene = EntityManager::Instance()->GetSpawnPointEntity(targetSceneName);
NiPoint3 pos;
NiQuaternion rot;
if (character->HasBeenToWorld(mapID) && targetSceneName.empty()) {
pos = character->GetRespawnPoint(mapID);
rot = dZoneManager::Instance()->GetZone()->GetSpawnRot();
} else if (targetScene) {
pos = targetScene->GetPosition();
rot = targetScene->GetRotation();
} else {
pos = dZoneManager::Instance()->GetZone()->GetSpawnPos();
rot = dZoneManager::Instance()->GetZone()->GetSpawnRot();
}
SetPosition(pos);
SetRotation(rot);
}
m_DirtyPosition = true;
}
@@ -208,9 +229,7 @@ void ControllablePhysicsComponent::UpdateXml(tinyxml2::XMLDocument* doc) {
}
void ControllablePhysicsComponent::SetPosition(const NiPoint3& pos) {
if (m_Static) {
return;
}
if (m_Static || pos == m_Position) return;
m_Position.x = pos.x;
m_Position.y = pos.y;
@@ -221,9 +240,7 @@ void ControllablePhysicsComponent::SetPosition(const NiPoint3& pos) {
}
void ControllablePhysicsComponent::SetRotation(const NiQuaternion& rot) {
if (m_Static) {
return;
}
if (m_Static || rot == m_Rotation) return;
m_Rotation = rot;
m_DirtyPosition = true;
@@ -232,9 +249,7 @@ void ControllablePhysicsComponent::SetRotation(const NiQuaternion& rot) {
}
void ControllablePhysicsComponent::SetVelocity(const NiPoint3& vel) {
if (m_Static) {
return;
}
if (m_Static || m_Velocity == vel) return;
m_Velocity = vel;
m_DirtyPosition = true;
@@ -244,9 +259,7 @@ void ControllablePhysicsComponent::SetVelocity(const NiPoint3& vel) {
}
void ControllablePhysicsComponent::SetAngularVelocity(const NiPoint3& vel) {
if (m_Static) {
return;
}
if (m_Static || vel == m_AngularVelocity) return;
m_AngularVelocity = vel;
m_DirtyPosition = true;
@@ -254,25 +267,15 @@ void ControllablePhysicsComponent::SetAngularVelocity(const NiPoint3& vel) {
}
void ControllablePhysicsComponent::SetIsOnGround(bool val) {
m_DirtyPosition = true;
if (m_IsOnGround == val) return;
m_IsOnGround = val;
m_DirtyPosition = true;
}
void ControllablePhysicsComponent::SetIsOnRail(bool val) {
m_DirtyPosition = true;
if (m_IsOnRail == val) return;
m_IsOnRail = val;
}
void ControllablePhysicsComponent::SetDirtyPosition(bool val) {
m_DirtyPosition = val;
}
void ControllablePhysicsComponent::SetDirtyVelocity(bool val) {
m_DirtyVelocity = val;
}
void ControllablePhysicsComponent::SetDirtyAngularVelocity(bool val) {
m_DirtyAngularVelocity = val;
m_DirtyPosition = true;
}
void ControllablePhysicsComponent::AddPickupRadiusScale(float value) {
@@ -297,10 +300,10 @@ void ControllablePhysicsComponent::RemovePickupRadiusScale(float value) {
m_PickupRadius = 0.0f;
m_DirtyEquippedItemInfo = true;
for (uint32_t i = 0; i < m_ActivePickupRadiusScales.size(); i++) {
auto candidateRadius = m_ActivePickupRadiusScales[i];
auto candidateRadius = m_ActivePickupRadiusScales.at(i);
if (m_PickupRadius < candidateRadius) m_PickupRadius = candidateRadius;
}
EntityManager::Instance()->SerializeEntity(m_Parent);
EntityManager::Instance()->SerializeEntity(m_ParentEntity);
}
void ControllablePhysicsComponent::AddSpeedboost(float value) {
@@ -319,33 +322,33 @@ void ControllablePhysicsComponent::RemoveSpeedboost(float value) {
}
// Recalculate speedboost since we removed one
m_SpeedBoost = 0.0f;
m_SpeedBoost = 500.0f;
if (m_ActiveSpeedBoosts.empty()) { // no active speed boosts left, so return to base speed
auto* levelProgressionComponent = m_Parent->GetComponent<LevelProgressionComponent>();
auto* levelProgressionComponent = m_ParentEntity->GetComponent<LevelProgressionComponent>();
if (levelProgressionComponent) m_SpeedBoost = levelProgressionComponent->GetSpeedBase();
} else { // Used the last applied speedboost
m_SpeedBoost = m_ActiveSpeedBoosts.back();
}
SetSpeedMultiplier(m_SpeedBoost / 500.0f); // 500 being the base speed
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) {
Game::logger->Log("ControllablePhysicsComponent", "Already in bubble");
Game::logger->Log("ControllablePhysicsComponent", "%llu is already in bubble", m_ParentEntity->GetObjectID());
return;
}
m_BubbleType = bubbleType;
m_IsInBubble = true;
m_DirtyBubble = true;
m_SpecialAnims = specialAnims;
EntityManager::Instance()->SerializeEntity(m_Parent);
EntityManager::Instance()->SerializeEntity(m_ParentEntity);
}
void ControllablePhysicsComponent::DeactivateBubbleBuff(){
void ControllablePhysicsComponent::DeactivateBubbleBuff() {
m_DirtyBubble = true;
m_IsInBubble = false;
EntityManager::Instance()->SerializeEntity(m_Parent);
EntityManager::Instance()->SerializeEntity(m_ParentEntity);
};
void ControllablePhysicsComponent::SetStunImmunity(
@@ -357,9 +360,9 @@ void ControllablePhysicsComponent::SetStunImmunity(
const bool bImmuneToStunJump,
const bool bImmuneToStunMove,
const bool bImmuneToStunTurn,
const bool bImmuneToStunUseItem){
const bool bImmuneToStunUseItem) {
if (state == eStateChangeType::POP){
if (state == eStateChangeType::POP) {
if (bImmuneToStunAttack && m_ImmuneToStunAttackCount > 0) m_ImmuneToStunAttackCount -= 1;
if (bImmuneToStunEquip && m_ImmuneToStunEquipCount > 0) m_ImmuneToStunEquipCount -= 1;
if (bImmuneToStunInteract && m_ImmuneToStunInteractCount > 0) m_ImmuneToStunInteractCount -= 1;
@@ -378,7 +381,7 @@ void ControllablePhysicsComponent::SetStunImmunity(
}
GameMessages::SendSetStunImmunity(
m_Parent->GetObjectID(), state, m_Parent->GetSystemAddress(), originator,
m_ParentEntity->GetObjectID(), state, m_ParentEntity->GetSystemAddress(), originator,
bImmuneToStunAttack,
bImmuneToStunEquip,
bImmuneToStunInteract,

View File

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

View File

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

View File

@@ -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"
VehiclePhysicsComponent::VehiclePhysicsComponent(Entity* parent) : Component(parent) {
HavokVehiclePhysicsComponent::HavokVehiclePhysicsComponent(Entity* parent) : Component(parent) {
m_Position = NiPoint3::ZERO;
m_Rotation = NiQuaternion::IDENTITY;
m_Velocity = NiPoint3::ZERO;
@@ -14,69 +14,53 @@ VehiclePhysicsComponent::VehiclePhysicsComponent(Entity* parent) : Component(par
m_EndBehavior = GeneralUtils::GenerateRandomNumber<uint32_t>(0, 7);
}
VehiclePhysicsComponent::~VehiclePhysicsComponent() {
}
void VehiclePhysicsComponent::SetPosition(const NiPoint3& pos) {
void HavokVehiclePhysicsComponent::SetPosition(const NiPoint3& pos) {
if (pos == m_Position) return;
m_DirtyPosition = true;
m_Position = pos;
}
void VehiclePhysicsComponent::SetRotation(const NiQuaternion& rot) {
void HavokVehiclePhysicsComponent::SetRotation(const NiQuaternion& rot) {
if (rot == m_Rotation) return;
m_DirtyPosition = true;
m_Rotation = rot;
}
void VehiclePhysicsComponent::SetVelocity(const NiPoint3& vel) {
void HavokVehiclePhysicsComponent::SetVelocity(const NiPoint3& vel) {
if (vel == m_Velocity) return;
m_DirtyPosition = true;
m_Velocity = vel;
}
void VehiclePhysicsComponent::SetAngularVelocity(const NiPoint3& vel) {
void HavokVehiclePhysicsComponent::SetAngularVelocity(const NiPoint3& vel) {
if (vel == m_AngularVelocity) return;
m_DirtyPosition = true;
m_AngularVelocity = vel;
}
void VehiclePhysicsComponent::SetIsOnGround(bool val) {
void HavokVehiclePhysicsComponent::SetIsOnGround(bool val) {
if (val == m_IsOnGround) return;
m_DirtyPosition = true;
m_IsOnGround = val;
}
void VehiclePhysicsComponent::SetIsOnRail(bool val) {
void HavokVehiclePhysicsComponent::SetIsOnRail(bool val) {
if (val == m_IsOnRail) return;
m_DirtyPosition = true;
m_IsOnRail = val;
}
void VehiclePhysicsComponent::SetRemoteInputInfo(const RemoteInputInfo& remoteInputInfo) {
void HavokVehiclePhysicsComponent::SetRemoteInputInfo(const RemoteInputInfo& remoteInputInfo) {
if (m_RemoteInputInfo == remoteInputInfo) return;
this->m_RemoteInputInfo = remoteInputInfo;
m_DirtyRemoteInput = true;
}
void VehiclePhysicsComponent::SetDirtyPosition(bool val) {
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) {
void HavokVehiclePhysicsComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags) {
outBitStream->Write(bIsInitialUpdate || m_DirtyPosition);
if (bIsInitialUpdate || m_DirtyPosition) {
m_DirtyPosition = false;
if (!bIsInitialUpdate) m_DirtyPosition = false;
outBitStream->Write(m_Position.x);
outBitStream->Write(m_Position.y);
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.y);
outBitStream->Write(m_Velocity.z);
m_DirtyVelocity = false;
if (!bIsInitialUpdate) m_DirtyVelocity = false;
}
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.y);
outBitStream->Write(m_AngularVelocity.z);
m_DirtyAngularVelocity = false;
if (!bIsInitialUpdate) m_DirtyAngularVelocity = false;
}
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_IsPowersliding);
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.
@@ -132,12 +116,3 @@ void VehiclePhysicsComponent::Serialize(RakNet::BitStream* outBitStream, bool bI
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.
*/
class VehiclePhysicsComponent : public Component {
class HavokVehiclePhysicsComponent final : public Component {
public:
static const eReplicaComponentType ComponentType = eReplicaComponentType::VEHICLE_PHYSICS;
inline static const eReplicaComponentType ComponentType = eReplicaComponentType::HAVOK_VEHICLE_PHYSICS;
VehiclePhysicsComponent(Entity* parentEntity);
~VehiclePhysicsComponent() override;
HavokVehiclePhysicsComponent(Entity* parentEntity);
void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags);
void Update(float deltaTime) override;
/**
* Sets the position
* @param pos the new position
@@ -109,9 +106,9 @@ public:
*/
const bool GetIsOnRail() const { return m_IsOnRail; }
void SetDirtyPosition(bool val);
void SetDirtyVelocity(bool val);
void SetDirtyAngularVelocity(bool val);
void SetDirtyPosition(bool val) { m_DirtyPosition = val; }
void SetDirtyVelocity(bool val) { m_DirtyVelocity = val; }
void SetDirtyAngularVelocity(bool val) { m_DirtyAngularVelocity = val; }
void SetRemoteInputInfo(const RemoteInputInfo&);
private:

File diff suppressed because it is too large Load Diff

View File

@@ -18,7 +18,7 @@
#include "Component.h"
#include "ItemSetPassiveAbility.h"
#include "eItemSetPassiveAbilityID.h"
#include "PossessorComponent.h"
#include "PossessionComponent.h"
#include "eInventoryType.h"
#include "eReplicaComponentType.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
* inventories.
*/
class InventoryComponent : public Component
class InventoryComponent final : public Component
{
public:
static const eReplicaComponentType ComponentType = eReplicaComponentType::INVENTORY;
inline static const eReplicaComponentType ComponentType = eReplicaComponentType::INVENTORY;
explicit InventoryComponent(Entity* parent, tinyxml2::XMLDocument* document = nullptr);
void Update(float deltaTime) override;
void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags);
void LoadXml(tinyxml2::XMLDocument* document);
void UpdateXml(tinyxml2::XMLDocument* document) override;
void ResetFlags();
void ResetFlags() { m_Dirty = false; };
/**
* 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
* @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
* 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
* @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
* @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
@@ -206,7 +208,7 @@ public:
* @param item the Item to unequip
* @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
@@ -247,13 +249,13 @@ public:
* Sets the current consumable lot
* @param lot the lot to set as consumable
*/
void SetConsumable(LOT lot);
void SetConsumable(LOT lot) { m_Consumable = lot; };
/**
* Returns 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
@@ -285,7 +287,7 @@ public:
* Triggers one of the passive abilities from the equipped item set
* @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
@@ -325,13 +327,13 @@ public:
* @param id the id of the object to check for
* @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
* @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
@@ -359,14 +361,14 @@ public:
*
* @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.
*
* @param unequippedItem The item script to lookup and call unequip on
*/
void UnequipScripts(Item* unequippedItem);
void UnequipScripts(Item* unequippedItem) const;
~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"
LUPExhibitComponent::LUPExhibitComponent(Entity* parent) : Component(parent) {
m_Exhibits = { 11121, 11295, 11423, 11979 };
m_ExhibitIndex = 0;
m_Exhibit = m_Exhibits[m_ExhibitIndex];
}
LUPExhibitComponent::~LUPExhibitComponent() {
m_UpdateTimer = 0.0f;
m_Exhibit = m_Exhibits.front();
m_DirtyExhibitInfo = true;
}
void LUPExhibitComponent::Update(float deltaTime) {
m_UpdateTimer += deltaTime;
if (m_UpdateTimer < 20.0f) return;
if (m_UpdateTimer > 20.0f) {
NextExhibit();
m_UpdateTimer = 0.0f;
}
NextExhibit();
m_UpdateTimer = 0.0f;
}
void LUPExhibitComponent::NextExhibit() {
m_ExhibitIndex++;
if (m_ExhibitIndex >= m_Exhibits.size()) {
m_ExhibitIndex = 0;
}
m_Exhibit = m_Exhibits[m_ExhibitIndex];
EntityManager::Instance()->SerializeEntity(m_Parent);
// After 1361 years, this will skip exhibit 4 one time. I think modulo is ok here.
m_Exhibit = m_Exhibits.at(m_ExhibitIndex % m_Exhibits.size());
EntityManager::Instance()->SerializeEntity(m_ParentEntity);
}
void LUPExhibitComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, uint32_t& flags) {
outBitStream->Write1(); // Dirty flag?
outBitStream->Write(m_Exhibit);
outBitStream->Write(bIsInitialUpdate || m_DirtyExhibitInfo);
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
* switching the LOTs around that we'd like to display.
*/
class LUPExhibitComponent : public Component
class LUPExhibitComponent final : public Component
{
public:
static const eReplicaComponentType ComponentType = eReplicaComponentType::EXHIBIT;
inline static const eReplicaComponentType ComponentType = eReplicaComponentType::EXHIBIT;
LUPExhibitComponent(Entity* parent);
~LUPExhibitComponent();
void Update(float deltaTime) override;
void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, uint32_t& flags);
@@ -36,10 +35,13 @@ private:
/**
* 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
*/
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"
LevelProgressionComponent::LevelProgressionComponent(Entity* parent) : Component(parent) {
m_Parent = parent;
m_ParentEntity = parent;
m_Level = 1;
m_SpeedBase = 500.0f;
m_CharacterVersion = eCharacterVersion::LIVE;
@@ -35,55 +35,60 @@ void LevelProgressionComponent::LoadFromXml(tinyxml2::XMLDocument* doc) {
uint32_t characterVersion;
level->QueryAttribute("cv", &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) {
outBitStream->Write(bIsInitialUpdate || m_DirtyLevelInfo);
if (bIsInitialUpdate || m_DirtyLevelInfo) outBitStream->Write(m_Level);
m_DirtyLevelInfo = false;
if (bIsInitialUpdate || m_DirtyLevelInfo) {
outBitStream->Write(m_Level);
if (!bIsInitialUpdate) m_DirtyLevelInfo = false;
}
}
void LevelProgressionComponent::HandleLevelUp() {
auto* rewardsTable = CDClientManager::Instance().GetTable<CDRewardsTable>();
const auto& rewards = rewardsTable->GetByLevelID(m_Level);
bool rewardingItem = rewards.size() > 0;
if (rewards.empty()) return;
auto* inventoryComponent = m_Parent->GetComponent<InventoryComponent>();
auto* controllablePhysicsComponent = m_Parent->GetComponent<ControllablePhysicsComponent>();
auto* inventoryComponent = m_ParentEntity->GetComponent<InventoryComponent>();
auto* controllablePhysicsComponent = m_ParentEntity->GetComponent<ControllablePhysicsComponent>();
if (!inventoryComponent || !controllablePhysicsComponent) return;
// 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) {
switch (reward->rewardType) {
case 0:
inventoryComponent->AddItem(reward->value, reward->count, eLootSourceType::LEVEL_REWARD);
break;
case 4:
{
case 4: {
auto* items = inventoryComponent->GetInventory(eInventoryType::ITEMS);
if (!items) continue;
items->SetSize(items->GetSize() + reward->value);
break;
}
break;
case 9:
SetSpeedBase(static_cast<float>(reward->value) );
SetSpeedBase(static_cast<float>(reward->value));
controllablePhysicsComponent->SetSpeedMultiplier(GetSpeedBase() / 500.0f);
break;
case 11:
case 12:
break;
default:
break;
}
}
// 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;
auto* controllablePhysicsComponent = m_Parent->GetComponent<ControllablePhysicsComponent>();
auto* controllablePhysicsComponent = m_ParentEntity->GetComponent<ControllablePhysicsComponent>();
if (controllablePhysicsComponent) controllablePhysicsComponent->SetSpeedMultiplier(m_SpeedBase / 500.0f);
}

View File

@@ -11,9 +11,9 @@
*
*/
class LevelProgressionComponent : public Component {
class LevelProgressionComponent final : public Component {
public:
static const eReplicaComponentType ComponentType = eReplicaComponentType::LEVEL_PROGRESSION;
inline static const eReplicaComponentType ComponentType = eReplicaComponentType::LEVEL_PROGRESSION;
/**
* Constructor for this component
@@ -45,7 +45,11 @@ public:
* Sets the level of the entity
* @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
@@ -98,7 +102,7 @@ private:
float m_SpeedBase;
/**
* The Character format version
* The Character format version. Certain bug fixes increment this version number.
*/
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
* Copyright 2019
* Copyright 2023
*/
#include <sstream>
#include <string>
#include "MissionComponent.h"
@@ -19,62 +18,40 @@
#include "MissionPrerequisites.h"
#include "AchievementCacheKey.h"
#include "eMissionState.h"
// MARK: Mission Component
#include "GeneralUtils.h"
std::unordered_map<AchievementCacheKey, std::vector<uint32_t>> MissionComponent::m_AchievementCache = {};
//! Initializer
MissionComponent::MissionComponent(Entity* parent) : Component(parent) {
m_LastUsedMissionOrderUID = dZoneManager::Instance()->GetUniqueMissionIdStartingValue();
}
//! Destructor
MissionComponent::~MissionComponent() {
for (const auto& mission : m_Missions) {
delete mission.second;
for (const auto& [missionId, mission] : m_Missions) {
delete mission;
}
this->m_Missions.clear();
}
Mission* MissionComponent::GetMission(const uint32_t missionId) const {
if (m_Missions.count(missionId) == 0) {
return nullptr;
}
if (m_Missions.count(missionId) == 0) return nullptr;
const auto& index = m_Missions.find(missionId);
if (index == m_Missions.end()) {
return nullptr;
}
return index->second;
return index == m_Missions.end() ? nullptr : index->second;
}
eMissionState MissionComponent::GetMissionState(const uint32_t missionId) const {
auto* mission = GetMission(missionId);
if (mission == nullptr) {
return CanAccept(missionId) ? eMissionState::AVAILABLE : eMissionState::UNKNOWN;
}
if (!mission) return CanAccept(missionId) ? eMissionState::AVAILABLE : eMissionState::UNKNOWN;
return mission->GetMissionState();
}
const std::unordered_map<uint32_t, Mission*>& MissionComponent::GetMissions() const {
return m_Missions;
}
bool MissionComponent::CanAccept(const uint32_t missionId) const {
return MissionPrerequisites::CanAccept(missionId, m_Missions);
}
void MissionComponent::AcceptMission(const uint32_t missionId, const bool skipChecks) {
if (!skipChecks && !CanAccept(missionId)) {
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"
auto* mission = this->GetMission(missionId);
if (mission != nullptr) {
if (mission) {
if (mission->GetClientInfo().repeatable) {
mission->Accept();
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);
if (missionId == 1728) {
//Needs to send a mail
auto address = m_Parent->GetSystemAddress();
Mail::HandleNotificationRequest(address, m_Parent->GetObjectID());
}
//Needs to send a mail
if (missionId == 1728) Mail::HandleNotificationRequest(m_ParentEntity->GetSystemAddress(), m_ParentEntity->GetObjectID());
}
void MissionComponent::CompleteMission(const uint32_t missionId, const bool skipChecks, const bool yieldRewards) {
// Get the mission first
auto* mission = this->GetMission(missionId);
if (mission == nullptr) {
if (!mission) {
AcceptMission(missionId, skipChecks);
mission = this->GetMission(missionId);
if (mission == nullptr) {
return;
}
if (!mission) return;
}
//If this mission is not repeatable, and already completed, we stop here.
if (mission->IsComplete() && !mission->IsRepeatable()) {
return;
}
if (mission->IsComplete() && !mission->IsRepeatable()) return;
mission->Complete(yieldRewards);
}
void MissionComponent::RemoveMission(uint32_t missionId) {
auto* mission = this->GetMission(missionId);
void MissionComponent::RemoveMission(const uint32_t missionId) {
auto missionItr = m_Missions.find(missionId);
if (mission == nullptr) {
return;
}
if (missionItr == m_Missions.end()) 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) {
for (const auto& pair : m_Missions) {
auto* mission = pair.second;
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& [missionId, mission] : m_Missions) {
if (!mission) continue;
if (mission->IsAchievement() && ignoreAchievements) 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) {
auto* mission = GetMission(missionId);
if (mission == nullptr) {
if (!acceptMission) {
return;
}
if (!mission) {
if (!acceptMission) return;
AcceptMission(missionId);
mission = GetMission(missionId);
if (mission == nullptr) {
return;
}
if (!mission) return;
}
for (auto* element : mission->GetTasks()) {
if (element->GetClientInfo().uid != taskId) continue;
std::for_each(mission->GetTasks().begin(), mission->GetTasks().end(), [value, taskId](MissionTask* element) {
if (element->GetClientInfo().uid != taskId) return;
element->AddProgress(value);
}
});
if (!mission->IsComplete()) {
mission->CheckCompletion();
}
if (!mission->IsComplete()) mission->CheckCompletion();
}
void MissionComponent::ForceProgressTaskType(const uint32_t missionId, const uint32_t taskType, const int32_t value, const bool acceptMission) {
auto* mission = GetMission(missionId);
if (mission == nullptr) {
if (!acceptMission) {
return;
}
if (!mission) {
if (!acceptMission) return;
CDMissions missionInfo;
if (!GetMissionInfo(missionId, missionInfo)) {
return;
}
if (!GetMissionInfo(missionId, missionInfo)) return;
if (missionInfo.isMission) {
return;
}
if (missionInfo.isMission) return;
AcceptMission(missionId);
mission = GetMission(missionId);
if (mission == nullptr) {
return;
}
if (!mission) return;
}
for (auto* element : mission->GetTasks()) {
if (element->GetType() != static_cast<eMissionTaskType>(taskType)) continue;
std::for_each(mission->GetTasks().begin(), mission->GetTasks().end(), [value, taskType](MissionTask* element) {
if (element->GetType() != static_cast<eMissionTaskType>(taskType)) return;
element->AddProgress(value);
}
});
if (!mission->IsComplete()) {
mission->CheckCompletion();
}
if (!mission->IsComplete()) 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);
if (mission == nullptr) {
if (!mission) {
if (!acceptMission) {
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>();
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()) {
@@ -280,10 +228,7 @@ bool MissionComponent::GetMissionInfo(uint32_t missionId, CDMissions& result) {
return true;
}
#define MISSION_NEW_METHOD
bool MissionComponent::LookForAchievements(eMissionTaskType type, int32_t value, bool progress, LWOOBJID associate, const std::string& targets, int32_t count) {
#ifdef 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) {
// Query for achievments, using the cache
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) {
// Check if we already have this achievement
if (GetMission(missionID) != nullptr) {
continue;
}
if (GetMission(missionID)) continue;
// Check if we can accept this achievement
if (!MissionPrerequisites::CanAccept(missionID, m_Missions)) {
@@ -311,87 +254,15 @@ bool MissionComponent::LookForAchievements(eMissionTaskType type, int32_t value,
any = true;
if (progress) {
// Progress mission to bring it up to speed
instance->Progress(type, value, associate, targets, count);
}
if (!progress) continue;
// Progress mission to bring it up to speed
instance->Progress(type, value, associate, targets, count);
}
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
AchievementCacheKey toFind;
toFind.SetType(type);
@@ -420,95 +291,68 @@ const std::vector<uint32_t>& MissionComponent::QueryAchievements(eMissionTaskTyp
// Seek the assosicated mission
auto foundMission = false;
const auto& mission = missionsTable->GetByMissionID(task.id, foundMission);
const auto& cdMission = missionsTable->GetByMissionID(task.id, foundMission);
if (!foundMission || mission.isMission) {
continue;
}
if (!foundMission || cdMission.isMission) continue;
// Compare the easy values
if (task.target == value || task.targetGroup == targets) {
result.push_back(mission.id);
result.push_back(cdMission.id);
continue;
}
// Compare the target group, array separated by ','
auto stream = std::istringstream(task.targetGroup);
std::string token;
for (const auto& possibleMissionStr : GeneralUtils::SplitString(task.targetGroup, ',')) {
uint32_t possibleMission;
if (GeneralUtils::TryParse(possibleMissionStr, possibleMission) && possibleMission == value) {
result.push_back(cdMission.id);
while (std::getline(stream, token, ',')) {
try {
if (std::stoi(token) == value) {
result.push_back(mission.id);
continue;
}
} catch (std::invalid_argument& exception) {
// Ignored
break;
}
}
}
// Insert into cache
m_AchievementCache.insert_or_assign(toFind, result);
return m_AchievementCache.find(toFind)->second;
// Insert into cache and return the inserted value.
return m_AchievementCache.insert_or_assign(toFind, result).first->second;
}
bool MissionComponent::RequiresItem(const LOT lot) {
auto query = CDClientDatabase::CreatePreppedStmt(
"SELECT type FROM Objects WHERE id = ?;");
query.bind(1, (int)lot);
auto query = CDClientDatabase::CreatePreppedStmt("SELECT type FROM Objects WHERE id = ?;");
query.bind(1, static_cast<int>(lot));
auto result = query.execQuery();
if (result.eof()) {
return false;
}
if (result.eof()) return false;
if (!result.fieldIsNull(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& pair : m_Missions) {
auto* mission = pair.second;
if (mission->IsComplete()) {
continue;
}
for (const auto& [missionId, mission] : m_Missions) {
if (mission->IsComplete()) continue;
for (auto* task : mission->GetTasks()) {
if (task->IsComplete() || task->GetType() != eMissionTaskType::GATHER) {
continue;
}
if (!task->InAllTargets(lot)) {
continue;
}
if (!task->InAllTargets(lot)) continue;
return true;
}
}
const auto required = LookForAchievements(eMissionTaskType::GATHER, lot, false);
return required;
return LookForAchievements(eMissionTaskType::GATHER, lot, false);
}
void MissionComponent::LoadFromXml(tinyxml2::XMLDocument* doc) {
if (doc == nullptr) return;
if (!doc) return;
auto* mis = doc->FirstChildElement("obj")->FirstChildElement("mis");
if (mis == nullptr) return;
if (!mis) return;
auto* cur = mis->FirstChildElement("cur");
auto* done = mis->FirstChildElement("done");
@@ -516,7 +360,7 @@ void MissionComponent::LoadFromXml(tinyxml2::XMLDocument* doc) {
auto* doneM = done->FirstChildElement();
while (doneM) {
int missionId;
uint32_t missionId;
doneM->QueryAttribute("id", &missionId);
@@ -533,7 +377,7 @@ void MissionComponent::LoadFromXml(tinyxml2::XMLDocument* doc) {
uint32_t missionOrder{};
while (currentM) {
int missionId;
uint32_t 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()) {
mission->SetUniqueMissionOrderID(missionOrder);
if (missionOrder > m_LastUsedMissionOrderUID) m_LastUsedMissionOrderUID = missionOrder;
m_LastUsedMissionOrderUID = std::max(missionOrder, m_LastUsedMissionOrderUID);
}
currentM = currentM->NextSiblingElement();
@@ -554,7 +398,7 @@ void MissionComponent::LoadFromXml(tinyxml2::XMLDocument* doc) {
void MissionComponent::UpdateXml(tinyxml2::XMLDocument* doc) {
if (doc == nullptr) return;
if (!doc) return;
auto shouldInsertMis = false;
@@ -562,7 +406,7 @@ void MissionComponent::UpdateXml(tinyxml2::XMLDocument* doc) {
auto* mis = obj->FirstChildElement("mis");
if (mis == nullptr) {
if (!mis) {
mis = doc->NewElement("mis");
shouldInsertMis = true;
@@ -573,50 +417,33 @@ void MissionComponent::UpdateXml(tinyxml2::XMLDocument* doc) {
auto* done = doc->NewElement("done");
auto* cur = doc->NewElement("cur");
for (const auto& pair : m_Missions) {
auto* mission = pair.second;
for (const auto& [missionId, mission] : m_Missions) {
if (!mission) continue;
const auto complete = mission->IsComplete();
if (mission) {
const auto complete = mission->IsComplete();
auto* missionElement = doc->NewElement("m");
auto* m = doc->NewElement("m");
if (!complete && mission->IsMission()) missionElement->SetAttribute("o", mission->GetUniqueMissionOrderID());
if (complete) {
mission->UpdateXml(m);
mission->UpdateXml(missionElement);
done->LinkEndChild(m);
continue;
}
if (mission->IsMission()) m->SetAttribute("o", mission->GetUniqueMissionOrderID());
mission->UpdateXml(m);
cur->LinkEndChild(m);
}
cur->LinkEndChild(missionElement);
}
mis->InsertFirstChild(done);
mis->InsertEndChild(cur);
if (shouldInsertMis) {
obj->LinkEndChild(mis);
}
if (shouldInsertMis) obj->LinkEndChild(mis);
}
void MissionComponent::AddCollectible(int32_t collectibleID) {
// Check if this collectible is already in the list
if (HasCollectible(collectibleID)) {
return;
}
m_Collectibles.push_back(collectibleID);
void MissionComponent::AddCollectible(const int32_t collectibleID) {
if (!HasCollectible(collectibleID)) 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();
}
bool MissionComponent::HasMission(uint32_t missionId) {
bool MissionComponent::HasMission(const uint32_t missionId) const {
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
* progression of each of the mission task types (see eMissionTaskType).
*/
class MissionComponent : public Component
class MissionComponent final : public Component
{
public:
static const eReplicaComponentType ComponentType = eReplicaComponentType::MISSION;
inline static const eReplicaComponentType ComponentType = eReplicaComponentType::MISSION;
explicit MissionComponent(Entity* parent);
~MissionComponent() override;
@@ -39,35 +39,35 @@ public:
* Returns all 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
* @param missionId the id of the mission to get
* @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
* @param missionId the ID of the mission to get the mission state for
* @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.
* @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
*/
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.
* @param missionId the ID of the mission to accept
* @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.
@@ -75,13 +75,13 @@ public:
* @param skipChecks skips the checks for having completed all of the mission tasks
* @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.
* @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
@@ -94,7 +94,7 @@ public:
* @param count the number to progress by, for example the number of items
* @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
@@ -103,7 +103,7 @@ public:
* @param value the value to progress with
* @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
@@ -112,7 +112,7 @@ public:
* @param value the value to progress with
* @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
@@ -121,7 +121,7 @@ public:
* @param value the value to check the mission values before progressing
* @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
@@ -129,7 +129,7 @@ public:
* @param result the result to store the information in
* @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
@@ -141,34 +141,34 @@ public:
* @param count the number of values to progress by (differs by task type)
* @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
* @param lot the LOT to check for
* @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
* @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
* @param collectibleID the ID of the collectible to check
* @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
* @param missionId the ID of the mission to check
* @return if the entity has a certain mission in its inventory
*/
bool HasMission(uint32_t missionId);
bool HasMission(const uint32_t missionId) const;
private:
/**
@@ -189,7 +189,7 @@ private:
* @param targets optional targets to progress with
* @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

View File

@@ -1,6 +1,6 @@
/*
* Darkflame Universe
* Copyright 2019
* Copyright 2023
*/
#include <sstream>
@@ -18,68 +18,33 @@
#include "CDComponentsRegistryTable.h"
OfferedMission::OfferedMission(const uint32_t missionId, const bool offersMission, const bool acceptsMission) {
this->missionId = missionId;
this->offersMission = offersMission;
this->acceptsMission = acceptsMission;
MissionOfferComponent::MissionOfferComponent(Entity* parent, const int32_t componentId) : Component(parent) {
m_ComponentId = componentId;
}
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 {
return this->missionId;
}
auto missions = missionNpcComponentTable->Query([=](const CDMissionNPCComponent& entry) {
return entry.id == static_cast<int32_t>(m_ComponentId);
});
bool OfferedMission::GetOfferMission() const {
return this->offersMission;
}
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);
}
for (const auto& mission : missions) {
this->offeredMissions.emplace_back(
std::make_unique<OfferedMission>(mission.missionID, mission.offersMission, mission.acceptsMission)
);
}
}
MissionOfferComponent::~MissionOfferComponent() {
for (auto* mission : this->offeredMissions) {
if (mission) {
delete mission;
mission = nullptr;
}
}
offeredMissions.clear();
}
void MissionOfferComponent::OnUse(Entity* originator) {
OfferMissions(originator);
}
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.
auto* missionComponent = static_cast<MissionComponent*>(entity->GetComponent(eReplicaComponentType::MISSION));
auto* missionComponent = entity->GetComponent<MissionComponent>();
if (!missionComponent) {
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{};
CDMissions info{};
CDMissions missionInfo{};
if (specifiedMissionId > 0 && !Mission::IsValidMission(specifiedMissionId, info)) {
if (specifiedMissionId > 0 && !Mission::IsValidMission(specifiedMissionId, missionInfo)) {
return;
}
for (auto* offeredMission : this->offeredMissions) {
if (specifiedMissionId > 0) {
if (offeredMission->GetMissionId() != specifiedMissionId && !info.isRandom) {
continue;
}
for (const auto& offeredMission : offeredMissions) {
if (specifiedMissionId > 0 && (offeredMission->GetMissionId() != specifiedMissionId && !missionInfo.isRandom)) {
continue;
}
// 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);
if (mission != nullptr) {
if (mission) {
if (specifiedMissionId <= 0) {
// 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;
}
}
// We have the mission, if it is not complete, offer it
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);
@@ -127,50 +93,30 @@ void MissionOfferComponent::OfferMissions(Entity* entity, const uint32_t specifi
const auto canAccept = MissionPrerequisites::CanAccept(missionId, missionComponent->GetMissions());
// Mission has not yet been accepted - check the prereqs
if (!canAccept)
continue;
if (!canAccept || !Mission::IsValidMission(missionId, missionInfo)) continue;
if (!Mission::IsValidMission(missionId, info)) {
continue;
}
// This means the mission is part of a random pool of missions.
if (missionInfo.isRandom && missionInfo.randomPool.empty()) continue;
const auto& randomPool = info.randomPool;
const auto isRandom = info.isRandom;
if (missionInfo.isRandom && !missionInfo.randomPool.empty()) {
auto randomMissionPoolStr = GeneralUtils::SplitString(missionInfo.randomPool, ',');
if (isRandom && randomPool.empty()) // This means the mission is part of a random pool of missions.
{
continue;
}
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());
}
std::vector<uint32_t> randomMissions;
for (const auto& randomMissionStr : randomMissionPoolStr) {
uint32_t randomMission;
if (GeneralUtils::TryParse(randomMissionStr, randomMission)) randomMissions.push_back(randomMission);
}
if (specifiedMissionId > 0) {
const auto& iter = std::find(randomMissionPool.begin(), randomMissionPool.end(), specifiedMissionId);
if (iter != randomMissionPool.end() && MissionPrerequisites::CanAccept(specifiedMissionId, missionComponent->GetMissions())) {
GameMessages::SendOfferMission(entity->GetObjectID(), entity->GetSystemAddress(), specifiedMissionId, m_Parent->GetObjectID());
if (std::find(randomMissions.begin(), randomMissions.end(), specifiedMissionId) != randomMissions.end() &&
MissionPrerequisites::CanAccept(specifiedMissionId, missionComponent->GetMissions())) {
GameMessages::SendOfferMission(entity->GetObjectID(), entity->GetSystemAddress(), specifiedMissionId, m_ParentEntity->GetObjectID());
return;
}
}
std::vector<uint32_t> canAcceptPool;
for (const auto sample : randomMissionPool) {
for (const auto& sample : randomMissions) {
const auto state = missionComponent->GetMissionState(sample);
if (state == eMissionState::ACTIVE ||
@@ -180,31 +126,29 @@ void MissionOfferComponent::OfferMissions(Entity* entity, const uint32_t specifi
sample == specifiedMissionId) {
mission = missionComponent->GetMission(sample);
if (mission == nullptr || mission->IsAchievement()) {
continue;
}
if (!mission || mission->IsAchievement()) continue;
GameMessages::SendOfferMission(entity->GetObjectID(), entity->GetSystemAddress(), sample, m_Parent->GetObjectID());
GameMessages::SendOfferMission(entity->GetObjectID(), entity->GetSystemAddress(), sample, m_ParentEntity->GetObjectID());
canAcceptPool.clear();
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);
}
}
// If the mission is already active or we already completed one of them today
if (canAcceptPool.empty())
continue;
if (canAcceptPool.empty()) 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()) {
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
* Copyright 2019
* Copyright 2023
*/
#ifndef MISSIONOFFERCOMPONENT_H
#define MISSIONOFFERCOMPONENT_H
#ifndef __MISSIONOFFERCOMPONENT_H__
#define __MISSIONOFFERCOMPONENT_H__
#pragma once
#include <memory>
#include <vector>
#include <stdint.h>
#include "dCommonVars.h"
#include "Component.h"
#include <vector>
#include <stdint.h>
#include "eReplicaComponentType.h"
class Entity;
@@ -18,25 +21,29 @@ class Entity;
* Light wrapper around missions that may be offered by an entity
*/
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
* @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
* @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)
* @return true if this mission may be accepted by the entity, false otherwise
*/
bool GetAcceptMission() const;
bool GetAcceptMission() const { return acceptsMission; };
private:
@@ -59,12 +66,13 @@ private:
/**
* Allows entities to offer missions to other entities, depending on their mission inventory progression.
*/
class MissionOfferComponent : public Component {
class MissionOfferComponent final : public Component {
public:
static const eReplicaComponentType ComponentType = eReplicaComponentType::MISSION_OFFER;
inline static const eReplicaComponentType ComponentType = eReplicaComponentType::MISSION_OFFER;
MissionOfferComponent(Entity* parent, LOT parentLot);
~MissionOfferComponent() override;
MissionOfferComponent(Entity* parent, const int32_t componentId = -1);
void LoadTemplateData() override;
/**
* 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
*/
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"
class Entity;
enum class ePhysicsBehaviorType : int32_t;
/**
* 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:
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);
@@ -29,7 +30,11 @@ public:
* Sets the original position of the model
* @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
@@ -41,10 +46,29 @@ public:
* Sets the original rotation of the model
* @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:
/**
* 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
*/
@@ -55,8 +79,4 @@ private:
*/
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 "Entity.h"
ModuleAssemblyComponent::ModuleAssemblyComponent(Entity* parent) : Component(parent) {
m_SubKey = LWOOBJID_EMPTY;
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) {
std::u16string val{};
val.reserve(value.size() + 1);
for (auto character : value) {
if (character == '+') character = ';';
val.push_back(character);
}
val.push_back(';');
m_AssemblyPartsLOTs = val;
}
const std::u16string& ModuleAssemblyComponent::GetAssemblyPartsLOTs() const {
return m_AssemblyPartsLOTs;
m_AssemblyPartsLOTs = value;
std::replace(m_AssemblyPartsLOTs.begin(), m_AssemblyPartsLOTs.end(), u'+', u';');
// 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.
m_AssemblyPartsLOTs.push_back(u';');
}
void ModuleAssemblyComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags) {
if (bIsInitialUpdate) {
outBitStream->Write1();
if (!bIsInitialUpdate) return;
outBitStream->Write(bIsInitialUpdate);
outBitStream->Write(m_SubKey != LWOOBJID_EMPTY);
if (m_SubKey != LWOOBJID_EMPTY) {
outBitStream->Write(m_SubKey);
}
outBitStream->Write(m_SubKey != LWOOBJID_EMPTY);
if (m_SubKey != LWOOBJID_EMPTY) outBitStream->Write(m_SubKey);
outBitStream->Write(m_UseOptionalParts);
outBitStream->Write(m_UseOptionalParts);
outBitStream->Write(static_cast<uint16_t>(m_AssemblyPartsLOTs.size()));
for (char16_t character : m_AssemblyPartsLOTs) {
outBitStream->Write(character);
}
outBitStream->Write<uint16_t>(m_AssemblyPartsLOTs.size());
for (const char16_t character : m_AssemblyPartsLOTs) {
outBitStream->Write(character);
}
}
void ModuleAssemblyComponent::Update(float deltaTime) {
}

View File

@@ -1,49 +1,51 @@
#ifndef __MODULEASSEMBLYCOMPONENT__H__
#define __MODULEASSEMBLYCOMPONENT__H__
#pragma once
#include "BitStream.h"
#include "Entity.h"
#include "Component.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
* 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.
*/
class ModuleAssemblyComponent : public Component {
class ModuleAssemblyComponent final : public Component {
public:
static const eReplicaComponentType ComponentType = eReplicaComponentType::MODULE_ASSEMBLY;
inline static const eReplicaComponentType ComponentType = eReplicaComponentType::MODULE_ASSEMBLY;
ModuleAssemblyComponent(Entity* parent);
~ModuleAssemblyComponent() override;
void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags);
void Update(float deltaTime) override;
/**
* Sets the subkey of this entity
* @param value the subkey to set
*/
void SetSubKey(LWOOBJID value);
void SetSubKey(const LWOOBJID& value) { m_SubKey = value; };
/**
* Returns 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
* @param value the value to set
*/
void SetUseOptionalParts(bool value);
void SetUseOptionalParts(bool value) { m_UseOptionalParts = value; };
/**
* Returns the optional parts value
* @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)
@@ -55,7 +57,7 @@ public:
* Returns the assembly part lots (the subsections of this modular build)
* @return
*/
const std::u16string& GetAssemblyPartsLOTs() const;
const std::u16string& GetAssemblyPartsLOTs() const { return m_AssemblyPartsLOTs; };
private:
@@ -75,3 +77,5 @@ private:
*/
std::u16string m_AssemblyPartsLOTs;
};
#endif //!__MODULEASSEMBLYCOMPONENT__H__

View File

@@ -10,31 +10,28 @@
#include "EntityManager.h"
#include "SimplePhysicsComponent.h"
#include "CDClientManager.h"
#include "Game.h"
#include "dLogger.h"
#include "SimplePhysicsComponent.h"
#include "ControllablePhysicsComponent.h"
#include "CDComponentsRegistryTable.h"
#include "CDPhysicsComponentTable.h"
#include "CDMovementAIComponentTable.h"
#include "Entity.h"
#include "BaseCombatAIComponent.h"
std::map<LOT, float> MovementAIComponent::m_PhysicsSpeedCache = {};
MovementAIComponent::MovementAIComponent(Entity* parent, MovementAIInfo info) : Component(parent) {
m_Info = std::move(info);
MovementAIComponent::MovementAIComponent(Entity* parent, int32_t componentId) : Component(parent) {
m_ComponentId = componentId;
m_Done = true;
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_Interrupted = false;
m_PullPoint = {};
m_PullPoint = NiPoint3::ZERO;
m_HaltDistance = 0;
m_Timer = 0;
m_CurrentSpeed = 0;
@@ -43,7 +40,47 @@ MovementAIComponent::MovementAIComponent(Entity* parent, MovementAIInfo info) :
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) {
if (m_Interrupted) {
@@ -51,11 +88,11 @@ void MovementAIComponent::Update(const float deltaTime) {
const auto speed = deltaTime * 2.5f;
NiPoint3 velocity;
velocity.x = (m_PullPoint.x - source.x) * speed;
velocity.y = (m_PullPoint.y - source.y) * speed;
velocity.z = (m_PullPoint.z - source.z) * speed;
NiPoint3 velocity(
(m_PullPoint.x - source.x) * speed,
(m_PullPoint.y - source.y) * speed,
(m_PullPoint.z - source.z) * speed
);
SetPosition(source + velocity);
@@ -66,28 +103,24 @@ void MovementAIComponent::Update(const float deltaTime) {
return;
}
if (AtFinalWaypoint()) // Are we done?
{
return;
}
// Are we done?
if (AtFinalWaypoint()) return;
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();
return;
}
}
if (m_Timer > 0) {
if (m_Timer > 0.0f) {
m_Timer -= deltaTime;
if (m_Timer > 0) {
return;
}
if (m_Timer > 0.0f) return;
m_Timer = 0;
m_Timer = 0.0f;
}
const auto source = GetCurrentWaypoint();
@@ -149,11 +182,7 @@ nextAction:
SetVelocity(velocity);
EntityManager::Instance()->SerializeEntity(m_Parent);
}
const MovementAIInfo& MovementAIComponent::GetInfo() const {
return m_Info;
EntityManager::Instance()->SerializeEntity(m_ParentEntity);
}
bool MovementAIComponent::AdvanceWaypointIndex() {
@@ -167,37 +196,23 @@ bool MovementAIComponent::AdvanceWaypointIndex() {
}
NiPoint3 MovementAIComponent::GetCurrentWaypoint() const {
if (m_PathIndex >= m_CurrentPath.size()) {
return GetCurrentPosition();
}
return m_CurrentPath[m_PathIndex];
}
NiPoint3 MovementAIComponent::GetNextWaypoint() const {
return m_NextWaypoint;
}
NiPoint3 MovementAIComponent::GetCurrentPosition() const {
return m_Parent->GetPosition();
return m_PathIndex >= m_CurrentPath.size() ? GetCurrentPosition() : m_CurrentPath[m_PathIndex];
}
NiPoint3 MovementAIComponent::ApproximateLocation() const {
auto source = GetCurrentPosition();
if (m_Done) {
return source;
}
if (m_Done) return source;
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);
auto y = source.y + factor * (destination.y - source.y);
auto z = source.z + factor * (destination.z - source.z);
NiPoint3 approximation = NiPoint3(x, y, z);
NiPoint3 approximation = NiPoint3(
source.x + factor * (destination.x - source.x),
source.y + factor * (destination.y - source.y),
source.z + factor * (destination.z - source.z)
);
if (dpWorld::Instance().IsLoaded()) {
approximation.y = dpWorld::Instance().GetNavMesh()->GetHeightAtPoint(approximation);
@@ -221,39 +236,30 @@ bool MovementAIComponent::Warp(const NiPoint3& point) {
SetPosition(destination);
EntityManager::Instance()->SerializeEntity(m_Parent);
EntityManager::Instance()->SerializeEntity(m_ParentEntity);
return true;
}
float MovementAIComponent::GetTimer() const {
return m_Timer;
}
bool MovementAIComponent::AtFinalWaypoint() const {
return m_Done;
}
void MovementAIComponent::Stop() {
if (m_Done) {
return;
}
if (m_Done) return;
SetPosition(ApproximateLocation());
SetVelocity(NiPoint3::ZERO);
m_TotalTime = m_Timer = 0;
m_TotalTime = 0.0f;
m_Timer = 0.0f;
m_Done = true;
m_CurrentPath = {};
m_CurrentPath.clear();
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) {
@@ -263,11 +269,9 @@ void MovementAIComponent::PullToPoint(const NiPoint3& point) {
m_PullPoint = point;
}
void MovementAIComponent::SetPath(std::vector<NiPoint3> path) {
std::reverse(path.begin(), path.end());
for (const auto& point : path) {
m_Queue.push(point);
void MovementAIComponent::SetPath(const std::vector<NiPoint3>& path) {
for (auto itr = path.rbegin(); itr != path.rend(); ++itr) {
m_Queue.push(*itr);
}
SetDestination(m_Queue.top());
@@ -275,7 +279,7 @@ void MovementAIComponent::SetPath(std::vector<NiPoint3> path) {
m_Queue.pop();
}
float MovementAIComponent::GetBaseSpeed(LOT lot) {
float MovementAIComponent::GetBaseSpeed(const LOT lot) {
// Check if the lot is in the cache
const auto& it = m_PhysicsSpeedCache.find(lot);
@@ -283,30 +287,19 @@ float MovementAIComponent::GetBaseSpeed(LOT lot) {
return it->second;
}
CDComponentsRegistryTable* componentRegistryTable = CDClientManager::Instance().GetTable<CDComponentsRegistryTable>();
CDPhysicsComponentTable* physicsComponentTable = CDClientManager::Instance().GetTable<CDPhysicsComponentTable>();
auto* componentRegistryTable = CDClientManager::Instance().GetTable<CDComponentsRegistryTable>();
auto* physicsComponentTable = CDClientManager::Instance().GetTable<CDPhysicsComponentTable>();
int32_t componentID;
CDPhysicsComponent* physicsComponent = nullptr;
componentID = componentRegistryTable->GetByIDAndType(lot, eReplicaComponentType::CONTROLLABLE_PHYSICS, -1);
if (componentID != -1) {
physicsComponent = physicsComponentTable->GetByID(componentID);
goto foundComponent;
if (componentID == -1) {
componentID = componentRegistryTable->GetByIDAndType(lot, eReplicaComponentType::SIMPLE_PHYSICS, -1);
}
componentID = componentRegistryTable->GetByIDAndType(lot, eReplicaComponentType::SIMPLE_PHYSICS, -1);
if (componentID != -1) {
physicsComponent = physicsComponentTable->GetByID(componentID);
goto foundComponent;
}
foundComponent:
physicsComponent = physicsComponentTable->GetByID(componentID);
// Client defaults speed to 10 and if the speed is also null in the table, it defaults to 10.
float speed = 10.0f;
@@ -321,73 +314,36 @@ foundComponent:
return speed;
}
void MovementAIComponent::SetPosition(const NiPoint3& value) {
auto* controllablePhysicsComponent = m_Parent->GetComponent<ControllablePhysicsComponent>();
if (controllablePhysicsComponent != nullptr) {
controllablePhysicsComponent->SetPosition(value);
return;
}
auto* simplePhysicsComponent = m_Parent->GetComponent<SimplePhysicsComponent>();
if (simplePhysicsComponent != nullptr) {
simplePhysicsComponent->SetPosition(value);
}
void MovementAIComponent::SetPosition(const NiPoint3& value) const {
m_ParentEntity->SetPosition(value);
}
void MovementAIComponent::SetRotation(const NiQuaternion& value) {
if (m_LockRotation) {
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::SetRotation(const NiQuaternion& value) const {
if (!m_LockRotation) m_ParentEntity->SetRotation(value);
}
void MovementAIComponent::SetVelocity(const NiPoint3& value) {
auto* controllablePhysicsComponent = m_Parent->GetComponent<ControllablePhysicsComponent>();
void MovementAIComponent::SetVelocity(const NiPoint3& value) const {
auto* controllablePhysicsComponent = m_ParentEntity->GetComponent<ControllablePhysicsComponent>();
if (controllablePhysicsComponent != nullptr) {
if (controllablePhysicsComponent) {
controllablePhysicsComponent->SetVelocity(value);
return;
}
auto* simplePhysicsComponent = m_Parent->GetComponent<SimplePhysicsComponent>();
auto* simplePhysicsComponent = m_ParentEntity->GetComponent<SimplePhysicsComponent>();
if (simplePhysicsComponent != nullptr) {
if (simplePhysicsComponent) {
simplePhysicsComponent->SetVelocity(value);
}
}
void MovementAIComponent::SetDestination(const NiPoint3& value) {
if (m_Interrupted) {
return;
}
/*if (Vector3::DistanceSquared(value, GetDestination()) < 2 * 2)
{
return;
}*/
if (m_Interrupted) return;
const auto location = ApproximateLocation();
if (!AtFinalWaypoint()) {
SetPosition(location);
}
if (!AtFinalWaypoint()) SetPosition(location);
std::vector<NiPoint3> computedPath;
@@ -409,17 +365,15 @@ void MovementAIComponent::SetDestination(const NiPoint3& value) {
}
}
if (computedPath.empty()) // Somehow failed
{
return;
}
// Somehow failed
if (computedPath.empty()) return;
m_CurrentPath.clear();
m_CurrentPath.push_back(location);
// Simply path
for (auto point : computedPath) {
for (auto& point : computedPath) {
if (dpWorld::Instance().IsLoaded()) {
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(computedPath[computedPath.size() - 1]);
m_CurrentPath.push_back(computedPath.back());
m_PathIndex = 0;
m_TotalTime = m_Timer = 0;
m_TotalTime = 0.0f;
m_Timer = 0.0f;
m_Done = false;
}
NiPoint3 MovementAIComponent::GetDestination() const {
if (m_CurrentPath.empty()) {
return GetCurrentPosition();
}
return m_CurrentPath[m_CurrentPath.size() - 1];
return m_CurrentPath.empty() ? GetCurrentPosition() : m_CurrentPath.back();
}
void MovementAIComponent::SetSpeed(const float value) {
@@ -449,38 +400,6 @@ void MovementAIComponent::SetSpeed(const float value) {
m_Acceleration = value / 5;
}
float MovementAIComponent::GetSpeed() const {
return m_Speed;
}
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;
NiPoint3 MovementAIComponent::GetCurrentPosition() const {
return m_ParentEntity->GetPosition();
}

View File

@@ -3,18 +3,16 @@
* Copyright 2018
*/
#ifndef MOVEMENTAICOMPONENT_H
#define MOVEMENTAICOMPONENT_H
#ifndef __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 "eReplicaComponentType.h"
#include <vector>
class ControllablePhysicsComponent;
class BaseCombatAIComponent;
@@ -23,6 +21,9 @@ class BaseCombatAIComponent;
* Information that describes the different variables used to make an entity move around
*/
struct MovementAIInfo {
// copy assignment
MovementAIInfo& operator=(const MovementAIInfo& other) = default;
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
* actually handles attackig and following enemy entities.
*/
class MovementAIComponent : public Component {
class MovementAIComponent final : public Component {
public:
static const eReplicaComponentType ComponentType = eReplicaComponentType::MOVEMENT_AI;
inline static const eReplicaComponentType ComponentType = eReplicaComponentType::MOVEMENT_AI;
MovementAIComponent(Entity* parentEntity, MovementAIInfo info);
~MovementAIComponent() override;
MovementAIComponent(Entity* parentEntity, int32_t componentId = -1);
void Startup() override;
void LoadTemplateData() override;
void LoadConfigData() override;
void Update(float deltaTime) override;
@@ -68,7 +71,7 @@ public:
* Returns 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
@@ -86,61 +89,61 @@ public:
* Sets the max speed at which this entity may run
* @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
* @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
* @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
* @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)
* @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)
* @return the current halting distance
*/
float GetHaltDistance() const;
float GetHaltDistance() const { return m_HaltDistance; };
/**
* Sets the speed the entity is currently running at
* @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
* @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
* @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
* @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
@@ -158,7 +161,7 @@ public:
* Returns 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
@@ -184,13 +187,13 @@ public:
* 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
*/
float GetTimer() const;
float GetTimer() const { return m_Timer; };
/**
* Returns 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
@@ -208,34 +211,35 @@ public:
* Sets a path to follow for the AI
* @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
* @param lot the lot to check for
* @return the base speed of the lot
*/
static float GetBaseSpeed(LOT lot);
static float GetBaseSpeed(const LOT lot);
void SetMoveInfo(const MovementAIInfo& value);
private:
/**
* Sets the current position of the entity
* @param value the position to set
*/
void SetPosition(const NiPoint3& value);
void SetPosition(const NiPoint3& value) const;
/**
* Sets the current rotation of the entity
* @param value the rotation to set
*/
void SetRotation(const NiQuaternion& value);
void SetRotation(const NiQuaternion& value) const;
/**
* Sets the current velocity of the entityes
* @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
@@ -326,6 +330,8 @@ private:
* Cache of all lots and their respective speeds
*/
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
* Copyright 2019
* Copyright 2023
*/
#include "MovingPlatformComponent.h"
@@ -15,7 +15,7 @@
#include "Zone.h"
MoverSubComponent::MoverSubComponent(const NiPoint3& startPos) {
mPosition = {};
mPosition = NiPoint3::ZERO;
mState = eMovementPlatformState::Stopped;
mDesiredWaypointIndex = 0; // -1;
@@ -30,8 +30,6 @@ MoverSubComponent::MoverSubComponent(const NiPoint3& startPos) {
mIdleTimeElapsed = 0.0f;
}
MoverSubComponent::~MoverSubComponent() = default;
void MoverSubComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags) const {
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) {
m_MoverSubComponentType = eMoverSubComponentType::mover;
m_MoverSubComponent = new MoverSubComponent(m_Parent->GetDefaultPosition());
m_MoverSubComponent = new MoverSubComponent(m_ParentEntity->GetDefaultPosition());
m_PathName = GeneralUtils::ASCIIToUTF16(pathName);
m_Path = dZoneManager::Instance()->GetZone()->GetPath(pathName);
m_NoAutoStart = false;
if (m_Path == nullptr) {
if (!m_Path) {
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
if (!m_Serialize) {
outBitStream->Write<bool>(false);
outBitStream->Write<bool>(false);
outBitStream->Write0();
outBitStream->Write0();
return;
}
outBitStream->Write<bool>(true);
outBitStream->Write1();
auto hasPath = !m_PathingStopped && !m_PathName.empty();
outBitStream->Write(hasPath);
@@ -133,7 +131,7 @@ void MovingPlatformComponent::SetMovementState(eMovementPlatformState value) {
subComponent->mState = value;
EntityManager::Instance()->SerializeEntity(m_Parent);
EntityManager::Instance()->SerializeEntity(m_ParentEntity);
}
void MovingPlatformComponent::GotoWaypoint(uint32_t index, bool stopAtWaypoint) {
@@ -147,7 +145,7 @@ void MovingPlatformComponent::GotoWaypoint(uint32_t index, bool stopAtWaypoint)
}
void MovingPlatformComponent::StartPathing() {
//GameMessages::SendStartPathing(m_Parent);
//GameMessages::SendStartPathing(m_ParentEntity);
m_PathingStopped = false;
auto* subComponent = static_cast<MoverSubComponent*>(m_MoverSubComponent);
@@ -167,14 +165,14 @@ void MovingPlatformComponent::StartPathing() {
targetPosition = nextWaypoint.position;
} else {
subComponent->mPosition = m_Parent->GetPosition();
subComponent->mPosition = m_ParentEntity->GetPosition();
subComponent->mSpeed = 1.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);
});
@@ -182,19 +180,17 @@ void MovingPlatformComponent::StartPathing() {
const auto travelNext = subComponent->mWaitTime + travelTime;
m_Parent->AddCallbackTimer(travelTime, [subComponent, this] {
for (CppScripts::Script* script : CppScripts::GetEntityScripts(m_Parent)) {
script->OnWaypointReached(m_Parent, subComponent->mNextWaypointIndex);
}
m_ParentEntity->AddCallbackTimer(travelTime, [subComponent, this] {
m_ParentEntity->GetScript()->OnWaypointReached(m_ParentEntity, subComponent->mNextWaypointIndex);
});
m_Parent->AddCallbackTimer(travelNext, [this] {
m_ParentEntity->AddCallbackTimer(travelNext, [this] {
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() {
@@ -222,17 +218,17 @@ void MovingPlatformComponent::ContinuePathing() {
targetPosition = nextWaypoint.position;
} else {
subComponent->mPosition = m_Parent->GetPosition();
subComponent->mPosition = m_ParentEntity->GetPosition();
subComponent->mSpeed = 1.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;
behavior = PathBehavior::Loop;
}
if (m_Parent->GetLOT() == 9483) {
if (m_ParentEntity->GetLOT() == 9483) {
behavior = PathBehavior::Bounce;
} else {
return;
@@ -242,7 +238,7 @@ void MovingPlatformComponent::ContinuePathing() {
subComponent->mCurrentWaypointIndex = pathSize;
switch (behavior) {
case PathBehavior::Once:
EntityManager::Instance()->SerializeEntity(m_Parent);
EntityManager::Instance()->SerializeEntity(m_ParentEntity);
return;
case PathBehavior::Bounce:
@@ -271,7 +267,7 @@ void MovingPlatformComponent::ContinuePathing() {
subComponent->mCurrentWaypointIndex = 1;
*/
//GameMessages::SendPlatformResync(m_Parent, UNASSIGNED_SYSTEM_ADDRESS);
//GameMessages::SendPlatformResync(m_ParentEntity, UNASSIGNED_SYSTEM_ADDRESS);
if (subComponent->mCurrentWaypointIndex == subComponent->mDesiredWaypointIndex) {
// TODO: Send event?
@@ -280,35 +276,33 @@ void MovingPlatformComponent::ContinuePathing() {
return;
}
m_Parent->CancelCallbackTimers();
m_ParentEntity->CancelCallbackTimers();
m_Parent->AddCallbackTimer(subComponent->mWaitTime, [this] {
m_ParentEntity->AddCallbackTimer(subComponent->mWaitTime, [this] {
SetMovementState(eMovementPlatformState::Moving);
});
auto travelTime = Vector3::Distance(targetPosition, subComponent->mPosition) / subComponent->mSpeed + 1.5;
if (m_Parent->GetLOT() == 9483) {
if (m_ParentEntity->GetLOT() == 9483) {
travelTime += 20;
}
const auto travelNext = subComponent->mWaitTime + travelTime;
m_Parent->AddCallbackTimer(travelTime, [subComponent, this] {
for (CppScripts::Script* script : CppScripts::GetEntityScripts(m_Parent)) {
script->OnWaypointReached(m_Parent, subComponent->mNextWaypointIndex);
}
m_ParentEntity->AddCallbackTimer(travelTime, [subComponent, this] {
m_ParentEntity->GetScript()->OnWaypointReached(m_ParentEntity, subComponent->mNextWaypointIndex);
});
m_Parent->AddCallbackTimer(travelNext, [this] {
m_ParentEntity->AddCallbackTimer(travelNext, [this] {
ContinuePathing();
});
EntityManager::Instance()->SerializeEntity(m_Parent);
EntityManager::Instance()->SerializeEntity(m_ParentEntity);
}
void MovingPlatformComponent::StopPathing() {
//m_Parent->CancelCallbackTimers();
//m_ParentEntity->CancelCallbackTimers();
auto* subComponent = static_cast<MoverSubComponent*>(m_MoverSubComponent);
@@ -318,9 +312,9 @@ void MovingPlatformComponent::StopPathing() {
subComponent->mDesiredWaypointIndex = -1;
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) {
@@ -338,10 +332,10 @@ void MovingPlatformComponent::SetNoAutoStart(const bool value) {
void MovingPlatformComponent::WarpToWaypoint(size_t index) {
const auto& waypoint = m_Path->pathWaypoints[index];
m_Parent->SetPosition(waypoint.position);
m_Parent->SetRotation(waypoint.rotation);
m_ParentEntity->SetPosition(waypoint.position);
m_ParentEntity->SetRotation(waypoint.rotation);
EntityManager::Instance()->SerializeEntity(m_Parent);
EntityManager::Instance()->SerializeEntity(m_ParentEntity);
}
size_t MovingPlatformComponent::GetLastWaypointIndex() const {

View File

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

View File

@@ -1,19 +1,21 @@
#pragma once
#include "Entity.h"
#include "MovementAIComponent.h"
#include "Component.h"
#include "Preconditions.h"
#include "eReplicaComponentType.h"
enum class PetAbilityType
{
class PreconditionExpression;
class MovementAIComponent;
enum class PetAbilityType : uint32_t {
Invalid,
GoToObject,
JumpOnObject,
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
* to dig for treasure and activate pet bouncers.
@@ -21,10 +23,9 @@ enum class PetAbilityType
class PetComponent : public Component
{
public:
static const eReplicaComponentType ComponentType = eReplicaComponentType::PET;
inline static const eReplicaComponentType ComponentType = eReplicaComponentType::PET;
explicit PetComponent(Entity* parentEntity, uint32_t componentId);
~PetComponent() override;
void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags);
void Update(float deltaTime) override;
@@ -109,7 +110,7 @@ public:
* Returns the ID of the owner of this pet (if any)
* @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)
@@ -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
* @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
* @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
* @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
* @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
* bit map
* @return the status of this pet
*/
uint32_t GetStatus() const;
uint32_t GetStatus() const { return m_Status; }
/**
* Sets the current status of the pet
* @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
* @return an ability the pet may perform
*/
PetAbilityType GetAbility() const;
PetAbilityType GetAbility() const { return m_Ability; }
/**
* Sets the ability of the pet, currently unused
@@ -172,12 +173,6 @@ public:
*/
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
* @param petName the name of the pet to set
@@ -195,14 +190,14 @@ public:
* @param tamer the entity that's currently taming
* @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)
* @param owner the owner of the pet that's spawned
* @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
@@ -263,7 +258,7 @@ private:
/**
* 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
@@ -359,5 +354,5 @@ private:
/**
* 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 "dpShapeBox.h"
#include "dpShapeSphere.h"
#include "NiPoint3.h"
#include "NiQuaternion.h"
PhantomPhysicsComponent::PhantomPhysicsComponent(Entity* parent) : Component(parent) {
m_Position = m_Parent->GetDefaultPosition();
m_Rotation = m_Parent->GetDefaultRotation();
m_Scale = m_Parent->GetDefaultScale();
m_Position = m_ParentEntity->GetDefaultPosition();
m_Rotation = m_ParentEntity->GetDefaultRotation();
m_Scale = m_ParentEntity->GetDefaultScale();
m_dpEntity = nullptr;
m_EffectInfoDirty = false;
@@ -45,232 +47,148 @@ PhantomPhysicsComponent::PhantomPhysicsComponent(Entity* parent) : Component(par
m_Max = 1;
m_IsDirectional = false;
m_Direction = NiPoint3(); // * m_DirectionalMultiplier
m_Direction = NiPoint3::ZERO;
}
if (m_Parent->GetVar<bool>(u"create_physics")) {
CreatePhysics();
}
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]));
}
}
PhantomPhysicsComponent::~PhantomPhysicsComponent() {
if (m_dpEntity) dpWorld::Instance().RemoveEntity(m_dpEntity);
}
void PhantomPhysicsComponent::LoadTemplateData() {
// HF - RespawnPoints. Legacy respawn entity.
if (m_Parent->GetLOT() == 4945) {
if (m_ParentEntity->GetLOT() == LOT_LEGACY_RESPAWN_POINT) {
m_IsRespawnVolume = true;
m_RespawnPos = m_Position;
m_RespawnRot = m_Rotation;
}
/*
for (LDFBaseData* data : settings) {
if (data) {
if (data->GetKey() == u"create_physics") {
if (bool(std::stoi(data->GetValueAsString()))) {
CreatePhysics(settings);
}
}
auto* compRegistryTable = CDClientManager::Instance().GetTable<CDComponentsRegistryTable>();
auto componentID = compRegistryTable->GetByIDAndType(m_ParentEntity->GetLOT(), eReplicaComponentType::PHANTOM_PHYSICS);
if (data->GetKey() == u"respawnVol") {
if (bool(std::stoi(data->GetValueAsString()))) {
m_IsRespawnVolume = true;
}
}
auto* physCompTable = CDClientManager::Instance().GetTable<CDPhysicsComponentTable>();
if (m_IsRespawnVolume) {
if (data->GetKey() == u"rspPos") {
//Joy, we get to split strings!
std::stringstream test(data->GetValueAsString());
std::string segment;
std::vector<std::string> seglist;
if (!physCompTable) return;
while (std::getline(test, segment, '\x1f')) {
seglist.push_back(segment);
}
auto* info = physCompTable->GetByID(componentID);
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 (data->GetKey() == u"rspRot") {
//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')) {
seglist.push_back(segment);
}
m_RespawnRot = NiQuaternion(std::stof(seglist[0]), std::stof(seglist[1]), std::stof(seglist[2]), std::stof(seglist[3]));
}
}
if (m_Parent->GetLOT() == 4945) // HF - RespawnPoints
{
m_IsRespawnVolume = true;
m_RespawnPos = m_Position;
m_RespawnRot = m_Rotation;
}
}
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);
m_dpEntity->SetRotation(m_Rotation);
m_dpEntity->SetPosition(m_Position);
} 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_ParentEntity->GetObjectID(), 1638.4f, 13.521004f * 2.0f, 1638.4f);
m_dpEntity->SetScale(m_Scale);
m_dpEntity->SetRotation(m_Rotation);
m_dpEntity->SetPosition(m_Position);
} 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);
m_dpEntity->SetPosition(m_Position);
} else if (info->physicsAsset == "env\\env_gen_placeholderphysics.hkx") {
m_dpEntity = new dpEntity(m_ParentEntity->GetObjectID(), 20.0f, 20.0f, 20.0f);
m_dpEntity->SetScale(m_Scale);
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() {
if (m_dpEntity) {
dpWorld::Instance().RemoveEntity(m_dpEntity);
void PhantomPhysicsComponent::LoadConfigData() {
if (m_ParentEntity->GetVar<bool>(u"create_physics")) {
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() {
unsigned char alpha;
unsigned char red;
unsigned char green;
unsigned char blue;
int type = -1;
float x = 0.0f;
float y = 0.0f;
float z = 0.0f;
int32_t type = -1;
NiPoint3 pos;
float width = 0.0f; //aka "radius"
float height = 0.0f;
if (m_Parent->HasVar(u"primitiveModelType")) {
type = m_Parent->GetVar<int32_t>(u"primitiveModelType");
x = m_Parent->GetVar<float>(u"primitiveModelValueX");
y = m_Parent->GetVar<float>(u"primitiveModelValueY");
z = m_Parent->GetVar<float>(u"primitiveModelValueZ");
if (m_ParentEntity->HasVar(u"primitiveModelType")) {
type = m_ParentEntity->GetVar<int32_t>(u"primitiveModelType");
pos.x = m_ParentEntity->GetVar<float>(u"primitiveModelValueX");
pos.y = m_ParentEntity->GetVar<float>(u"primitiveModelValueY");
pos.z = m_ParentEntity->GetVar<float>(u"primitiveModelValueZ");
} else {
CDComponentsRegistryTable* compRegistryTable = CDClientManager::Instance().GetTable<CDComponentsRegistryTable>();
auto componentID = compRegistryTable->GetByIDAndType(m_Parent->GetLOT(), eReplicaComponentType::PHANTOM_PHYSICS);
auto* compRegistryTable = CDClientManager::Instance().GetTable<CDComponentsRegistryTable>();
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;
width = info->playerRadius;
@@ -279,8 +197,8 @@ void PhantomPhysicsComponent::CreatePhysics() {
switch (type) {
case 1: { //Make a new box shape
NiPoint3 boxSize(x, y, z);
if (x == 0.0f) {
BoxDimensions boxSize(pos.x, pos.y, pos.z);
if (pos.x == 0.0f) {
//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 (width < 0.5f) width = 2.0f;
@@ -289,17 +207,21 @@ void PhantomPhysicsComponent::CreatePhysics() {
width = width * m_Scale;
height = height * m_Scale;
boxSize = NiPoint3(width, height, width);
boxSize = BoxDimensions(width, height, width);
}
m_dpEntity = new dpEntity(m_Parent->GetObjectID(), boxSize);
if (m_dpEntity) delete m_dpEntity;
m_dpEntity = new dpEntity(m_ParentEntity->GetObjectID(), boxSize);
break;
}
default: {
Game::logger->Log("PhantomPhysicsComponent", "Unknown shape type: %d", type);
break;
}
}
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);
@@ -312,12 +234,13 @@ void PhantomPhysicsComponent::Serialize(RakNet::BitStream* outBitStream, bool bI
outBitStream->Write(m_Position.x);
outBitStream->Write(m_Position.y);
outBitStream->Write(m_Position.z);
outBitStream->Write(m_Rotation.x);
outBitStream->Write(m_Rotation.y);
outBitStream->Write(m_Rotation.z);
outBitStream->Write(m_Rotation.w);
m_PositionInfoDirty = false;
if (!bIsInitialUpdate) m_PositionInfoDirty = false;
}
outBitStream->Write(m_EffectInfoDirty || bIsInitialUpdate);
@@ -328,7 +251,7 @@ void PhantomPhysicsComponent::Serialize(RakNet::BitStream* outBitStream, bool bI
outBitStream->Write(m_EffectType);
outBitStream->Write(m_DirectionalMultiplier);
// forgive me father for i have sinned
// distance info. Option.
outBitStream->Write0();
//outBitStream->Write(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;
//Process enter events
for (auto en : m_dpEntity->GetNewObjects()) {
m_Parent->OnCollisionPhantom(en->GetObjectID());
for (auto* en : m_dpEntity->GetNewObjects()) {
m_ParentEntity->OnCollisionPhantom(en->GetObjectID());
//If we are a respawn volume, inform the client:
if (m_IsRespawnVolume) {
auto entity = EntityManager::Instance()->GetEntity(en->GetObjectID());
if (!m_IsRespawnVolume) continue;
auto entity = EntityManager::Instance()->GetEntity(en->GetObjectID());
if (entity) {
GameMessages::SendPlayerReachedRespawnCheckpoint(entity, m_RespawnPos, m_RespawnRot);
entity->SetRespawnPos(m_RespawnPos);
entity->SetRespawnRot(m_RespawnRot);
}
}
if (!entity) continue;
GameMessages::SendPlayerReachedRespawnCheckpoint(entity, m_RespawnPos, m_RespawnRot);
entity->SetRespawnPosition(m_RespawnPos);
entity->SetRespawnRotation(m_RespawnRot);
}
//Process exit events
for (auto en : m_dpEntity->GetRemovedObjects()) {
m_Parent->OnCollisionLeavePhantom(en->GetObjectID());
for (auto* en : m_dpEntity->GetRemovedObjects()) {
m_ParentEntity->OnCollisionLeavePhantom(en->GetObjectID());
}
}
void PhantomPhysicsComponent::SetDirection(const NiPoint3& pos) {
if (m_Direction == pos) return;
m_Direction = pos;
m_Direction.x *= m_DirectionalMultiplier;
m_Direction.y *= m_DirectionalMultiplier;
@@ -391,53 +314,60 @@ void PhantomPhysicsComponent::SetDirection(const NiPoint3& pos) {
void PhantomPhysicsComponent::SpawnVertices() {
if (!m_dpEntity) return;
std::cout << m_Parent->GetObjectID() << std::endl;
auto box = static_cast<dpShapeBox*>(m_dpEntity->GetShape());
Game::logger->Log("PhantomPhysicsComponent", "objectId is %llu", m_ParentEntity->GetObjectID());
auto box = dynamic_cast<dpShapeBox*>(m_dpEntity->GetShape());
if (!box) return;
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;
info.lot = 33;
info.pos = vert;
info.spawner = nullptr;
info.spawnerID = m_Parent->GetObjectID();
info.spawnerID = m_ParentEntity->GetObjectID();
info.spawnerNodeID = 0;
Entity* newEntity = EntityManager::Instance()->CreateEntity(info, nullptr);
Entity* newEntity = EntityManager::Instance()->CreateEntity(info);
EntityManager::Instance()->ConstructEntity(newEntity);
}
}
void PhantomPhysicsComponent::SetDirectionalMultiplier(float mul) {
if (mul == m_DirectionalMultiplier) return;
m_DirectionalMultiplier = mul;
m_EffectInfoDirty = true;
}
void PhantomPhysicsComponent::SetEffectType(ePhysicsEffectType type) {
void PhantomPhysicsComponent::SetEffectType(const ePhysicsEffectType type) {
if (type == m_EffectType) return;
m_EffectType = type;
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_MinMax = 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_MinMax = true;
m_EffectInfoDirty = true;
}
void PhantomPhysicsComponent::SetPosition(const NiPoint3& pos) {
if (pos == m_Position) return;
m_Position = pos;
m_PositionInfoDirty = true;
if (m_dpEntity) m_dpEntity->SetPosition(pos);
}
void PhantomPhysicsComponent::SetRotation(const NiQuaternion& rot) {
if (rot == m_Rotation) return;
m_Rotation = rot;
m_PositionInfoDirty = true;
if (m_dpEntity) m_dpEntity->SetRotation(rot);
}

View File

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

View File

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

View File

@@ -10,14 +10,13 @@
*/
class PlayerForcedMovementComponent : public Component {
public:
static const eReplicaComponentType ComponentType = eReplicaComponentType::PLAYER_FORCED_MOVEMENT;
inline static const eReplicaComponentType ComponentType = eReplicaComponentType::PLAYER_FORCED_MOVEMENT;
/**
* Constructor for this component
* @param parent parent that contains this component
*/
PlayerForcedMovementComponent(Entity* parent);
~PlayerForcedMovementComponent() override;
PlayerForcedMovementComponent(Entity* parent) : Component(parent) {};
void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags);
@@ -26,28 +25,23 @@ public:
*
* @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
*
* @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 GetShowBillboard() { return m_ShowBillboard; }

View File

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

View File

@@ -4,7 +4,7 @@
#include "Entity.h"
#include "Component.h"
#include "Item.h"
#include "PossessorComponent.h"
#include "PossessionComponent.h"
#include "eAninmationFlags.h"
#include "eReplicaComponentType.h"
@@ -14,16 +14,13 @@
*/
class PossessableComponent : public Component {
public:
static const eReplicaComponentType ComponentType = eReplicaComponentType::POSSESSABLE;
inline static const eReplicaComponentType ComponentType = eReplicaComponentType::POSSESSABLE;
PossessableComponent(Entity* parentEntity, uint32_t componentId);
void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags);
void LoadTemplateData() override;
/**
* @brief mounts the Entity
*/
void Mount();
void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags);
/**
* @brief dismounts the Entity
@@ -34,7 +31,11 @@ public:
* Sets the possessor of this Entity
* @param value the ID of the possessor to set
*/
void SetPossessor(LWOOBJID value) { m_Possessor = value; m_DirtyPossessable = true; };
void SetPossessor(const LWOOBJID& value) {
if (m_Possessor == value) return;
m_Possessor = value;
m_DirtyPossessable = true;
}
/**
* Returns the possessor of this Entity
@@ -46,7 +47,11 @@ public:
* Sets the animation Flag of the possessable
* @param value the animation flag to set to
*/
void SetAnimationFlag(eAnimationFlags value) { m_AnimationFlag = value; m_DirtyPossessable = true; };
void SetAnimationFlag(eAnimationFlags value) {
if (m_AnimationFlag == value) return;
m_AnimationFlag = value;
m_DirtyPossessable = true;
}
/**
* Returns the possession type of this Entity
@@ -63,7 +68,10 @@ public:
/**
* 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
@@ -123,4 +131,6 @@ private:
*
*/
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.
*/
class PossessorComponent : public Component {
class PossessionComponent : public Component {
public:
static const eReplicaComponentType ComponentType = eReplicaComponentType::POSSESSOR;
inline static const eReplicaComponentType ComponentType = eReplicaComponentType::POSSESSION;
PossessorComponent(Entity* parent);
~PossessorComponent() override;
PossessionComponent(Entity* parent);
~PossessionComponent() override;
void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags);
@@ -44,7 +44,11 @@ public:
* Sets 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
@@ -68,7 +72,11 @@ public:
* Sets the possesible type that's currently used, merely used by the shooting gallery if it's 0
* @param value The possesible type to set
*/
void SetPossessableType(ePossessionType value) { m_PossessableType = value; m_DirtyPossesor = true; }
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;
/**
* @brief If the possessor is dirty
* @brief If the possession is dirty
*
*/
bool m_DirtyPossesor = false;
/**
* @brief If the possessor is busy dismounting
* @brief If the possession is busy dismounting
*
*/
bool m_IsDismounting = false;

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