mirror of
https://github.com/DarkflameUniverse/DarkflameServer.git
synced 2025-12-16 20:24:39 -06:00
Compare commits
122 Commits
v2.3.0
...
components
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6240eefc7e | ||
|
|
949a6db4bc | ||
|
|
49f3d757e5 | ||
|
|
c204ea4009 | ||
|
|
a37ec326b9 | ||
|
|
8678ed03c3 | ||
|
|
37bcc81694 | ||
|
|
4d88f63338 | ||
|
|
59831fc15d | ||
|
|
fe6b279ebb | ||
|
|
598d88b307 | ||
|
|
28637a206d | ||
|
|
87675aa62d | ||
|
|
83780affbd | ||
|
|
7ca9e59ef2 | ||
|
|
d2a7e147cc | ||
|
|
8a512e5c76 | ||
|
|
2528e02b98 | ||
|
|
1b7be5d7db | ||
|
|
790bd6c03b | ||
|
|
28fbe20b97 | ||
|
|
cf53e35af5 | ||
|
|
5301346ed5 | ||
|
|
001f6a75eb | ||
|
|
950a1fe423 | ||
| 197d1bcdee | |||
|
|
68a2a04e5d | ||
|
|
cfec9801a8 | ||
|
|
8ede5b87ca | ||
|
|
c22040c6eb | ||
| 6fb1786cf1 | |||
|
|
81404d9671 | ||
|
|
0544eeba1f | ||
|
|
c2fe7f6205 | ||
|
|
f55bec026d | ||
|
|
478b6ff6a9 | ||
|
|
7c1265911c | ||
|
|
c6063aac66 | ||
|
|
2abcb142ad | ||
|
|
d9a3bea6d5 | ||
|
|
fdcfbdee85 | ||
| fd182d222f | |||
| 68f90b7136 | |||
|
|
d29287f9d9 | ||
|
|
06acd23cb7 | ||
|
|
a5611e9c7f | ||
|
|
34cfd45d40 | ||
|
|
ec9278286b | ||
|
|
9121bf41c5 | ||
|
|
fee1025982 | ||
|
|
3f328a18be | ||
| 485a88dfd4 | |||
| c237c16c33 | |||
| d44b18e38f | |||
| d153d66e26 | |||
| 34d22d2d0d | |||
| bcbc5882dc | |||
| f27e0400e7 | |||
| c78760db59 | |||
| 907e045904 | |||
| dc96fcba85 | |||
|
|
e180430ede | ||
|
|
1bdec00a61 | ||
|
|
68a5cc1d89 | ||
|
|
be17d1a467 | ||
|
|
a992a28088 | ||
|
|
891648288a | ||
|
|
92006123b8 | ||
|
|
9a9b9aa813 | ||
|
|
ea975965ca | ||
|
|
35e5d8497b | ||
|
|
4d57eff946 | ||
|
|
2a8f40f8e8 | ||
|
|
355f4f4df8 | ||
|
|
451f7e76d7 | ||
|
|
83065dfb6f | ||
| fdd98ab825 | |||
|
|
31be1fbe4c | ||
|
|
d8e2e92428 | ||
| e389a619ad | |||
|
|
45bcc80a1b | ||
|
|
b2fee29ee0 | ||
|
|
326c495776 | ||
|
|
d224a86e93 | ||
|
|
f9ac0a9dec | ||
|
|
3f3810519d | ||
|
|
5be9146662 | ||
|
|
262b6ebb58 | ||
|
|
6f38a150d3 | ||
|
|
36c44ecc83 | ||
|
|
fc719cbb0a | ||
|
|
f78ea1bbc9 | ||
|
|
b43e5c2165 | ||
|
|
5f139c75e0 | ||
|
|
77dc6ff312 | ||
|
|
0b5df9f0b1 | ||
|
|
b91f84d884 | ||
|
|
cebe3c732a | ||
|
|
5714ac558e | ||
|
|
2a2799793d | ||
|
|
1c23f3c030 | ||
| a68fa69e7a | |||
|
|
ddc5f0e117 | ||
|
|
e3a716a9cf | ||
|
|
0b37dc1e4d | ||
|
|
f2d28cc0bd | ||
|
|
5da776a084 | ||
|
|
6f057204be | ||
|
|
f555ba8c25 | ||
|
|
e2dfa1809d | ||
|
|
62aa863997 | ||
|
|
ec00f5fd9d | ||
| d11e2db887 | |||
|
|
9e9e4dc087 | ||
|
|
ea9d0d8592 | ||
|
|
716a5fcf37 | ||
| 15988afb4c | |||
| 5e9a956bed | |||
| c2d5be0e5d | |||
| 2ae9a92b55 | |||
| 50978620d8 | |||
| c168f6c970 |
@@ -10,7 +10,7 @@ trim_trailing_whitespace=true
|
||||
end_of_line=lf
|
||||
insert_final_newline=true
|
||||
|
||||
[*.{c++,cc,cpp,cxx,h,h++,hh,hpp,hxx,inl,ipp,tlh,tli}]
|
||||
[*.{c++,cc,cpp,cxx,h,h++,hh,hpp,hxx,inl,ipp,tlh,tli,tcc,tpp}]
|
||||
|
||||
vc_generate_documentation_comments=doxygen_slash_star
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
/*!
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
14
dCommon/dEnums/ePhysicsBehaviorType.h
Normal file
14
dCommon/dEnums/ePhysicsBehaviorType.h
Normal file
@@ -0,0 +1,14 @@
|
||||
#ifndef __EPHYSICSBEHAVIORTYPE__H__
|
||||
#define __EPHYSICSBEHAVIORTYPE__H__
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
enum class ePhysicsBehaviorType : int32_t {
|
||||
INVALID = -1,
|
||||
GROUND,
|
||||
FLYING,
|
||||
STANDARD,
|
||||
DYNAMIC
|
||||
};
|
||||
|
||||
#endif //!__EPHYSICSBEHAVIORTYPE__H__
|
||||
@@ -11,14 +11,14 @@ enum class eReplicaComponentType : uint32_t {
|
||||
CHARACTER,
|
||||
SCRIPT,
|
||||
BOUNCER,
|
||||
BUFF, // buff is really 98, this is DESTROYABLE
|
||||
DESTROYABLE,
|
||||
GHOST,
|
||||
SKILL,
|
||||
SPAWNER,
|
||||
SPAWN,
|
||||
ITEM,
|
||||
REBUILD,
|
||||
REBUILD_START,
|
||||
REBUILD_ACTIVATOR,
|
||||
MODULAR_BUILD,
|
||||
BUILD_CONTROLLER,
|
||||
BUILD_ACTIVATOR,
|
||||
ICON_ONLY,
|
||||
VENDOR,
|
||||
INVENTORY,
|
||||
@@ -33,8 +33,8 @@ enum class eReplicaComponentType : uint32_t {
|
||||
PET,
|
||||
PLATFORM_BOUNDARY,
|
||||
MODULE,
|
||||
ARCADE,
|
||||
VEHICLE_PHYSICS, // Havok demo based
|
||||
JETPACKPAD,
|
||||
HAVOK_VEHICLE_PHYSICS,
|
||||
MOVEMENT_AI,
|
||||
EXHIBIT,
|
||||
OVERHEAD_ICON,
|
||||
@@ -46,23 +46,23 @@ enum class eReplicaComponentType : uint32_t {
|
||||
SCRIPTED_ACTIVITY,
|
||||
PHANTOM_PHYSICS,
|
||||
SPRINGPAD,
|
||||
MODEL,
|
||||
MODEL_BEHAVIOR,
|
||||
PROPERTY_ENTRANCE,
|
||||
FX,
|
||||
PROPERTY_MANAGEMENT,
|
||||
VEHICLE_PHYSICS_NEW, // internal physics based on havok
|
||||
VEHICLE_PHYSICS,
|
||||
PHYSICS_SYSTEM,
|
||||
QUICK_BUILD,
|
||||
SWITCH,
|
||||
ZONE_CONTROL, // Minigame
|
||||
CHANGLING,
|
||||
MINIGAME_CONTROL,
|
||||
CHANGLING_BUILD,
|
||||
CHOICE_BUILD,
|
||||
PACKAGE,
|
||||
SOUND_REPEATER,
|
||||
SOUND_AMBIENT_2D,
|
||||
SOUND_AMBIENT_3D,
|
||||
PRECONDITION,
|
||||
PLAYER_FLAG,
|
||||
FLAG,
|
||||
CUSTOM_BUILD_ASSEMBLY,
|
||||
BASE_COMBAT_AI,
|
||||
MODULE_ASSEMBLY,
|
||||
@@ -71,8 +71,8 @@ enum class eReplicaComponentType : uint32_t {
|
||||
GENERIC_ACTIVATOR,
|
||||
PROPERTY_VENDOR,
|
||||
HF_LIGHT_DIRECTION_GADGET,
|
||||
ROCKET_LAUNCH,
|
||||
ROCKET_LANDING,
|
||||
ROCKET_LAUNCHPAD_CONTROL,
|
||||
ROCKET_ANIMATION_CONTROL,
|
||||
TRIGGER,
|
||||
DROPPED_LOOT,
|
||||
RACING_CONTROL,
|
||||
@@ -84,7 +84,7 @@ enum class eReplicaComponentType : uint32_t {
|
||||
SOUND_TRIGGER,
|
||||
PROXIMITY_MONITOR,
|
||||
RACING_SOUND_TRIGGER,
|
||||
CHAT,
|
||||
CHAT_BUBBLE,
|
||||
FRIENDS_LIST,
|
||||
GUILD,
|
||||
LOCAL_SYSTEM,
|
||||
@@ -101,12 +101,12 @@ enum class eReplicaComponentType : uint32_t {
|
||||
TRADE,
|
||||
USER_CONTROL,
|
||||
IGNORE_LIST,
|
||||
ROCKET_LAUNCH_LUP,
|
||||
BUFF_REAL, // the real buff component, should just be name BUFF
|
||||
MULTI_ZONE_ENTRANCE,
|
||||
BUFF,
|
||||
INTERACTION_MANAGER,
|
||||
DONATION_VENDOR,
|
||||
COMBAT_MEDIATOR,
|
||||
COMMENDATION_VENDOR,
|
||||
ACHIEVEMENT_VENDOR,
|
||||
GATE_RUSH_CONTROL,
|
||||
RAIL_ACTIVATOR,
|
||||
ROLLER,
|
||||
@@ -114,14 +114,14 @@ enum class eReplicaComponentType : uint32_t {
|
||||
CRAFTING,
|
||||
POSSESSABLE,
|
||||
LEVEL_PROGRESSION,
|
||||
POSSESSOR,
|
||||
POSSESSION,
|
||||
MOUNT_CONTROL,
|
||||
UNKNOWN_112,
|
||||
PROPERTY_PLAQUE,
|
||||
BUILD_BORDER,
|
||||
UNKNOWN_115,
|
||||
CULLING_PLANE,
|
||||
DESTROYABLE = 1000 // Actually 7
|
||||
NUMBER_OF_COMPONENTS
|
||||
};
|
||||
|
||||
#endif //!__EREPLICACOMPONENTTYPE__H__
|
||||
|
||||
13
dCommon/dEnums/eUgcModerationStatus.h
Normal file
13
dCommon/dEnums/eUgcModerationStatus.h
Normal file
@@ -0,0 +1,13 @@
|
||||
|
||||
#ifndef __EUGCMODERATIONSTATUS__H__
|
||||
#define __EUGCMODERATIONSTATUS__H__
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
enum class eUgcModerationStatus : uint32_t {
|
||||
NoStatus,
|
||||
Approved,
|
||||
Rejected,
|
||||
};
|
||||
|
||||
#endif //!__EUGCMODERATIONSTATUS__H__
|
||||
@@ -90,4 +90,11 @@ int32_t CDComponentsRegistryTable::GetByIDAndType(uint32_t id, eReplicaComponent
|
||||
return defaultValue;
|
||||
#endif
|
||||
}
|
||||
|
||||
std::vector<std::pair<eReplicaComponentType, uint32_t>> CDComponentsRegistryTable::GetTemplateComponents(LOT templateId) {
|
||||
std::vector<std::pair<eReplicaComponentType, uint32_t>> components;
|
||||
for (int8_t i = 0; static_cast<eReplicaComponentType>(i) < eReplicaComponentType::NUMBER_OF_COMPONENTS; i++) {
|
||||
auto compId = GetByIDAndType(templateId, static_cast<eReplicaComponentType>(i), -1);
|
||||
if (compId != -1) components.push_back(std::make_pair(static_cast<eReplicaComponentType>(i), compId));
|
||||
}
|
||||
return components;
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
// Custom Classes
|
||||
#include "CDTable.h"
|
||||
#include "dCommonVars.h"
|
||||
|
||||
enum class eReplicaComponentType : uint32_t;
|
||||
struct CDComponentsRegistry {
|
||||
@@ -18,4 +19,5 @@ private:
|
||||
public:
|
||||
CDComponentsRegistryTable();
|
||||
int32_t GetByIDAndType(uint32_t id, eReplicaComponentType componentType, int32_t defaultValue = 0);
|
||||
std::vector<std::pair<eReplicaComponentType, uint32_t>> GetTemplateComponents(LOT templateId);
|
||||
};
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
#include "CDItemComponentTable.h"
|
||||
#include "GeneralUtils.h"
|
||||
|
||||
#include "eItemType.h"
|
||||
|
||||
CDItemComponent CDItemComponentTable::Default = {};
|
||||
|
||||
//! Constructor
|
||||
@@ -74,8 +76,8 @@ CDItemComponentTable::CDItemComponentTable(void) {
|
||||
#endif
|
||||
}
|
||||
|
||||
const CDItemComponent& CDItemComponentTable::GetItemComponentByID(unsigned int skillID) {
|
||||
const auto& it = this->entries.find(skillID);
|
||||
const CDItemComponent& CDItemComponentTable::GetItemComponentByID(unsigned int id) {
|
||||
const auto& it = this->entries.find(id);
|
||||
if (it != this->entries.end()) {
|
||||
return it->second;
|
||||
}
|
||||
@@ -83,11 +85,11 @@ const CDItemComponent& CDItemComponentTable::GetItemComponentByID(unsigned int s
|
||||
#ifndef CDCLIENT_CACHE_ALL
|
||||
std::stringstream query;
|
||||
|
||||
query << "SELECT * FROM ItemComponent WHERE id = " << std::to_string(skillID);
|
||||
query << "SELECT * FROM ItemComponent WHERE id = " << std::to_string(id);
|
||||
|
||||
auto tableData = CDClientDatabase::ExecuteQuery(query.str());
|
||||
if (tableData.eof()) {
|
||||
entries.insert(std::make_pair(skillID, Default));
|
||||
entries.insert(std::make_pair(id, Default));
|
||||
return Default;
|
||||
}
|
||||
|
||||
@@ -98,7 +100,7 @@ const CDItemComponent& CDItemComponentTable::GetItemComponentByID(unsigned int s
|
||||
entry.baseValue = tableData.getIntField("baseValue", -1);
|
||||
entry.isKitPiece = tableData.getIntField("isKitPiece", -1) == 1 ? true : false;
|
||||
entry.rarity = tableData.getIntField("rarity", 0);
|
||||
entry.itemType = tableData.getIntField("itemType", -1);
|
||||
entry.itemType = static_cast<eItemType>(tableData.getIntField("itemType", -1));
|
||||
entry.itemInfo = tableData.getInt64Field("itemInfo", -1);
|
||||
entry.inLootTable = tableData.getIntField("inLootTable", -1) == 1 ? true : false;
|
||||
entry.inVendor = tableData.getIntField("inVendor", -1) == 1 ? true : false;
|
||||
@@ -140,7 +142,7 @@ const CDItemComponent& CDItemComponentTable::GetItemComponentByID(unsigned int s
|
||||
tableData.nextRow();
|
||||
}
|
||||
|
||||
const auto& it2 = this->entries.find(skillID);
|
||||
const auto& it2 = this->entries.find(id);
|
||||
if (it2 != this->entries.end()) {
|
||||
return it2->second;
|
||||
}
|
||||
|
||||
@@ -4,13 +4,15 @@
|
||||
#include "CDTable.h"
|
||||
#include "dCommonVars.h"
|
||||
|
||||
enum class eItemType : int32_t;
|
||||
|
||||
struct CDItemComponent {
|
||||
unsigned int id; //!< The Component ID
|
||||
std::string equipLocation; //!< The equip location
|
||||
unsigned int baseValue; //!< The monetary base value of the item
|
||||
bool isKitPiece; //!< Whether or not the item belongs to a kit
|
||||
unsigned int rarity; //!< The rarity of the item
|
||||
unsigned int itemType; //!< The item type
|
||||
eItemType itemType; //!< The item type
|
||||
int64_t itemInfo; //!< The item info
|
||||
bool inLootTable; //!< Whether or not the item is in a loot table
|
||||
bool inVendor; //!< Whether or not the item is in a vendor inventory
|
||||
@@ -58,7 +60,7 @@ public:
|
||||
static std::map<LOT, uint32_t> ParseCraftingCurrencies(const CDItemComponent& itemComponent);
|
||||
|
||||
// Gets an entry by ID
|
||||
const CDItemComponent& GetItemComponentByID(unsigned int skillID);
|
||||
const CDItemComponent& GetItemComponentByID(unsigned int id);
|
||||
|
||||
static CDItemComponent Default;
|
||||
};
|
||||
|
||||
@@ -37,10 +37,7 @@ CDPhysicsComponentTable::~CDPhysicsComponentTable() {
|
||||
}
|
||||
|
||||
CDPhysicsComponent* CDPhysicsComponentTable::GetByID(unsigned int componentID) {
|
||||
for (auto e : m_entries) {
|
||||
if (e.first == componentID) return e.second;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
auto itr = m_entries.find(componentID);
|
||||
return itr != m_entries.end() ? itr->second : nullptr;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
set(DGAME_SOURCES "Character.cpp"
|
||||
"Entity.cpp"
|
||||
"EntityManager.cpp"
|
||||
"LeaderboardManager.cpp"
|
||||
"Player.cpp"
|
||||
|
||||
2056
dGame/Entity.cpp
2056
dGame/Entity.cpp
File diff suppressed because it is too large
Load Diff
503
dGame/Entity.h
503
dGame/Entity.h
@@ -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;
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
#include "eMissionTaskType.h"
|
||||
|
||||
#ifndef __ACHIEVEMENTCACHEKEY__H__
|
||||
#define __ACHIEVEMENTCACHEKEY__H__
|
||||
|
||||
#include "eMissionTaskType.h"
|
||||
#include "GeneralUtils.h"
|
||||
|
||||
class AchievementCacheKey {
|
||||
public:
|
||||
AchievementCacheKey() {
|
||||
|
||||
5
dGame/dComponents/AchievementVendorComponent.cpp
Normal file
5
dGame/dComponents/AchievementVendorComponent.cpp
Normal file
@@ -0,0 +1,5 @@
|
||||
#include "AchievementVendorComponent.h"
|
||||
|
||||
AchievementVendorComponent::AchievementVendorComponent(Entity* parent) : VendorComponent(parent) {
|
||||
|
||||
}
|
||||
16
dGame/dComponents/AchievementVendorComponent.h
Normal file
16
dGame/dComponents/AchievementVendorComponent.h
Normal file
@@ -0,0 +1,16 @@
|
||||
#ifndef __ACHIEVEMENTVENDORCOMPONENT__H__
|
||||
#define __ACHIEVEMENTVENDORCOMPONENT__H__
|
||||
|
||||
#include "VendorComponent.h"
|
||||
#include "eReplicaComponentType.h"
|
||||
|
||||
class Entity;
|
||||
|
||||
class AchievementVendorComponent final : public VendorComponent {
|
||||
public:
|
||||
inline static const eReplicaComponentType ComponentType = eReplicaComponentType::ACHIEVEMENT_VENDOR;
|
||||
AchievementVendorComponent(Entity* parent);
|
||||
};
|
||||
|
||||
|
||||
#endif //!__ACHIEVEMENTVENDORCOMPONENT__H__
|
||||
621
dGame/dComponents/ActivityComponent.cpp
Normal file
621
dGame/dComponents/ActivityComponent.cpp
Normal file
@@ -0,0 +1,621 @@
|
||||
#include "ActivityComponent.h"
|
||||
#include "GameMessages.h"
|
||||
#include "CDClientManager.h"
|
||||
#include "MissionComponent.h"
|
||||
#include "Character.h"
|
||||
#include "dZoneManager.h"
|
||||
#include "ZoneInstanceManager.h"
|
||||
#include "Game.h"
|
||||
#include "dLogger.h"
|
||||
#include <WorldPackets.h>
|
||||
#include "EntityManager.h"
|
||||
#include "ChatPackets.h"
|
||||
#include "Player.h"
|
||||
#include "PacketUtils.h"
|
||||
#include "dServer.h"
|
||||
#include "GeneralUtils.h"
|
||||
#include "dZoneManager.h"
|
||||
#include "dConfig.h"
|
||||
#include "InventoryComponent.h"
|
||||
#include "DestroyableComponent.h"
|
||||
#include "Loot.h"
|
||||
#include "eMissionTaskType.h"
|
||||
#include "eMatchUpdate.h"
|
||||
#include "eConnectionType.h"
|
||||
#include "eChatInternalMessageType.h"
|
||||
|
||||
#include "CDCurrencyTableTable.h"
|
||||
#include "CDActivityRewardsTable.h"
|
||||
#include "CDActivitiesTable.h"
|
||||
#include "LeaderboardManager.h"
|
||||
|
||||
ActivityComponent::ActivityComponent(Entity* parent, int32_t componentId) : Component(parent) {
|
||||
m_ActivityID = componentId;
|
||||
CDActivitiesTable* activitiesTable = CDClientManager::Instance().GetTable<CDActivitiesTable>();
|
||||
std::vector<CDActivities> activities = activitiesTable->Query([=](CDActivities entry) {return (entry.ActivityID == m_ActivityID); });
|
||||
|
||||
for (CDActivities activity : activities) {
|
||||
m_ActivityInfo = activity;
|
||||
if (static_cast<LeaderboardType>(activity.leaderboardType) == LeaderboardType::Racing && Game::config->GetValue("solo_racing") == "1") {
|
||||
m_ActivityInfo.minTeamSize = 1;
|
||||
m_ActivityInfo.minTeams = 1;
|
||||
}
|
||||
|
||||
const auto& transferOverride = parent->GetVar<std::u16string>(u"transferZoneID");
|
||||
if (!transferOverride.empty()) {
|
||||
m_ActivityInfo.instanceMapID = std::stoi(GeneralUtils::UTF16ToWTF8(transferOverride));
|
||||
|
||||
// TODO: LU devs made me do it (for some reason cannon cove instancer is marked to go to GF survival)
|
||||
// NOTE: 1301 is GF survival
|
||||
if (m_ActivityInfo.instanceMapID == 1301) {
|
||||
m_ActivityInfo.instanceMapID = 1302;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto* destroyableComponent = m_ParentEntity->GetComponent<DestroyableComponent>();
|
||||
|
||||
if (destroyableComponent) {
|
||||
// check for LMIs and set the loot LMIs
|
||||
CDActivityRewardsTable* activityRewardsTable = CDClientManager::Instance().GetTable<CDActivityRewardsTable>();
|
||||
std::vector<CDActivityRewards> activityRewards = activityRewardsTable->Query([=](CDActivityRewards entry) {return (entry.LootMatrixIndex == destroyableComponent->GetLootMatrixID()); });
|
||||
|
||||
uint32_t startingLMI = 0;
|
||||
|
||||
if (activityRewards.size() > 0) {
|
||||
startingLMI = activityRewards[0].LootMatrixIndex;
|
||||
}
|
||||
|
||||
if (startingLMI > 0) {
|
||||
// now time for bodge :)
|
||||
|
||||
std::vector<CDActivityRewards> objectTemplateActivities = activityRewardsTable->Query([=](CDActivityRewards entry) {return (activityRewards[0].objectTemplate == entry.objectTemplate); });
|
||||
for (const auto& item : objectTemplateActivities) {
|
||||
if (item.activityRating > 0 && item.activityRating < 5) {
|
||||
m_ActivityLootMatrices.insert({ item.activityRating, item.LootMatrixIndex });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ActivityComponent::~ActivityComponent()
|
||||
= default;
|
||||
|
||||
void ActivityComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags) const {
|
||||
outBitStream->Write(true);
|
||||
outBitStream->Write<uint32_t>(m_ActivityPlayers.size());
|
||||
|
||||
if (!m_ActivityPlayers.empty()) {
|
||||
for (const auto& activityPlayer : m_ActivityPlayers) {
|
||||
|
||||
outBitStream->Write<LWOOBJID>(activityPlayer->playerID);
|
||||
for (const auto& activityValue : activityPlayer->values) {
|
||||
outBitStream->Write<float_t>(activityValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ActivityComponent::ReloadConfig() {
|
||||
CDActivitiesTable* activitiesTable = CDClientManager::Instance().GetTable<CDActivitiesTable>();
|
||||
std::vector<CDActivities> activities = activitiesTable->Query([=](CDActivities entry) {return (entry.ActivityID == m_ActivityID); });
|
||||
for (auto activity : activities) {
|
||||
auto mapID = m_ActivityInfo.instanceMapID;
|
||||
if ((mapID == 1203 || mapID == 1261 || mapID == 1303 || mapID == 1403) && Game::config->GetValue("solo_racing") == "1") {
|
||||
m_ActivityInfo.minTeamSize = 1;
|
||||
m_ActivityInfo.minTeams = 1;
|
||||
} else {
|
||||
m_ActivityInfo.minTeamSize = activity.minTeamSize;
|
||||
m_ActivityInfo.minTeams = activity.minTeams;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ActivityComponent::HandleMessageBoxResponse(Entity* player, const std::string& id) {
|
||||
if (m_ActivityInfo.ActivityID == 103) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (id == "LobbyExit") {
|
||||
PlayerLeave(player->GetObjectID());
|
||||
} else if (id == "PlayButton") {
|
||||
PlayerJoin(player);
|
||||
}
|
||||
}
|
||||
|
||||
void ActivityComponent::PlayerJoin(Entity* player) {
|
||||
if (m_ActivityInfo.ActivityID == 103 || PlayerIsInQueue(player) || !IsValidActivity(player)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If we have a lobby, queue the player and allow others to join, otherwise spin up an instance on the spot
|
||||
if (HasLobby()) {
|
||||
PlayerJoinLobby(player);
|
||||
} else if (!IsPlayedBy(player)) {
|
||||
auto* instance = NewInstance();
|
||||
instance->AddParticipant(player);
|
||||
}
|
||||
|
||||
EntityManager::Instance()->SerializeEntity(m_ParentEntity);
|
||||
}
|
||||
|
||||
void ActivityComponent::PlayerJoinLobby(Entity* player) {
|
||||
if (!m_ParentEntity->HasComponent(eReplicaComponentType::QUICK_BUILD))
|
||||
GameMessages::SendMatchResponse(player, player->GetSystemAddress(), 0); // tell the client they joined a lobby
|
||||
LobbyPlayer* newLobbyPlayer = new LobbyPlayer();
|
||||
newLobbyPlayer->entityID = player->GetObjectID();
|
||||
Lobby* playerLobby = nullptr;
|
||||
|
||||
auto* character = player->GetCharacter();
|
||||
if (character != nullptr)
|
||||
character->SetLastNonInstanceZoneID(dZoneManager::Instance()->GetZone()->GetWorldID());
|
||||
|
||||
for (Lobby* lobby : m_Queue) {
|
||||
if (lobby->players.size() < m_ActivityInfo.maxTeamSize || m_ActivityInfo.maxTeamSize == 1 && lobby->players.size() < m_ActivityInfo.maxTeams) {
|
||||
// If an empty slot in an existing lobby is found
|
||||
lobby->players.push_back(newLobbyPlayer);
|
||||
playerLobby = lobby;
|
||||
|
||||
// Update the joining player on players already in the lobby, and update players already in the lobby on the joining player
|
||||
std::string matchUpdateJoined = "player=9:" + std::to_string(player->GetObjectID()) + "\nplayerName=0:" + player->GetCharacter()->GetName();
|
||||
for (LobbyPlayer* joinedPlayer : lobby->players) {
|
||||
auto* entity = joinedPlayer->GetEntity();
|
||||
|
||||
if (entity == nullptr) {
|
||||
continue;
|
||||
}
|
||||
|
||||
std::string matchUpdate = "player=9:" + std::to_string(entity->GetObjectID()) + "\nplayerName=0:" + entity->GetCharacter()->GetName();
|
||||
GameMessages::SendMatchUpdate(player, player->GetSystemAddress(), matchUpdate, eMatchUpdate::PLAYER_ADDED);
|
||||
PlayerReady(entity, joinedPlayer->ready);
|
||||
GameMessages::SendMatchUpdate(entity, entity->GetSystemAddress(), matchUpdateJoined, eMatchUpdate::PLAYER_ADDED);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!playerLobby) {
|
||||
// If all lobbies are full
|
||||
playerLobby = new Lobby();
|
||||
playerLobby->players.push_back(newLobbyPlayer);
|
||||
playerLobby->timer = m_ActivityInfo.waitTime / 1000;
|
||||
m_Queue.push_back(playerLobby);
|
||||
}
|
||||
|
||||
if (m_ActivityInfo.maxTeamSize != 1 && playerLobby->players.size() >= m_ActivityInfo.minTeamSize || m_ActivityInfo.maxTeamSize == 1 && playerLobby->players.size() >= m_ActivityInfo.minTeams) {
|
||||
// Update the joining player on the match timer
|
||||
std::string matchTimerUpdate = "time=3:" + std::to_string(playerLobby->timer);
|
||||
GameMessages::SendMatchUpdate(player, player->GetSystemAddress(), matchTimerUpdate, eMatchUpdate::PHASE_WAIT_READY);
|
||||
}
|
||||
}
|
||||
|
||||
void ActivityComponent::PlayerLeave(LWOOBJID playerID) {
|
||||
|
||||
// Removes the player from a lobby and notifies the others, not applicable for non-lobby instances
|
||||
for (Lobby* lobby : m_Queue) {
|
||||
for (int i = 0; i < lobby->players.size(); ++i) {
|
||||
if (lobby->players[i]->entityID == playerID) {
|
||||
std::string matchUpdateLeft = "player=9:" + std::to_string(playerID);
|
||||
for (LobbyPlayer* lobbyPlayer : lobby->players) {
|
||||
auto* entity = lobbyPlayer->GetEntity();
|
||||
if (entity == nullptr)
|
||||
continue;
|
||||
|
||||
GameMessages::SendMatchUpdate(entity, entity->GetSystemAddress(), matchUpdateLeft, eMatchUpdate::PLAYER_REMOVED);
|
||||
}
|
||||
|
||||
delete lobby->players[i];
|
||||
lobby->players[i] = nullptr;
|
||||
lobby->players.erase(lobby->players.begin() + i);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ActivityComponent::Update(float deltaTime) {
|
||||
std::vector<Lobby*> lobbiesToRemove{};
|
||||
// Ticks all the lobbies, not applicable for non-instance activities
|
||||
for (Lobby* lobby : m_Queue) {
|
||||
for (LobbyPlayer* player : lobby->players) {
|
||||
auto* entity = player->GetEntity();
|
||||
if (entity == nullptr) {
|
||||
PlayerLeave(player->entityID);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (lobby->players.empty()) {
|
||||
lobbiesToRemove.push_back(lobby);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Update the match time for all players
|
||||
if (m_ActivityInfo.maxTeamSize != 1 && lobby->players.size() >= m_ActivityInfo.minTeamSize
|
||||
|| m_ActivityInfo.maxTeamSize == 1 && lobby->players.size() >= m_ActivityInfo.minTeams) {
|
||||
if (lobby->timer == m_ActivityInfo.waitTime / 1000) {
|
||||
for (LobbyPlayer* joinedPlayer : lobby->players) {
|
||||
auto* entity = joinedPlayer->GetEntity();
|
||||
|
||||
if (entity == nullptr)
|
||||
continue;
|
||||
|
||||
std::string matchTimerUpdate = "time=3:" + std::to_string(lobby->timer);
|
||||
GameMessages::SendMatchUpdate(entity, entity->GetSystemAddress(), matchTimerUpdate, eMatchUpdate::PHASE_WAIT_READY);
|
||||
}
|
||||
}
|
||||
|
||||
lobby->timer -= deltaTime;
|
||||
}
|
||||
|
||||
bool lobbyReady = true;
|
||||
for (LobbyPlayer* player : lobby->players) {
|
||||
if (player->ready) continue;
|
||||
lobbyReady = false;
|
||||
}
|
||||
|
||||
// If everyone's ready, jump the timer
|
||||
if (lobbyReady && lobby->timer > m_ActivityInfo.startDelay / 1000) {
|
||||
lobby->timer = m_ActivityInfo.startDelay / 1000;
|
||||
|
||||
// Update players in lobby on switch to start delay
|
||||
std::string matchTimerUpdate = "time=3:" + std::to_string(lobby->timer);
|
||||
for (LobbyPlayer* player : lobby->players) {
|
||||
auto* entity = player->GetEntity();
|
||||
|
||||
if (entity == nullptr)
|
||||
continue;
|
||||
|
||||
GameMessages::SendMatchUpdate(entity, entity->GetSystemAddress(), matchTimerUpdate, eMatchUpdate::PHASE_WAIT_START);
|
||||
}
|
||||
}
|
||||
|
||||
// The timer has elapsed, start the instance
|
||||
if (lobby->timer <= 0.0f) {
|
||||
Game::logger->Log("ActivityComponent", "Setting up instance.");
|
||||
ActivityInstance* instance = NewInstance();
|
||||
LoadPlayersIntoInstance(instance, lobby->players);
|
||||
instance->StartZone();
|
||||
lobbiesToRemove.push_back(lobby);
|
||||
}
|
||||
}
|
||||
|
||||
while (!lobbiesToRemove.empty()) {
|
||||
RemoveLobby(lobbiesToRemove.front());
|
||||
lobbiesToRemove.erase(lobbiesToRemove.begin());
|
||||
}
|
||||
}
|
||||
|
||||
void ActivityComponent::RemoveLobby(Lobby* lobby) {
|
||||
for (int i = 0; i < m_Queue.size(); ++i) {
|
||||
if (m_Queue[i] == lobby) {
|
||||
m_Queue.erase(m_Queue.begin() + i);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool ActivityComponent::HasLobby() const {
|
||||
// If the player is not in the world he has to be, create a lobby for the transfer
|
||||
return m_ActivityInfo.instanceMapID != UINT_MAX && m_ActivityInfo.instanceMapID != Game::server->GetZoneID();
|
||||
}
|
||||
|
||||
bool ActivityComponent::IsValidActivity(Entity* player) {
|
||||
// Makes it so that scripted activities with an unimplemented map cannot be joined
|
||||
/*if (player->GetGMLevel() < eGameMasterLevel::DEVELOPER && (m_ActivityInfo.instanceMapID == 1302 || m_ActivityInfo.instanceMapID == 1301)) {
|
||||
if (m_ParentEntity->GetLOT() == 4860) {
|
||||
auto* missionComponent = player->GetComponent<MissionComponent>();
|
||||
missionComponent->CompleteMission(229);
|
||||
}
|
||||
|
||||
ChatPackets::SendSystemMessage(player->GetSystemAddress(), u"Sorry, this activity is not ready.");
|
||||
static_cast<Player*>(player)->SendToZone(dZoneManager::Instance()->GetZone()->GetWorldID()); // Gets them out of this stuck state
|
||||
|
||||
return false;
|
||||
}*/
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ActivityComponent::PlayerIsInQueue(Entity* player) {
|
||||
for (Lobby* lobby : m_Queue) {
|
||||
for (LobbyPlayer* lobbyPlayer : lobby->players) {
|
||||
if (player->GetObjectID() == lobbyPlayer->entityID) return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ActivityComponent::IsPlayedBy(Entity* player) const {
|
||||
for (const auto* instance : this->m_Instances) {
|
||||
for (const auto* instancePlayer : instance->GetParticipants()) {
|
||||
if (instancePlayer != nullptr && instancePlayer->GetObjectID() == player->GetObjectID())
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ActivityComponent::IsPlayedBy(LWOOBJID playerID) const {
|
||||
for (const auto* instance : this->m_Instances) {
|
||||
for (const auto* instancePlayer : instance->GetParticipants()) {
|
||||
if (instancePlayer != nullptr && instancePlayer->GetObjectID() == playerID)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ActivityComponent::TakeCost(Entity* player) const {
|
||||
if (m_ActivityInfo.optionalCostLOT <= 0 || m_ActivityInfo.optionalCostCount <= 0)
|
||||
return true;
|
||||
|
||||
auto* inventoryComponent = player->GetComponent<InventoryComponent>();
|
||||
if (inventoryComponent == nullptr)
|
||||
return false;
|
||||
|
||||
if (inventoryComponent->GetLotCount(m_ActivityInfo.optionalCostLOT) < m_ActivityInfo.optionalCostCount)
|
||||
return false;
|
||||
|
||||
inventoryComponent->RemoveItem(m_ActivityInfo.optionalCostLOT, m_ActivityInfo.optionalCostCount);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ActivityComponent::PlayerReady(Entity* player, bool bReady) {
|
||||
for (Lobby* lobby : m_Queue) {
|
||||
for (LobbyPlayer* lobbyPlayer : lobby->players) {
|
||||
if (lobbyPlayer->entityID == player->GetObjectID()) {
|
||||
|
||||
lobbyPlayer->ready = bReady;
|
||||
|
||||
// Update players in lobby on player being ready
|
||||
std::string matchReadyUpdate = "player=9:" + std::to_string(player->GetObjectID());
|
||||
eMatchUpdate readyStatus = eMatchUpdate::PLAYER_READY;
|
||||
if (!bReady) readyStatus = eMatchUpdate::PLAYER_NOT_READY;
|
||||
for (LobbyPlayer* otherPlayer : lobby->players) {
|
||||
auto* entity = otherPlayer->GetEntity();
|
||||
if (entity == nullptr)
|
||||
continue;
|
||||
|
||||
GameMessages::SendMatchUpdate(entity, entity->GetSystemAddress(), matchReadyUpdate, readyStatus);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ActivityInstance* ActivityComponent::NewInstance() {
|
||||
auto* instance = new ActivityInstance(m_ParentEntity, m_ActivityInfo);
|
||||
m_Instances.push_back(instance);
|
||||
return instance;
|
||||
}
|
||||
|
||||
void ActivityComponent::LoadPlayersIntoInstance(ActivityInstance* instance, const std::vector<LobbyPlayer*>& lobby) const {
|
||||
for (LobbyPlayer* player : lobby) {
|
||||
auto* entity = player->GetEntity();
|
||||
if (entity == nullptr || !TakeCost(entity)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
instance->AddParticipant(entity);
|
||||
}
|
||||
}
|
||||
|
||||
const std::vector<ActivityInstance*>& ActivityComponent::GetInstances() const {
|
||||
return m_Instances;
|
||||
}
|
||||
|
||||
ActivityInstance* ActivityComponent::GetInstance(const LWOOBJID playerID) {
|
||||
for (const auto* instance : GetInstances()) {
|
||||
for (const auto* participant : instance->GetParticipants()) {
|
||||
if (participant->GetObjectID() == playerID)
|
||||
return const_cast<ActivityInstance*>(instance);
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void ActivityComponent::ClearInstances() {
|
||||
for (ActivityInstance* instance : m_Instances) {
|
||||
delete instance;
|
||||
}
|
||||
m_Instances.clear();
|
||||
}
|
||||
|
||||
ActivityPlayer* ActivityComponent::GetActivityPlayerData(LWOOBJID playerID) {
|
||||
for (auto* activityData : m_ActivityPlayers) {
|
||||
if (activityData->playerID == playerID) {
|
||||
return activityData;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void ActivityComponent::RemoveActivityPlayerData(LWOOBJID playerID) {
|
||||
for (size_t i = 0; i < m_ActivityPlayers.size(); i++) {
|
||||
if (m_ActivityPlayers[i]->playerID == playerID) {
|
||||
delete m_ActivityPlayers[i];
|
||||
m_ActivityPlayers[i] = nullptr;
|
||||
|
||||
m_ActivityPlayers.erase(m_ActivityPlayers.begin() + i);
|
||||
EntityManager::Instance()->SerializeEntity(m_ParentEntity);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ActivityPlayer* ActivityComponent::AddActivityPlayerData(LWOOBJID playerID) {
|
||||
auto* data = GetActivityPlayerData(playerID);
|
||||
if (data != nullptr)
|
||||
return data;
|
||||
|
||||
m_ActivityPlayers.push_back(new ActivityPlayer{ playerID, {} });
|
||||
EntityManager::Instance()->SerializeEntity(m_ParentEntity);
|
||||
|
||||
return GetActivityPlayerData(playerID);
|
||||
}
|
||||
|
||||
float_t ActivityComponent::GetActivityValue(LWOOBJID playerID, uint32_t index) {
|
||||
auto value = -1.0f;
|
||||
|
||||
auto* data = GetActivityPlayerData(playerID);
|
||||
if (data != nullptr) {
|
||||
value = data->values[std::min(index, (uint32_t)9)];
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
void ActivityComponent::SetActivityValue(LWOOBJID playerID, uint32_t index, float_t value) {
|
||||
auto* data = AddActivityPlayerData(playerID);
|
||||
if (data != nullptr) {
|
||||
data->values[std::min(index, (uint32_t)9)] = value;
|
||||
}
|
||||
|
||||
EntityManager::Instance()->SerializeEntity(m_ParentEntity);
|
||||
}
|
||||
|
||||
void ActivityComponent::PlayerRemove(LWOOBJID playerID) {
|
||||
for (auto* instance : GetInstances()) {
|
||||
auto participants = instance->GetParticipants();
|
||||
for (const auto* participant : participants) {
|
||||
if (participant != nullptr && participant->GetObjectID() == playerID) {
|
||||
instance->RemoveParticipant(participant);
|
||||
RemoveActivityPlayerData(playerID);
|
||||
|
||||
// If the instance is empty after the delete of the participant, delete the instance too
|
||||
if (instance->GetParticipants().empty()) {
|
||||
m_Instances.erase(std::find(m_Instances.begin(), m_Instances.end(), instance));
|
||||
delete instance;
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ActivityInstance::StartZone() {
|
||||
if (m_Participants.empty())
|
||||
return;
|
||||
|
||||
const auto& participants = GetParticipants();
|
||||
if (participants.empty())
|
||||
return;
|
||||
|
||||
auto* leader = participants[0];
|
||||
LWOZONEID zoneId = LWOZONEID(m_ActivityInfo.instanceMapID, 0, leader->GetCharacter()->GetPropertyCloneID());
|
||||
|
||||
// only make a team if we have more than one participant
|
||||
if (participants.size() > 1) {
|
||||
CBITSTREAM;
|
||||
PacketUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::CREATE_TEAM);
|
||||
|
||||
bitStream.Write(leader->GetObjectID());
|
||||
bitStream.Write(m_Participants.size());
|
||||
|
||||
for (const auto& participant : m_Participants) {
|
||||
bitStream.Write(participant);
|
||||
}
|
||||
|
||||
bitStream.Write(zoneId);
|
||||
|
||||
Game::chatServer->Send(&bitStream, SYSTEM_PRIORITY, RELIABLE, 0, Game::chatSysAddr, false);
|
||||
}
|
||||
|
||||
const auto cloneId = GeneralUtils::GenerateRandomNumber<uint32_t>(1, UINT32_MAX);
|
||||
for (Entity* player : participants) {
|
||||
const auto objid = player->GetObjectID();
|
||||
ZoneInstanceManager::Instance()->RequestZoneTransfer(Game::server, m_ActivityInfo.instanceMapID, cloneId, false, [objid](bool mythranShift, uint32_t zoneID, uint32_t zoneInstance, uint32_t zoneClone, std::string serverIP, uint16_t serverPort) {
|
||||
|
||||
auto* player = EntityManager::Instance()->GetEntity(objid);
|
||||
if (player == nullptr)
|
||||
return;
|
||||
|
||||
Game::logger->Log("UserManager", "Transferring %s to Zone %i (Instance %i | Clone %i | Mythran Shift: %s) with IP %s and Port %i", player->GetCharacter()->GetName().c_str(), zoneID, zoneInstance, zoneClone, mythranShift == true ? "true" : "false", serverIP.c_str(), serverPort);
|
||||
if (player->GetCharacter()) {
|
||||
player->GetCharacter()->SetZoneID(zoneID);
|
||||
player->GetCharacter()->SetZoneInstance(zoneInstance);
|
||||
player->GetCharacter()->SetZoneClone(zoneClone);
|
||||
}
|
||||
|
||||
WorldPackets::SendTransferToWorld(player->GetSystemAddress(), serverIP, serverPort, mythranShift);
|
||||
return;
|
||||
});
|
||||
}
|
||||
|
||||
m_NextZoneCloneID++;
|
||||
}
|
||||
|
||||
void ActivityInstance::RewardParticipant(Entity* participant) {
|
||||
auto* missionComponent = participant->GetComponent<MissionComponent>();
|
||||
if (missionComponent) {
|
||||
missionComponent->Progress(eMissionTaskType::ACTIVITY, m_ActivityInfo.ActivityID);
|
||||
}
|
||||
|
||||
// First, get the activity data
|
||||
auto* activityRewardsTable = CDClientManager::Instance().GetTable<CDActivityRewardsTable>();
|
||||
std::vector<CDActivityRewards> activityRewards = activityRewardsTable->Query([=](CDActivityRewards entry) { return (entry.objectTemplate == m_ActivityInfo.ActivityID); });
|
||||
|
||||
if (!activityRewards.empty()) {
|
||||
uint32_t minCoins = 0;
|
||||
uint32_t maxCoins = 0;
|
||||
|
||||
auto* currencyTableTable = CDClientManager::Instance().GetTable<CDCurrencyTableTable>();
|
||||
std::vector<CDCurrencyTable> currencyTable = currencyTableTable->Query([=](CDCurrencyTable entry) { return (entry.currencyIndex == activityRewards[0].CurrencyIndex && entry.npcminlevel == 1); });
|
||||
|
||||
if (!currencyTable.empty()) {
|
||||
minCoins = currencyTable[0].minvalue;
|
||||
maxCoins = currencyTable[0].maxvalue;
|
||||
}
|
||||
|
||||
LootGenerator::Instance().DropLoot(participant, m_ParentEntity, activityRewards[0].LootMatrixIndex, minCoins, maxCoins);
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<Entity*> ActivityInstance::GetParticipants() const {
|
||||
std::vector<Entity*> entities;
|
||||
entities.reserve(m_Participants.size());
|
||||
|
||||
for (const auto& id : m_Participants) {
|
||||
auto* entity = EntityManager::Instance()->GetEntity(id);
|
||||
if (entity != nullptr)
|
||||
entities.push_back(entity);
|
||||
}
|
||||
|
||||
return entities;
|
||||
}
|
||||
|
||||
void ActivityInstance::AddParticipant(Entity* participant) {
|
||||
const auto id = participant->GetObjectID();
|
||||
if (std::count(m_Participants.begin(), m_Participants.end(), id))
|
||||
return;
|
||||
|
||||
m_Participants.push_back(id);
|
||||
}
|
||||
|
||||
void ActivityInstance::RemoveParticipant(const Entity* participant) {
|
||||
const auto loadedParticipant = std::find(m_Participants.begin(), m_Participants.end(), participant->GetObjectID());
|
||||
if (loadedParticipant != m_Participants.end()) {
|
||||
m_Participants.erase(loadedParticipant);
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t ActivityInstance::GetScore() const {
|
||||
return score;
|
||||
}
|
||||
|
||||
void ActivityInstance::SetScore(uint32_t score) {
|
||||
this->score = score;
|
||||
}
|
||||
|
||||
Entity* LobbyPlayer::GetEntity() const {
|
||||
return EntityManager::Instance()->GetEntity(entityID);
|
||||
}
|
||||
379
dGame/dComponents/ActivityComponent.h
Normal file
379
dGame/dComponents/ActivityComponent.h
Normal file
@@ -0,0 +1,379 @@
|
||||
/*
|
||||
* Darkflame Universe
|
||||
* Copyright 2018
|
||||
*/
|
||||
|
||||
#include "CDClientManager.h"
|
||||
|
||||
#ifndef ACTIVITYCOMPONENT_H
|
||||
#define ACTIVITYCOMPONENT_H
|
||||
|
||||
#include "BitStream.h"
|
||||
#include "Entity.h"
|
||||
#include "Component.h"
|
||||
#include "eReplicaComponentType.h"
|
||||
|
||||
#include "CDActivitiesTable.h"
|
||||
|
||||
/**
|
||||
* Represents an instance of an activity, having participants and score
|
||||
*/
|
||||
class ActivityInstance {
|
||||
public:
|
||||
ActivityInstance(Entity* parent, CDActivities activityInfo) { m_ParentEntity = parent; m_ActivityInfo = activityInfo; };
|
||||
//~ActivityInstance();
|
||||
|
||||
/**
|
||||
* Adds an entity to this activity
|
||||
* @param participant the entity to add
|
||||
*/
|
||||
void AddParticipant(Entity* participant);
|
||||
|
||||
/**
|
||||
* Removes all the participants from this activity
|
||||
*/
|
||||
void ClearParticipants() { m_Participants.clear(); };
|
||||
|
||||
/**
|
||||
* Starts the instance world for this activity and sends all participants there
|
||||
*/
|
||||
void StartZone();
|
||||
|
||||
/**
|
||||
* Gives the rewards for completing this activity to some participant
|
||||
* @param participant the participant to give rewards to
|
||||
*/
|
||||
void RewardParticipant(Entity* participant);
|
||||
|
||||
/**
|
||||
* Removes a participant from this activity
|
||||
* @param participant the participant to remove
|
||||
*/
|
||||
void RemoveParticipant(const Entity* participant);
|
||||
|
||||
/**
|
||||
* Returns all the participants of this activity
|
||||
* @return all the participants of this activity
|
||||
*/
|
||||
std::vector<Entity*> GetParticipants() const;
|
||||
|
||||
/**
|
||||
* Currently unused
|
||||
*/
|
||||
uint32_t GetScore() const;
|
||||
|
||||
/**
|
||||
* Currently unused
|
||||
*/
|
||||
void SetScore(uint32_t score);
|
||||
private:
|
||||
|
||||
/**
|
||||
* Currently unused
|
||||
*/
|
||||
uint32_t score = 0;
|
||||
|
||||
/**
|
||||
* The instance ID of this activity
|
||||
*/
|
||||
uint32_t m_NextZoneCloneID = 0;
|
||||
|
||||
/**
|
||||
* The database information for this activity
|
||||
*/
|
||||
CDActivities m_ActivityInfo;
|
||||
|
||||
/**
|
||||
* The entity that owns this activity (the entity that has the ActivityComponent)
|
||||
*/
|
||||
Entity* m_ParentEntity;
|
||||
|
||||
/**
|
||||
* All the participants of this activity
|
||||
*/
|
||||
std::vector<LWOOBJID> m_Participants;
|
||||
};
|
||||
|
||||
/**
|
||||
* Represents an entity in a lobby
|
||||
*/
|
||||
struct LobbyPlayer {
|
||||
|
||||
/**
|
||||
* The ID of the entity that is in the lobby
|
||||
*/
|
||||
LWOOBJID entityID;
|
||||
|
||||
/**
|
||||
* Whether or not the entity is ready
|
||||
*/
|
||||
bool ready = false;
|
||||
|
||||
/**
|
||||
* Returns the entity that is in the lobby
|
||||
* @return the entity that is in the lobby
|
||||
*/
|
||||
Entity* GetEntity() const;
|
||||
};
|
||||
|
||||
/**
|
||||
* Represents a lobby of players with a timer until it should start the activity
|
||||
*/
|
||||
struct Lobby {
|
||||
|
||||
/**
|
||||
* The lobby of players
|
||||
*/
|
||||
std::vector<LobbyPlayer*> players;
|
||||
|
||||
/**
|
||||
* The timer that determines when the activity should start
|
||||
*/
|
||||
float timer;
|
||||
};
|
||||
|
||||
/**
|
||||
* Represents the score for the player in an activity, one index might represent score, another one time, etc.
|
||||
*/
|
||||
struct ActivityPlayer {
|
||||
|
||||
/**
|
||||
* The entity that the score is tracked for
|
||||
*/
|
||||
LWOOBJID playerID;
|
||||
|
||||
/**
|
||||
* The list of score for this entity
|
||||
*/
|
||||
float values[10];
|
||||
};
|
||||
|
||||
/**
|
||||
* Welcome to the absolute behemoth that is the activity component. I have now clue how this was managed in
|
||||
* live but I figure somewhat similarly and it's terrible. In a nutshell, this components handles any activity that
|
||||
* can be done in the game from quick builds to boss fights to races. On top of that, this component handles instancing
|
||||
* and lobbying.
|
||||
*/
|
||||
class ActivityComponent : public Component {
|
||||
public:
|
||||
ActivityComponent(Entity* parent, int32_t componentId);
|
||||
~ActivityComponent() override;
|
||||
|
||||
void Update(float deltaTime) override;
|
||||
void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags) const;
|
||||
|
||||
/**
|
||||
* Makes some entity join the minigame, if it's a lobbied one, the entity will be placed in the lobby
|
||||
* @param player the entity to join the game
|
||||
*/
|
||||
void PlayerJoin(Entity* player);
|
||||
|
||||
/**
|
||||
* Makes an entity join the lobby for this minigame, if it exists
|
||||
* @param player the entity to join
|
||||
*/
|
||||
void PlayerJoinLobby(Entity* player);
|
||||
|
||||
/**
|
||||
* Makes the player leave the lobby
|
||||
* @param playerID the entity to leave the lobby
|
||||
*/
|
||||
void PlayerLeave(LWOOBJID playerID);
|
||||
|
||||
/**
|
||||
* Removes the entity from the minigame (and its score)
|
||||
* @param playerID the entity to remove from the minigame
|
||||
*/
|
||||
void PlayerRemove(LWOOBJID playerID);
|
||||
|
||||
/**
|
||||
* Adds all the players to an instance of some activity
|
||||
* @param instance the instance to load the players into
|
||||
* @param lobby the players to load into the instance
|
||||
*/
|
||||
void LoadPlayersIntoInstance(ActivityInstance* instance, const std::vector<LobbyPlayer*>& lobby) const;
|
||||
|
||||
/**
|
||||
* Removes a lobby from the activity manager
|
||||
* @param lobby the lobby to remove
|
||||
*/
|
||||
void RemoveLobby(Lobby* lobby);
|
||||
|
||||
/**
|
||||
* Marks a player as (un)ready in a lobby
|
||||
* @param player the entity to mark
|
||||
* @param bReady true if the entity is ready, false otherwise
|
||||
*/
|
||||
void PlayerReady(Entity* player, bool bReady);
|
||||
|
||||
/**
|
||||
* Returns the ID of this activity
|
||||
* @return the ID of this activity
|
||||
*/
|
||||
int GetActivityID() { return m_ActivityInfo.ActivityID; }
|
||||
|
||||
/**
|
||||
* Returns if this activity has a lobby, e.g. if it needs to instance players to some other map
|
||||
* @return true if this activity has a lobby, false otherwise
|
||||
*/
|
||||
bool HasLobby() const;
|
||||
|
||||
/**
|
||||
* Checks if a player is currently waiting in a lobby
|
||||
* @param player the entity to check for
|
||||
* @return true if the entity is waiting in a lobby, false otherwise
|
||||
*/
|
||||
bool PlayerIsInQueue(Entity* player);
|
||||
|
||||
/**
|
||||
* Checks if an entity is currently playing this activity
|
||||
* @param player the entity to check
|
||||
* @return true if the entity is playing this lobby, false otherwise
|
||||
*/
|
||||
bool IsPlayedBy(Entity* player) const;
|
||||
|
||||
/**
|
||||
* Checks if an entity is currently playing this activity
|
||||
* @param playerID the entity to check
|
||||
* @return true if the entity is playing this lobby, false otherwise
|
||||
*/
|
||||
bool IsPlayedBy(LWOOBJID playerID) const;
|
||||
|
||||
/**
|
||||
* Legacy: used to check for unimplemented maps, gladly, this now just returns true :)
|
||||
*/
|
||||
bool IsValidActivity(Entity* player);
|
||||
|
||||
/**
|
||||
* Removes the cost of the activity (e.g. green imaginate) for the entity that plays this activity
|
||||
* @param player the entity to take cost for
|
||||
* @return true if the cost was successfully deducted, false otherwise
|
||||
*/
|
||||
bool TakeCost(Entity* player) const;
|
||||
|
||||
/**
|
||||
* Handles any response from a player clicking on a lobby / instance menu
|
||||
* @param player the entity that clicked
|
||||
* @param id the message that was passed
|
||||
*/
|
||||
void HandleMessageBoxResponse(Entity* player, const std::string& id);
|
||||
|
||||
/**
|
||||
* Creates a new instance for this activity
|
||||
* @return a new instance for this activity
|
||||
*/
|
||||
ActivityInstance* NewInstance();
|
||||
|
||||
/**
|
||||
* Returns all the currently active instances of this activity
|
||||
* @return all the currently active instances of this activity
|
||||
*/
|
||||
const std::vector<ActivityInstance*>& GetInstances() const;
|
||||
|
||||
/**
|
||||
* Returns the instance that some entity is currently playing in
|
||||
* @param playerID the entity to check for
|
||||
* @return if any, the instance that the entity is currently in
|
||||
*/
|
||||
ActivityInstance* GetInstance(const LWOOBJID playerID);
|
||||
|
||||
/**
|
||||
* @brief Reloads the config settings for this component
|
||||
*
|
||||
*/
|
||||
void ReloadConfig();
|
||||
|
||||
/**
|
||||
* Removes all the instances
|
||||
*/
|
||||
void ClearInstances();
|
||||
|
||||
/**
|
||||
* Returns all the score for the players that are currently playing this activity
|
||||
* @return
|
||||
*/
|
||||
std::vector<ActivityPlayer*> GetActivityPlayers() { return m_ActivityPlayers; };
|
||||
|
||||
/**
|
||||
* Returns activity data for a specific entity (e.g. score and such).
|
||||
* @param playerID the entity to get data for
|
||||
* @return the activity data (score) for the passed player in this activity, if it exists
|
||||
*/
|
||||
ActivityPlayer* GetActivityPlayerData(LWOOBJID playerID);
|
||||
|
||||
/**
|
||||
* Sets some score value for an entity
|
||||
* @param playerID the entity to set score for
|
||||
* @param index the score index to set
|
||||
* @param value the value to set in for that index
|
||||
*/
|
||||
void SetActivityValue(LWOOBJID playerID, uint32_t index, float_t value);
|
||||
|
||||
/**
|
||||
* Returns activity score for the passed parameters
|
||||
* @param playerID the entity to get score for
|
||||
* @param index the index to get score for
|
||||
* @return activity score for the passed parameters
|
||||
*/
|
||||
float_t GetActivityValue(LWOOBJID playerID, uint32_t index);
|
||||
|
||||
/**
|
||||
* Removes activity score tracking for some entity
|
||||
* @param playerID the entity to remove score for
|
||||
*/
|
||||
void RemoveActivityPlayerData(LWOOBJID playerID);
|
||||
|
||||
/**
|
||||
* Adds activity score tracking for some entity
|
||||
* @param playerID the entity to add the activity score for
|
||||
* @return the created entry
|
||||
*/
|
||||
ActivityPlayer* AddActivityPlayerData(LWOOBJID playerID);
|
||||
|
||||
/**
|
||||
* Sets the mapID that this activity points to
|
||||
* @param mapID the map ID to set
|
||||
*/
|
||||
void SetInstanceMapID(uint32_t mapID) { m_ActivityInfo.instanceMapID = mapID; };
|
||||
|
||||
/**
|
||||
* Returns the LMI that this activity points to for a team size
|
||||
* @param teamSize the team size to get the LMI for
|
||||
* @return the LMI that this activity points to for a team size
|
||||
*/
|
||||
uint32_t GetLootMatrixForTeamSize(uint32_t teamSize) { return m_ActivityLootMatrices[teamSize]; }
|
||||
private:
|
||||
|
||||
/**
|
||||
* The database information for this activity
|
||||
*/
|
||||
CDActivities m_ActivityInfo;
|
||||
|
||||
/**
|
||||
* All the active instances of this activity
|
||||
*/
|
||||
std::vector<ActivityInstance*> m_Instances;
|
||||
|
||||
/**
|
||||
* The current lobbies for this activity
|
||||
*/
|
||||
std::vector<Lobby*> m_Queue;
|
||||
|
||||
/**
|
||||
* All the activity score for the players in this activity
|
||||
*/
|
||||
std::vector<ActivityPlayer*> m_ActivityPlayers;
|
||||
|
||||
/**
|
||||
* LMIs for team sizes
|
||||
*/
|
||||
std::unordered_map<uint32_t, uint32_t> m_ActivityLootMatrices;
|
||||
|
||||
/**
|
||||
* The activity id
|
||||
*
|
||||
*/
|
||||
int32_t m_ActivityID;
|
||||
};
|
||||
|
||||
#endif // ACTIVITYCOMPONENT_H
|
||||
@@ -1,6 +1,10 @@
|
||||
#include "BaseCombatAIComponent.h"
|
||||
#include <BitStream.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
|
||||
#include "BitStream.h"
|
||||
#include "Entity.h"
|
||||
#include "EntityManager.h"
|
||||
#include "ControllablePhysicsComponent.h"
|
||||
@@ -15,32 +19,32 @@
|
||||
#include "CDClientManager.h"
|
||||
#include "DestroyableComponent.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
|
||||
#include "SkillComponent.h"
|
||||
#include "RebuildComponent.h"
|
||||
#include "QuickBuildComponent.h"
|
||||
#include "DestroyableComponent.h"
|
||||
#include "Metrics.hpp"
|
||||
#include "CDComponentsRegistryTable.h"
|
||||
#include "CDPhysicsComponentTable.h"
|
||||
|
||||
BaseCombatAIComponent::BaseCombatAIComponent(Entity* parent, const uint32_t id): Component(parent) {
|
||||
BaseCombatAIComponent::BaseCombatAIComponent(Entity* parent, const uint32_t componentId) : Component(parent) {
|
||||
m_Target = LWOOBJID_EMPTY;
|
||||
SetAiState(AiState::spawn);
|
||||
m_ComponentId = componentId;
|
||||
SetAiState(AiState::Spawn);
|
||||
m_Timer = 1.0f;
|
||||
m_StartPosition = parent->GetPosition();
|
||||
m_MovementAI = nullptr;
|
||||
m_Disabled = false;
|
||||
m_SkillEntries = {};
|
||||
m_MovementAI = nullptr;
|
||||
m_SoftTimer = 5.0f;
|
||||
m_dpEntity = nullptr;
|
||||
m_dpEntityEnemy = nullptr;
|
||||
}
|
||||
|
||||
void BaseCombatAIComponent::LoadTemplateData() {
|
||||
//Grab the aggro information from BaseCombatAI:
|
||||
auto componentQuery = CDClientDatabase::CreatePreppedStmt(
|
||||
"SELECT aggroRadius, tetherSpeed, pursuitSpeed, softTetherRadius, hardTetherRadius FROM BaseCombatAIComponent WHERE id = ?;");
|
||||
componentQuery.bind(1, (int)id);
|
||||
componentQuery.bind(1, m_ComponentId);
|
||||
|
||||
auto componentResult = componentQuery.execQuery();
|
||||
|
||||
@@ -63,21 +67,12 @@ BaseCombatAIComponent::BaseCombatAIComponent(Entity* parent, const uint32_t id):
|
||||
|
||||
componentResult.finalize();
|
||||
|
||||
// Get aggro and tether radius from settings and use this if it is present. Only overwrite the
|
||||
// radii if it is greater than the one in the database.
|
||||
if (m_Parent) {
|
||||
auto aggroRadius = m_Parent->GetVar<float>(u"aggroRadius");
|
||||
m_AggroRadius = aggroRadius != 0 ? aggroRadius : m_AggroRadius;
|
||||
auto tetherRadius = m_Parent->GetVar<float>(u"tetherRadius");
|
||||
m_HardTetherRadius = tetherRadius != 0 ? tetherRadius : m_HardTetherRadius;
|
||||
}
|
||||
|
||||
/*
|
||||
* Find skills
|
||||
*/
|
||||
auto skillQuery = CDClientDatabase::CreatePreppedStmt(
|
||||
"SELECT skillID, cooldown, behaviorID FROM SkillBehavior WHERE skillID IN (SELECT skillID FROM ObjectSkills WHERE objectTemplate = ?);");
|
||||
skillQuery.bind(1, (int)parent->GetLOT());
|
||||
skillQuery.bind(1, m_ParentEntity->GetLOT());
|
||||
|
||||
auto result = skillQuery.execQuery();
|
||||
|
||||
@@ -90,44 +85,51 @@ BaseCombatAIComponent::BaseCombatAIComponent(Entity* parent, const uint32_t id):
|
||||
|
||||
auto* behavior = Behavior::CreateBehavior(behaviorId);
|
||||
|
||||
std::stringstream behaviorQuery;
|
||||
|
||||
AiSkillEntry entry = { skillId, 0, abilityCooldown, behavior };
|
||||
|
||||
m_SkillEntries.push_back(entry);
|
||||
m_SkillEntries.push_back(AiSkillEntry(skillId, 0, abilityCooldown, behavior));
|
||||
|
||||
result.nextRow();
|
||||
}
|
||||
}
|
||||
|
||||
void BaseCombatAIComponent::LoadConfigData() {
|
||||
// Get aggro and tether radius from settings and use this if it is present. Only overwrite the
|
||||
// radii if it is greater than the one in the database.
|
||||
if (m_ParentEntity) {
|
||||
auto aggroRadius = m_ParentEntity->GetVar<float>(u"aggroRadius");
|
||||
m_AggroRadius = aggroRadius != 0 ? aggroRadius : m_AggroRadius;
|
||||
auto tetherRadius = m_ParentEntity->GetVar<float>(u"tetherRadius");
|
||||
m_HardTetherRadius = tetherRadius != 0 ? tetherRadius : m_HardTetherRadius;
|
||||
}
|
||||
}
|
||||
|
||||
void BaseCombatAIComponent::Startup() {
|
||||
Stun(1.0f);
|
||||
|
||||
/*
|
||||
* Add physics
|
||||
*/
|
||||
|
||||
// Add physics
|
||||
int32_t collisionGroup = (COLLISION_GROUP_DYNAMIC | COLLISION_GROUP_ENEMY);
|
||||
|
||||
CDComponentsRegistryTable* componentRegistryTable = CDClientManager::Instance().GetTable<CDComponentsRegistryTable>();
|
||||
auto componentID = componentRegistryTable->GetByIDAndType(parent->GetLOT(), eReplicaComponentType::CONTROLLABLE_PHYSICS);
|
||||
auto* componentRegistryTable = CDClientManager::Instance().GetTable<CDComponentsRegistryTable>();
|
||||
if (!componentRegistryTable) return;
|
||||
|
||||
CDPhysicsComponentTable* physicsComponentTable = CDClientManager::Instance().GetTable<CDPhysicsComponentTable>();
|
||||
auto componentID = componentRegistryTable->GetByIDAndType(m_ParentEntity->GetLOT(), eReplicaComponentType::CONTROLLABLE_PHYSICS);
|
||||
|
||||
if (physicsComponentTable != nullptr) {
|
||||
auto* info = physicsComponentTable->GetByID(componentID);
|
||||
if (info != nullptr) {
|
||||
collisionGroup = info->bStatic ? COLLISION_GROUP_NEUTRAL : info->collisionGroup;
|
||||
}
|
||||
}
|
||||
auto* physicsComponentTable = CDClientManager::Instance().GetTable<CDPhysicsComponentTable>();
|
||||
|
||||
if (!physicsComponentTable) return;
|
||||
|
||||
auto* info = physicsComponentTable->GetByID(componentID);
|
||||
if (info) collisionGroup = info->bStatic ? COLLISION_GROUP_NEUTRAL : info->collisionGroup;
|
||||
|
||||
// Why are these new'd here and then deleted by the dpworld??
|
||||
//Create a phantom physics volume so we can detect when we're aggro'd.
|
||||
m_dpEntity = new dpEntity(m_Parent->GetObjectID(), m_AggroRadius);
|
||||
m_dpEntityEnemy = new dpEntity(m_Parent->GetObjectID(), m_AggroRadius, false);
|
||||
m_dpEntity = new dpEntity(m_ParentEntity->GetObjectID(), m_AggroRadius);
|
||||
m_dpEntityEnemy = new dpEntity(m_ParentEntity->GetObjectID(), m_AggroRadius, false);
|
||||
|
||||
m_dpEntity->SetCollisionGroup(collisionGroup);
|
||||
m_dpEntityEnemy->SetCollisionGroup(collisionGroup);
|
||||
|
||||
m_dpEntity->SetPosition(m_Parent->GetPosition());
|
||||
m_dpEntityEnemy->SetPosition(m_Parent->GetPosition());
|
||||
m_dpEntity->SetPosition(m_ParentEntity->GetPosition());
|
||||
m_dpEntityEnemy->SetPosition(m_ParentEntity->GetPosition());
|
||||
|
||||
dpWorld::Instance().AddEntity(m_dpEntity);
|
||||
dpWorld::Instance().AddEntity(m_dpEntityEnemy);
|
||||
@@ -135,28 +137,29 @@ BaseCombatAIComponent::BaseCombatAIComponent(Entity* parent, const uint32_t id):
|
||||
}
|
||||
|
||||
BaseCombatAIComponent::~BaseCombatAIComponent() {
|
||||
if (m_dpEntity)
|
||||
dpWorld::Instance().RemoveEntity(m_dpEntity);
|
||||
if (m_dpEntity) dpWorld::Instance().RemoveEntity(m_dpEntity);
|
||||
|
||||
if (m_dpEntityEnemy)
|
||||
dpWorld::Instance().RemoveEntity(m_dpEntityEnemy);
|
||||
if (m_dpEntityEnemy) dpWorld::Instance().RemoveEntity(m_dpEntityEnemy);
|
||||
m_MovementAI = nullptr;
|
||||
m_dpEntity = nullptr;
|
||||
m_dpEntityEnemy = nullptr;
|
||||
}
|
||||
|
||||
void BaseCombatAIComponent::Update(const float deltaTime) {
|
||||
//First, we need to process physics:
|
||||
if (!m_dpEntity) return;
|
||||
|
||||
m_dpEntity->SetPosition(m_Parent->GetPosition()); //make sure our position is synced with our dpEntity
|
||||
m_dpEntityEnemy->SetPosition(m_Parent->GetPosition());
|
||||
m_dpEntity->SetPosition(m_ParentEntity->GetPosition()); //make sure our position is synced with our dpEntity
|
||||
m_dpEntityEnemy->SetPosition(m_ParentEntity->GetPosition());
|
||||
|
||||
//Process enter events
|
||||
for (auto en : m_dpEntity->GetNewObjects()) {
|
||||
m_Parent->OnCollisionPhantom(en->GetObjectID());
|
||||
m_ParentEntity->OnCollisionPhantom(en->GetObjectID());
|
||||
}
|
||||
|
||||
//Process exit events
|
||||
for (auto en : m_dpEntity->GetRemovedObjects()) {
|
||||
m_Parent->OnCollisionLeavePhantom(en->GetObjectID());
|
||||
m_ParentEntity->OnCollisionLeavePhantom(en->GetObjectID());
|
||||
}
|
||||
|
||||
// Check if we should stop the tether effect
|
||||
@@ -165,39 +168,35 @@ void BaseCombatAIComponent::Update(const float deltaTime) {
|
||||
const auto& info = m_MovementAI->GetInfo();
|
||||
if (m_Target != LWOOBJID_EMPTY || (NiPoint3::DistanceSquared(
|
||||
m_StartPosition,
|
||||
m_Parent->GetPosition()) < 20 * 20 && m_TetherTime <= 0)
|
||||
m_ParentEntity->GetPosition()) < 20 * 20 && m_TetherTime <= 0)
|
||||
) {
|
||||
GameMessages::SendStopFXEffect(m_Parent, true, "tether");
|
||||
GameMessages::SendStopFXEffect(m_ParentEntity, true, "tether");
|
||||
m_TetherEffectActive = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (m_SoftTimer <= 0.0f) {
|
||||
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() {
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include "Component.h"
|
||||
#include "eReplicaComponentType.h"
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
|
||||
@@ -19,20 +20,25 @@ class Entity;
|
||||
/**
|
||||
* The current state of the AI
|
||||
*/
|
||||
enum class AiState : int {
|
||||
idle = 0, // Doing nothing
|
||||
aggro, // Waiting for an enemy to cross / running back to spawn
|
||||
tether, // Chasing an enemy
|
||||
spawn, // Spawning into the world
|
||||
dead // Killed
|
||||
enum class AiState : int32_t {
|
||||
Idle = 0, // Doing nothing
|
||||
Aggro, // Waiting for an enemy to cross / running back to spawn
|
||||
Tether, // Chasing an enemy
|
||||
Spawn, // Spawning into the world
|
||||
Dead // Killed
|
||||
};
|
||||
|
||||
/**
|
||||
* Represents a skill that can be cast by this enemy, including its cooldowns, which determines how often the skill
|
||||
* may be cast.
|
||||
*/
|
||||
struct AiSkillEntry
|
||||
{
|
||||
struct AiSkillEntry {
|
||||
AiSkillEntry(uint32_t skillId, float cooldown, float abilityCooldown, Behavior* behavior) {
|
||||
this->skillId = skillId;
|
||||
this->cooldown = cooldown;
|
||||
this->abilityCooldown = abilityCooldown;
|
||||
this->behavior = behavior;
|
||||
}
|
||||
uint32_t skillId;
|
||||
|
||||
float cooldown;
|
||||
@@ -45,13 +51,16 @@ struct AiSkillEntry
|
||||
/**
|
||||
* Handles the AI of entities, making them wander, tether and attack their enemies
|
||||
*/
|
||||
class BaseCombatAIComponent : public Component {
|
||||
class BaseCombatAIComponent final : public Component {
|
||||
public:
|
||||
static const eReplicaComponentType ComponentType = eReplicaComponentType::BASE_COMBAT_AI;
|
||||
inline static const eReplicaComponentType ComponentType = eReplicaComponentType::BASE_COMBAT_AI;
|
||||
|
||||
BaseCombatAIComponent(Entity* parentEntity, uint32_t id);
|
||||
~BaseCombatAIComponent() override;
|
||||
|
||||
void LoadTemplateData() override;
|
||||
void LoadConfigData() override;
|
||||
void Startup() override;
|
||||
void Update(float deltaTime) override;
|
||||
void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags);
|
||||
|
||||
@@ -146,37 +155,37 @@ public:
|
||||
* Gets whether or not the entity is currently stunned
|
||||
* @return whether the entity is currently stunned
|
||||
*/
|
||||
bool GetStunned() const;
|
||||
bool GetStunned() const { return m_Stunned; }
|
||||
|
||||
/**
|
||||
* (un)stuns the entity, determining whether it'll be able to attack other entities
|
||||
* @param value whether the enemy is stunned
|
||||
*/
|
||||
void SetStunned(bool value);
|
||||
void SetStunned(bool value) { m_Stunned = value; }
|
||||
|
||||
/**
|
||||
* Gets if this entity may be stunned
|
||||
* @return if this entity may be stunned
|
||||
*/
|
||||
bool GetStunImmune() const;
|
||||
bool GetStunImmune() const { return m_StunImmune; }
|
||||
|
||||
/**
|
||||
* Set the stun immune value, determining if the entity may be stunned
|
||||
* @param value
|
||||
*/
|
||||
void SetStunImmune(bool value);
|
||||
void SetStunImmune(bool value) { m_StunImmune = value; }
|
||||
|
||||
/**
|
||||
* Gets the current speed at which an entity runs when tethering
|
||||
* @return the current speed at which an entity runs when tethering
|
||||
*/
|
||||
float GetTetherSpeed() const;
|
||||
float GetTetherSpeed() const { return m_TetherSpeed; }
|
||||
|
||||
/**
|
||||
* Sets the speed at which an entity will tether
|
||||
* @param value the new tether speed
|
||||
*/
|
||||
void SetTetherSpeed(float value);
|
||||
void SetTetherSpeed(float value) { m_TetherSpeed = value; }
|
||||
|
||||
/**
|
||||
* Stuns the entity for a certain amount of time, will not work if the entity is stun immune
|
||||
@@ -188,13 +197,13 @@ public:
|
||||
* Gets the radius that will cause this entity to get aggro'd, causing a target chase
|
||||
* @return the aggro radius of the entity
|
||||
*/
|
||||
float GetAggroRadius() const;
|
||||
float GetAggroRadius() const { return m_AggroRadius; }
|
||||
|
||||
/**
|
||||
* Sets the aggro radius, causing the entity to start chasing enemies in this range
|
||||
* @param value the aggro radius to set
|
||||
*/
|
||||
void SetAggroRadius(float value);
|
||||
void SetAggroRadius(float value) { m_AggroRadius = value; }
|
||||
|
||||
/**
|
||||
* Makes the entity look at a certain point in space
|
||||
@@ -206,13 +215,13 @@ public:
|
||||
* (dis)ables the AI, causing it to stop/start attacking enemies
|
||||
* @param value
|
||||
*/
|
||||
void SetDisabled(bool value);
|
||||
void SetDisabled(bool value) { m_Disabled = value; }
|
||||
|
||||
/**
|
||||
* Gets the current state of the AI, whether or not it's looking for enemies to attack
|
||||
* @return
|
||||
*/
|
||||
bool GetDistabled() const;
|
||||
bool GetDistabled() const { return m_Disabled; }
|
||||
|
||||
/**
|
||||
* Turns the entity asleep, stopping updates to its physics volumes
|
||||
@@ -386,7 +395,9 @@ private:
|
||||
* Whether the current entity is a mech enemy, needed as mechs tether radius works differently
|
||||
* @return whether this entity is a mech
|
||||
*/
|
||||
bool IsMech();
|
||||
bool IsMech() const { return m_ParentEntity->GetLOT() == 6253; };
|
||||
|
||||
int32_t m_ComponentId;
|
||||
};
|
||||
|
||||
#endif // BASECOMBATAICOMPONENT_H
|
||||
|
||||
@@ -6,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();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -10,41 +10,27 @@
|
||||
/**
|
||||
* Attached to bouncer entities, allowing other entities to bounce off of it
|
||||
*/
|
||||
class BouncerComponent : public Component {
|
||||
class BouncerComponent final : public Component {
|
||||
public:
|
||||
static const eReplicaComponentType ComponentType = eReplicaComponentType::BOUNCER;
|
||||
inline static const eReplicaComponentType ComponentType = eReplicaComponentType::BOUNCER;
|
||||
|
||||
BouncerComponent(Entity* parentEntity);
|
||||
~BouncerComponent() override;
|
||||
|
||||
void Startup() override;
|
||||
void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags);
|
||||
|
||||
Entity* GetParentEntity() const;
|
||||
|
||||
/**
|
||||
* Sets whether or not this bouncer needs to be activated by a pet
|
||||
* @param value whether or not this bouncer needs to be activated by a pet
|
||||
*/
|
||||
void SetPetEnabled(bool value);
|
||||
void SetBounceOnCollision(bool value);
|
||||
|
||||
/**
|
||||
* Sets whether or not this bouncer is currently being activated by a pet, allowing entities to bounce off of it,
|
||||
* also displays FX accordingly.
|
||||
* @param value whether or not this bouncer is activated by a pet
|
||||
*/
|
||||
void SetPetBouncerEnabled(bool value);
|
||||
|
||||
/**
|
||||
* Gets whether this bouncer should be enabled using pets
|
||||
* @return whether this bouncer should be enabled using pets
|
||||
*/
|
||||
bool GetPetEnabled() const;
|
||||
|
||||
/**
|
||||
* Gets whether this bouncer is currently activated by a pet
|
||||
* @return whether this bouncer is currently activated by a pet
|
||||
*/
|
||||
bool GetPetBouncerEnabled() const;
|
||||
void SetBouncerEnabled(bool value);
|
||||
|
||||
/**
|
||||
* Finds the switch used to activate this bouncer if its pet-enabled and stores this components' state there
|
||||
@@ -52,20 +38,12 @@ public:
|
||||
void LookupPetSwitch();
|
||||
|
||||
private:
|
||||
/**
|
||||
* Whether this bouncer needs to be activated by a pet
|
||||
*/
|
||||
bool m_PetEnabled;
|
||||
|
||||
/**
|
||||
* Whether this bouncer is currently being activated by a pet
|
||||
*/
|
||||
bool m_PetBouncerEnabled;
|
||||
bool m_BounceOnCollision;
|
||||
|
||||
/**
|
||||
* Whether the pet switch for this bouncer has been located
|
||||
*/
|
||||
bool m_PetSwitchLoaded;
|
||||
bool m_DirtyBounceInfo;
|
||||
};
|
||||
|
||||
#endif // BOUNCERCOMPONENT_H
|
||||
|
||||
@@ -14,20 +14,12 @@
|
||||
|
||||
std::unordered_map<int32_t, std::vector<BuffParameter>> BuffComponent::m_Cache{};
|
||||
|
||||
BuffComponent::BuffComponent(Entity* parent) : Component(parent) {
|
||||
}
|
||||
|
||||
BuffComponent::~BuffComponent() {
|
||||
}
|
||||
|
||||
void BuffComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags) {
|
||||
if (!bIsInitialUpdate) return;
|
||||
if (m_Buffs.empty()) {
|
||||
outBitStream->Write0();
|
||||
} else {
|
||||
outBitStream->Write1();
|
||||
outBitStream->Write<uint32_t>(m_Buffs.size());
|
||||
|
||||
outBitStream->Write(!m_Buffs.empty());
|
||||
if (!m_Buffs.empty()) {
|
||||
outBitStream->Write<uint32_t>(m_Buffs.size());
|
||||
for (const auto& buff : m_Buffs) {
|
||||
outBitStream->Write<uint32_t>(buff.first);
|
||||
outBitStream->Write0();
|
||||
@@ -47,7 +39,7 @@ void BuffComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUp
|
||||
}
|
||||
}
|
||||
|
||||
outBitStream->Write0();
|
||||
outBitStream->Write0(); // immunities
|
||||
}
|
||||
|
||||
void BuffComponent::Update(float deltaTime) {
|
||||
@@ -55,30 +47,28 @@ void BuffComponent::Update(float deltaTime) {
|
||||
* Loop through all buffs and apply deltaTime to ther time.
|
||||
* If they have expired, remove the buff and break.
|
||||
*/
|
||||
for (auto& buff : m_Buffs) {
|
||||
for (auto& [buffId, buffInfo] : m_Buffs) {
|
||||
// For damage buffs
|
||||
if (buff.second.tick != 0.0f && buff.second.stacks > 0) {
|
||||
buff.second.tickTime -= deltaTime;
|
||||
if (buffInfo.tick != 0.0f && buffInfo.stacks > 0) {
|
||||
buffInfo.tickTime -= deltaTime;
|
||||
|
||||
if (buff.second.tickTime <= 0.0f) {
|
||||
buff.second.tickTime = buff.second.tick;
|
||||
buff.second.stacks--;
|
||||
if (buffInfo.tickTime <= 0.0f) {
|
||||
buffInfo.tickTime = buffInfo.tick;
|
||||
buffInfo.stacks--;
|
||||
|
||||
SkillComponent::HandleUnmanaged(buff.second.behaviorID, m_Parent->GetObjectID(), buff.second.source);
|
||||
SkillComponent::HandleUnmanaged(buffInfo.behaviorID, m_ParentEntity->GetObjectID(), buffInfo.source);
|
||||
}
|
||||
}
|
||||
|
||||
// These are indefinate buffs, don't update them.
|
||||
if (buff.second.time == 0.0f) {
|
||||
continue;
|
||||
}
|
||||
if (buffInfo.time == 0.0f) continue;
|
||||
|
||||
buff.second.time -= deltaTime;
|
||||
buffInfo.time -= deltaTime;
|
||||
|
||||
if (buff.second.time <= 0.0f) {
|
||||
RemoveBuff(buff.first);
|
||||
if (buffInfo.time <= 0.0f) {
|
||||
RemoveBuff(buffId);
|
||||
|
||||
break;
|
||||
break; // Break because we modified or may modify the map.
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -87,11 +77,9 @@ void BuffComponent::ApplyBuff(const int32_t id, const float duration, const LWOO
|
||||
bool cancelOnDamaged, bool cancelOnDeath, bool cancelOnLogout, bool cancelOnRemoveBuff,
|
||||
bool cancelOnUi, bool cancelOnUnequip, bool cancelOnZone) {
|
||||
// Prevent buffs from stacking.
|
||||
if (HasBuff(id)) {
|
||||
return;
|
||||
}
|
||||
if (HasBuff(id)) return;
|
||||
|
||||
GameMessages::SendAddBuff(const_cast<LWOOBJID&>(m_Parent->GetObjectID()), source, (uint32_t)id,
|
||||
GameMessages::SendAddBuff(m_ParentEntity->GetObjectID(), source, (uint32_t)id,
|
||||
(uint32_t)duration * 1000, addImmunity, cancelOnDamaged, cancelOnDeath,
|
||||
cancelOnLogout, cancelOnRemoveBuff, cancelOnUi, cancelOnUnequip, cancelOnZone);
|
||||
|
||||
@@ -100,14 +88,14 @@ void BuffComponent::ApplyBuff(const int32_t id, const float duration, const LWOO
|
||||
int32_t behaviorID = 0;
|
||||
|
||||
const auto& parameters = GetBuffParameters(id);
|
||||
auto* skillBehaviorTable = CDClientManager::Instance().GetTable<CDSkillBehaviorTable>();
|
||||
for (const auto& parameter : parameters) {
|
||||
if (parameter.name == "overtime") {
|
||||
auto* behaviorTemplateTable = CDClientManager::Instance().GetTable<CDSkillBehaviorTable>();
|
||||
|
||||
behaviorID = behaviorTemplateTable->GetSkillByID(parameter.values[0]).behaviorID;
|
||||
stacks = static_cast<int32_t>(parameter.values[1]);
|
||||
tick = parameter.values[2];
|
||||
const auto unknown2 = parameter.values[3]; // Always 0
|
||||
behaviorID = skillBehaviorTable->GetSkillByID(parameter.values.skillId).behaviorID;
|
||||
stacks = static_cast<int32_t>(parameter.values.stacks);
|
||||
tick = parameter.values.tick;
|
||||
const auto unknown2 = parameter.values.unknown2; // Always 0
|
||||
}
|
||||
}
|
||||
|
||||
@@ -122,34 +110,28 @@ void BuffComponent::ApplyBuff(const int32_t id, const float duration, const LWOO
|
||||
buff.source = source;
|
||||
buff.behaviorID = behaviorID;
|
||||
|
||||
m_Buffs.emplace(id, buff);
|
||||
m_Buffs.insert_or_assign(id, buff);
|
||||
}
|
||||
|
||||
void BuffComponent::RemoveBuff(int32_t id, bool fromUnEquip, bool removeImmunity) {
|
||||
const auto& iter = m_Buffs.find(id);
|
||||
|
||||
if (iter == m_Buffs.end()) {
|
||||
return;
|
||||
}
|
||||
if (iter == m_Buffs.end()) return;
|
||||
|
||||
GameMessages::SendRemoveBuff(m_Parent, fromUnEquip, removeImmunity, id);
|
||||
GameMessages::SendRemoveBuff(m_ParentEntity, fromUnEquip, removeImmunity, id);
|
||||
|
||||
m_Buffs.erase(iter);
|
||||
|
||||
RemoveBuffEffect(id);
|
||||
}
|
||||
|
||||
bool BuffComponent::HasBuff(int32_t id) {
|
||||
return m_Buffs.find(id) != m_Buffs.end();
|
||||
}
|
||||
|
||||
void BuffComponent::ApplyBuffEffect(int32_t id) {
|
||||
const auto& parameters = GetBuffParameters(id);
|
||||
for (const auto& parameter : parameters) {
|
||||
if (parameter.name == "max_health") {
|
||||
const auto maxHealth = parameter.value;
|
||||
|
||||
auto* destroyable = this->GetParent()->GetComponent<DestroyableComponent>();
|
||||
auto* destroyable = this->GetParentEntity()->GetComponent<DestroyableComponent>();
|
||||
|
||||
if (destroyable == nullptr) return;
|
||||
|
||||
@@ -157,7 +139,7 @@ void BuffComponent::ApplyBuffEffect(int32_t id) {
|
||||
} else if (parameter.name == "max_armor") {
|
||||
const auto maxArmor = parameter.value;
|
||||
|
||||
auto* destroyable = this->GetParent()->GetComponent<DestroyableComponent>();
|
||||
auto* destroyable = this->GetParentEntity()->GetComponent<DestroyableComponent>();
|
||||
|
||||
if (destroyable == nullptr) return;
|
||||
|
||||
@@ -165,13 +147,13 @@ void BuffComponent::ApplyBuffEffect(int32_t id) {
|
||||
} else if (parameter.name == "max_imagination") {
|
||||
const auto maxImagination = parameter.value;
|
||||
|
||||
auto* destroyable = this->GetParent()->GetComponent<DestroyableComponent>();
|
||||
auto* destroyable = this->GetParentEntity()->GetComponent<DestroyableComponent>();
|
||||
|
||||
if (destroyable == nullptr) return;
|
||||
|
||||
destroyable->SetMaxImagination(destroyable->GetMaxImagination() + maxImagination);
|
||||
} else if (parameter.name == "speed") {
|
||||
auto* controllablePhysicsComponent = this->GetParent()->GetComponent<ControllablePhysicsComponent>();
|
||||
auto* controllablePhysicsComponent = this->GetParentEntity()->GetComponent<ControllablePhysicsComponent>();
|
||||
if (!controllablePhysicsComponent) return;
|
||||
const auto speed = parameter.value;
|
||||
controllablePhysicsComponent->AddSpeedboost(speed);
|
||||
@@ -185,29 +167,29 @@ void BuffComponent::RemoveBuffEffect(int32_t id) {
|
||||
if (parameter.name == "max_health") {
|
||||
const auto maxHealth = parameter.value;
|
||||
|
||||
auto* destroyable = this->GetParent()->GetComponent<DestroyableComponent>();
|
||||
auto* destroyable = this->GetParentEntity()->GetComponent<DestroyableComponent>();
|
||||
|
||||
if (destroyable == nullptr) return;
|
||||
if (!destroyable) return;
|
||||
|
||||
destroyable->SetMaxHealth(destroyable->GetMaxHealth() - maxHealth);
|
||||
} else if (parameter.name == "max_armor") {
|
||||
const auto maxArmor = parameter.value;
|
||||
|
||||
auto* destroyable = this->GetParent()->GetComponent<DestroyableComponent>();
|
||||
auto* destroyable = this->GetParentEntity()->GetComponent<DestroyableComponent>();
|
||||
|
||||
if (destroyable == nullptr) return;
|
||||
if (!destroyable) return;
|
||||
|
||||
destroyable->SetMaxArmor(destroyable->GetMaxArmor() - maxArmor);
|
||||
} else if (parameter.name == "max_imagination") {
|
||||
const auto maxImagination = parameter.value;
|
||||
|
||||
auto* destroyable = this->GetParent()->GetComponent<DestroyableComponent>();
|
||||
auto* destroyable = this->GetParentEntity()->GetComponent<DestroyableComponent>();
|
||||
|
||||
if (destroyable == nullptr) return;
|
||||
if (!destroyable) return;
|
||||
|
||||
destroyable->SetMaxImagination(destroyable->GetMaxImagination() - maxImagination);
|
||||
} else if (parameter.name == "speed") {
|
||||
auto* controllablePhysicsComponent = this->GetParent()->GetComponent<ControllablePhysicsComponent>();
|
||||
auto* controllablePhysicsComponent = this->GetParentEntity()->GetComponent<ControllablePhysicsComponent>();
|
||||
if (!controllablePhysicsComponent) return;
|
||||
const auto speed = parameter.value;
|
||||
controllablePhysicsComponent->RemoveSpeedboost(speed);
|
||||
@@ -216,27 +198,19 @@ void BuffComponent::RemoveBuffEffect(int32_t id) {
|
||||
}
|
||||
|
||||
void BuffComponent::RemoveAllBuffs() {
|
||||
for (const auto& buff : m_Buffs) {
|
||||
RemoveBuffEffect(buff.first);
|
||||
for (const auto& [buffId, buffInfo] : m_Buffs) {
|
||||
RemoveBuffEffect(buffId);
|
||||
}
|
||||
|
||||
m_Buffs.clear();
|
||||
}
|
||||
|
||||
void BuffComponent::Reset() {
|
||||
RemoveAllBuffs();
|
||||
}
|
||||
|
||||
void BuffComponent::ReApplyBuffs() {
|
||||
for (const auto& buff : m_Buffs) {
|
||||
ApplyBuffEffect(buff.first);
|
||||
for (const auto& [buffId, buffInfo] : m_Buffs) {
|
||||
ApplyBuffEffect(buffId);
|
||||
}
|
||||
}
|
||||
|
||||
Entity* BuffComponent::GetParent() const {
|
||||
return m_Parent;
|
||||
}
|
||||
|
||||
void BuffComponent::LoadFromXml(tinyxml2::XMLDocument* doc) {
|
||||
// Load buffs
|
||||
auto* dest = doc->FirstChildElement("obj")->FirstChildElement("dest");
|
||||
@@ -245,9 +219,7 @@ void BuffComponent::LoadFromXml(tinyxml2::XMLDocument* doc) {
|
||||
auto* buffElement = dest->FirstChildElement("buff");
|
||||
|
||||
// Old character, no buffs to load
|
||||
if (buffElement == nullptr) {
|
||||
return;
|
||||
}
|
||||
if (buffElement) return;
|
||||
|
||||
auto* buffEntry = buffElement->FirstChildElement("b");
|
||||
|
||||
@@ -305,12 +277,9 @@ void BuffComponent::UpdateXml(tinyxml2::XMLDocument* doc) {
|
||||
const std::vector<BuffParameter>& BuffComponent::GetBuffParameters(int32_t buffId) {
|
||||
const auto& pair = m_Cache.find(buffId);
|
||||
|
||||
if (pair != m_Cache.end()) {
|
||||
return pair->second;
|
||||
}
|
||||
if (pair != m_Cache.end()) return pair->second;
|
||||
|
||||
auto query = CDClientDatabase::CreatePreppedStmt(
|
||||
"SELECT * FROM BuffParameters WHERE BuffID = ?;");
|
||||
auto query = CDClientDatabase::CreatePreppedStmt("SELECT * FROM BuffParameters WHERE BuffID = ?;");
|
||||
query.bind(1, (int)buffId);
|
||||
|
||||
auto result = query.execQuery();
|
||||
@@ -325,17 +294,15 @@ const std::vector<BuffParameter>& BuffComponent::GetBuffParameters(int32_t buffI
|
||||
param.value = result.getFloatField(2);
|
||||
|
||||
if (!result.fieldIsNull(3)) {
|
||||
std::istringstream stream(result.getStringField(3));
|
||||
std::string token;
|
||||
|
||||
while (std::getline(stream, token, ',')) {
|
||||
try {
|
||||
const auto value = std::stof(token);
|
||||
|
||||
param.values.push_back(value);
|
||||
} catch (std::invalid_argument& exception) {
|
||||
Game::logger->Log("BuffComponent", "Failed to parse value (%s): (%s)!", token.c_str(), exception.what());
|
||||
}
|
||||
const auto parameterInfo = result.getStringField(3);
|
||||
const auto values = GeneralUtils::SplitString(parameterInfo, ',');
|
||||
if (values.size() >= 4) {
|
||||
GeneralUtils::TryParse(values.at(0), param.values.skillId);
|
||||
GeneralUtils::TryParse(values.at(1), param.values.stacks);
|
||||
GeneralUtils::TryParse(values.at(2), param.values.tick);
|
||||
GeneralUtils::TryParse(values.at(3), param.values.unknown2);
|
||||
} else {
|
||||
Game::logger->Log("BuffComponent", "Failed to parse %s into parameter struct. Too few parameters to split on.", parameterInfo);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -14,20 +14,24 @@ class Entity;
|
||||
/**
|
||||
* Extra information on effects to apply after applying a buff, for example whether to buff armor, imag or health and by how much
|
||||
*/
|
||||
struct BuffParameter
|
||||
{
|
||||
int32_t buffId;
|
||||
struct BuffParameter {
|
||||
struct ParameterValues {
|
||||
int32_t skillId = 0;
|
||||
int32_t stacks = 0;
|
||||
float tick = 0.0f;
|
||||
int32_t unknown2 = 0;
|
||||
};
|
||||
int32_t buffId = 0;
|
||||
std::string name;
|
||||
float value;
|
||||
std::vector<float> values;
|
||||
int32_t effectId;
|
||||
float value = 0.0f;
|
||||
ParameterValues values;
|
||||
int32_t effectId = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* Meta information about a buff that can be applied, e.g. how long it's applied, who applied it, etc.
|
||||
*/
|
||||
struct Buff
|
||||
{
|
||||
struct Buff {
|
||||
int32_t id = 0;
|
||||
float time = 0;
|
||||
float tick = 0;
|
||||
@@ -40,15 +44,11 @@ struct Buff
|
||||
/**
|
||||
* Allows for the application of buffs to the parent entity, altering health, armor and imagination.
|
||||
*/
|
||||
class BuffComponent : public Component {
|
||||
class BuffComponent final : public Component {
|
||||
public:
|
||||
static const eReplicaComponentType ComponentType = eReplicaComponentType::BUFF;
|
||||
inline static const eReplicaComponentType ComponentType = eReplicaComponentType::BUFF;
|
||||
|
||||
explicit BuffComponent(Entity* parent);
|
||||
|
||||
~BuffComponent();
|
||||
|
||||
Entity* GetParent() const;
|
||||
explicit BuffComponent(Entity* parent) : Component(parent) {};
|
||||
|
||||
void LoadFromXml(tinyxml2::XMLDocument* doc) override;
|
||||
|
||||
@@ -88,7 +88,7 @@ public:
|
||||
* @param id the id of the buff to find
|
||||
* @return whether or not the entity has a buff with the specified id active
|
||||
*/
|
||||
bool HasBuff(int32_t id);
|
||||
bool HasBuff(int32_t id) { return m_Buffs.find(id) != m_Buffs.end(); };
|
||||
|
||||
/**
|
||||
* Applies the effects of the buffs on the entity, e.g.: changing armor, health, imag, etc.
|
||||
@@ -110,7 +110,7 @@ public:
|
||||
/**
|
||||
* Removes all buffs for the entity and reverses all of their effects
|
||||
*/
|
||||
void Reset();
|
||||
void Reset() { RemoveAllBuffs(); };
|
||||
|
||||
/**
|
||||
* Applies all effects for all buffs, active or not, again
|
||||
|
||||
@@ -9,62 +9,40 @@
|
||||
#include "Item.h"
|
||||
#include "PropertyManagementComponent.h"
|
||||
|
||||
BuildBorderComponent::BuildBorderComponent(Entity* parent) : Component(parent) {
|
||||
}
|
||||
|
||||
BuildBorderComponent::~BuildBorderComponent() {
|
||||
}
|
||||
|
||||
void BuildBorderComponent::OnUse(Entity* originator) {
|
||||
if (originator->GetCharacter()) {
|
||||
const auto& entities = 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());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,27 +6,23 @@
|
||||
#ifndef BUILDBORDERCOMPONENT_H
|
||||
#define BUILDBORDERCOMPONENT_H
|
||||
|
||||
#include "BitStream.h"
|
||||
#include "Entity.h"
|
||||
#include "Component.h"
|
||||
#include "eReplicaComponentType.h"
|
||||
|
||||
/**
|
||||
* Component for the build border, allowing the user to start building when interacting with it
|
||||
*/
|
||||
class BuildBorderComponent : public Component {
|
||||
class BuildBorderComponent final : public Component {
|
||||
public:
|
||||
static const eReplicaComponentType ComponentType = eReplicaComponentType::BUILD_BORDER;
|
||||
inline static const eReplicaComponentType ComponentType = eReplicaComponentType::BUILD_BORDER;
|
||||
|
||||
BuildBorderComponent(Entity* parent);
|
||||
~BuildBorderComponent() override;
|
||||
BuildBorderComponent(Entity* parent) : Component(parent) { };
|
||||
|
||||
/**
|
||||
* Causes the originator to start build with this entity as a reference point
|
||||
* @param originator the entity (probably a player) that triggered the event
|
||||
*/
|
||||
void OnUse(Entity* originator) override;
|
||||
private:
|
||||
};
|
||||
|
||||
#endif // BUILDBORDERCOMPONENT_H
|
||||
|
||||
@@ -1,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)
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
7
dGame/dComponents/CollectibleComponent.cpp
Normal file
7
dGame/dComponents/CollectibleComponent.cpp
Normal file
@@ -0,0 +1,7 @@
|
||||
#include "CollectibleComponent.h"
|
||||
|
||||
#include "Entity.h"
|
||||
|
||||
void CollectibleComponent::Startup() {
|
||||
m_CollectibleId = GetParentEntity()->GetVarAs<int32_t>(u"collectible_id");
|
||||
}
|
||||
22
dGame/dComponents/CollectibleComponent.h
Normal file
22
dGame/dComponents/CollectibleComponent.h
Normal file
@@ -0,0 +1,22 @@
|
||||
#ifndef __COLLECTIBLECOMPONENT__H__
|
||||
#define __COLLECTIBLECOMPONENT__H__
|
||||
|
||||
#include "Component.h"
|
||||
#include "eReplicaComponentType.h"
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
class CollectibleComponent : public Component {
|
||||
public:
|
||||
inline static const eReplicaComponentType ComponentType = eReplicaComponentType::COLLECTIBLE;
|
||||
CollectibleComponent(Entity* parent) : Component(parent) { };
|
||||
|
||||
void Startup() override;
|
||||
|
||||
uint32_t GetCollectibleId() const { return m_CollectibleId; }
|
||||
private:
|
||||
uint32_t m_CollectibleId;
|
||||
};
|
||||
|
||||
|
||||
#endif //!__COLLECTIBLECOMPONENT__H__
|
||||
@@ -1,30 +1,7 @@
|
||||
#include "Component.h"
|
||||
#include "DluAssert.h"
|
||||
|
||||
|
||||
Component::Component(Entity* parent) {
|
||||
m_Parent = parent;
|
||||
}
|
||||
|
||||
Component::~Component() {
|
||||
|
||||
}
|
||||
|
||||
Entity* Component::GetParent() const {
|
||||
return m_Parent;
|
||||
}
|
||||
|
||||
void Component::Update(float deltaTime) {
|
||||
|
||||
}
|
||||
|
||||
void Component::OnUse(Entity* originator) {
|
||||
|
||||
}
|
||||
|
||||
void Component::UpdateXml(tinyxml2::XMLDocument* doc) {
|
||||
|
||||
}
|
||||
|
||||
void Component::LoadFromXml(tinyxml2::XMLDocument* doc) {
|
||||
|
||||
Component::Component(Entity* owningEntity) {
|
||||
DluAssert(owningEntity != nullptr);
|
||||
m_ParentEntity = owningEntity;
|
||||
}
|
||||
|
||||
@@ -1,52 +1,74 @@
|
||||
#pragma once
|
||||
|
||||
#include "../thirdparty/tinyxml2/tinyxml2.h"
|
||||
#include "tinyxml2.h"
|
||||
|
||||
class Entity;
|
||||
|
||||
namespace RakNet {
|
||||
class BitStream;
|
||||
}
|
||||
|
||||
/**
|
||||
* Component base class, provides methods for game loop updates, usage events and loading and saving to XML.
|
||||
*/
|
||||
class Component
|
||||
{
|
||||
class Component {
|
||||
public:
|
||||
Component(Entity* parent);
|
||||
virtual ~Component();
|
||||
Component(Entity* owningEntity);
|
||||
virtual ~Component() {};
|
||||
|
||||
/**
|
||||
* Gets the owner of this component
|
||||
* @return the owner of this component
|
||||
*/
|
||||
Entity* GetParent() const;
|
||||
|
||||
/**
|
||||
* Updates the component in the game loop
|
||||
* @param deltaTime time passed since last update
|
||||
*/
|
||||
virtual void Update(float deltaTime);
|
||||
Entity* GetParentEntity() const { return m_ParentEntity; };
|
||||
|
||||
/**
|
||||
* Event called when this component is being used, e.g. when some entity interacted with it
|
||||
* @param originator
|
||||
*/
|
||||
virtual void OnUse(Entity* originator);
|
||||
virtual void OnUse(Entity* originator) {};
|
||||
|
||||
/**
|
||||
* Save data from this componennt to character XML
|
||||
* @param doc the document to write data to
|
||||
*/
|
||||
virtual void UpdateXml(tinyxml2::XMLDocument* doc);
|
||||
virtual void UpdateXml(tinyxml2::XMLDocument* doc) {};
|
||||
|
||||
/**
|
||||
* Load base data for this component from character XML
|
||||
* @param doc the document to read data from
|
||||
*/
|
||||
virtual void LoadFromXml(tinyxml2::XMLDocument* doc);
|
||||
virtual void LoadFromXml(tinyxml2::XMLDocument* doc) {};
|
||||
|
||||
/**
|
||||
* Call after you have newed the component to initialize it
|
||||
*/
|
||||
virtual void Startup() {};
|
||||
|
||||
/**
|
||||
* Updates the component in the game loop
|
||||
* @param deltaTime time passed since last update
|
||||
*/
|
||||
virtual void Update(float deltaTime) {};
|
||||
|
||||
/**
|
||||
* Loads the data of this component from the luz/lvl configuration
|
||||
*/
|
||||
virtual void LoadConfigData() {};
|
||||
|
||||
/**
|
||||
* Loads the data of this component from the cdclient database
|
||||
*/
|
||||
virtual void LoadTemplateData() {};
|
||||
|
||||
/**
|
||||
* Serializes the component for delivery to the client(s)
|
||||
*/
|
||||
virtual void Serialize(RakNet::BitStream* bitStream, bool isConstruction = false) {};
|
||||
protected:
|
||||
|
||||
/**
|
||||
* The entity that owns this component
|
||||
*/
|
||||
Entity* m_Parent;
|
||||
Entity* m_ParentEntity;
|
||||
};
|
||||
|
||||
153
dGame/dComponents/ComponentHeirachy.md
Normal file
153
dGame/dComponents/ComponentHeirachy.md
Normal file
@@ -0,0 +1,153 @@
|
||||
Legend
|
||||
```
|
||||
├── Explicit inheritance
|
||||
├~~ Loaded with Parent
|
||||
├-> SubComponent
|
||||
├-? idk lol, but related
|
||||
|
||||
originalName -> newName
|
||||
|
||||
```
|
||||
|
||||
|
||||
```tree
|
||||
|
||||
LWOActivityComponent
|
||||
├── LWOQuickBuildComponent
|
||||
├── LWOMiniGameControlComponent
|
||||
├── LWOShootingGalleryComponent
|
||||
├── LWOScriptedActivityComponent
|
||||
| └── LWOBaseRacingControlComponent -> RacingControlComponent
|
||||
| ├── LWORacingControlComponent -> RacingComponent
|
||||
| └── LWOGateRushControlComponent -> GateRushComponent
|
||||
LWOBaseCombatAIComponent
|
||||
├~~ LWOPathfindingControlComponent
|
||||
├~~ LWOProximityMonitorComponent
|
||||
LWOProjectilePhysComponent
|
||||
LWOBasePhysComponent
|
||||
├── LWORigidBodyPhantomComponent
|
||||
├── LWOPhantomPhysComponent
|
||||
├── LWOVehiclePhysicsComponent
|
||||
├── LWOSimplePhysComponent
|
||||
| ├~~ LWOPhantomPhysicsComponent
|
||||
├── LWOPhysicsSystemComponent
|
||||
├── LWOHavokVehiclePhysicsComponent
|
||||
├── LWOControllablePhysComponent
|
||||
LWOBaseRenderComponent
|
||||
├── LWOSkinnedRenderComponent
|
||||
├~~ LWOSkinnedRenderComponent
|
||||
├~~ LWOFXComponent
|
||||
LWOBaseVendorComponent
|
||||
├── LWOVendorComponent
|
||||
| ├~~ LWOProximityMonitorComponent
|
||||
├── LWODonationVendorComponent
|
||||
| ├~~ LWOProximityMonitorComponent
|
||||
├── LWOAchievementVendorComponent
|
||||
| ├~~ LWOProximityMonitorComponent
|
||||
LWOBBBComponent_Common
|
||||
├── LWOBBBComponent_Client
|
||||
LWOBlueprintComponent
|
||||
LWOBouncerComponent
|
||||
LWOBuildControllerComponentCommon
|
||||
├── LWOBuildControllerComponent
|
||||
LWOChangelingBuildComponent
|
||||
LWOCharacterComponent
|
||||
├── LWOMinifigComponent
|
||||
├~~ LWOPossessionControlComponent
|
||||
├~~ LWOMountControlComponent
|
||||
├~~ LWOPetCreatorComponent
|
||||
├~~ LWOLevelProgressionComponent
|
||||
├~~ LWOPlayerForcedMovementComponent
|
||||
| ├~? LWOPathfindingControlComponent
|
||||
LWOChestComponent
|
||||
LWOChoiceBuildComponent
|
||||
LWOCollectibleComponent
|
||||
LWOCustomBuildAssemblyComponent
|
||||
LWODestroyableComponent
|
||||
├~~ LWOBuffComponent
|
||||
├~~ LWOStatusEffectComponent
|
||||
LWODropEffectComponent
|
||||
LWODroppedLootComponent
|
||||
LWOExhibitComponent
|
||||
LWOGenericActivatorComponent
|
||||
LWOGhostComponent
|
||||
LWOHFLightDirectionGadgetComponent
|
||||
LWOInventoryComponent_Common
|
||||
├── LWOInventoryComponent_Client
|
||||
| └── LWOInventoryComponent_EquippedItem
|
||||
LWOItemComponent
|
||||
LWOLUPExhibitComponent
|
||||
LWOMissionOfferComponent
|
||||
LWOModelBehaviorComponent
|
||||
├~~ LWOSimplePhysComponent
|
||||
├~~ LWOControllablePhysComponent
|
||||
├~~ LWOPathfindingControlComponent
|
||||
├~~ LWOMutableModelBehaviorComponent
|
||||
LWOModelBuilderComponent
|
||||
LWOModularBuildComponentCommon
|
||||
├── LWOModularBuildComponent
|
||||
LWOModuleAssemblyComponent
|
||||
├── LWOModuleAssemblyComponentCommon
|
||||
LWOModuleComponentCommon
|
||||
├── LWOModuleComponent
|
||||
LWOMovementAIComponent
|
||||
LWOMultiZoneEntranceComponent
|
||||
LWOOverheadIconComponent
|
||||
LWOPetComponent
|
||||
├~~ LWOPathfindingControlComponent
|
||||
├~? LWOItemComponent
|
||||
├~? LWOModelBehaviorComponent
|
||||
| ├~~ ...
|
||||
LWOPlatformBoundaryComponent
|
||||
LWOPlatformComponent
|
||||
├-> LWOMoverPlatformSubComponent
|
||||
├-> LWOSimpleMoverPlatformSubComponent
|
||||
├-> LWORotaterPlatformSubComponent
|
||||
LWOPropertyComponent
|
||||
LWOPropertyEntranceComponent
|
||||
LWOPropertyManagementComponent
|
||||
LWOPropertyVendorComponent
|
||||
LWOProximityMonitorComponent
|
||||
LWOSoundTriggerComponent
|
||||
├── LWORacingSoundTriggerComponent
|
||||
LWORacingStatsComponentCommon
|
||||
├── LWORacingStatsComponent
|
||||
LWORocketAnimationControlComponentCommon
|
||||
├── LWORocketAnimationControlComponent
|
||||
LWORocketLaunchpadControlComponentCommon
|
||||
├── LWORocketLaunchpadControlComponent
|
||||
LWOScriptComponent
|
||||
├~~ LWOPathfindingControlComponent
|
||||
├~~ LWOProximityMonitorComponent
|
||||
LWOShowcaseModelHandlerComponent
|
||||
LWOSkillComponent
|
||||
LWOSoundAmbient2DComponent
|
||||
LWOSoundAmbient3DComponent
|
||||
LWOSoundRepeaterComponent
|
||||
LWOSpawnComponent
|
||||
LWOSpringpadComponent
|
||||
LWOSwitchComponent
|
||||
LWOTriggerComponent
|
||||
LocalPlayer - This is a function call in the client which, if the generated Entity is a player, the below components are added. This **must** be done for all players.
|
||||
├~~ LWOInteractionManagerComponent
|
||||
├~~ LWOUserControlComponent
|
||||
├~~ LWOFriendsListComponent
|
||||
├~~ LWOIgnoreListComponent
|
||||
├~~ LWOTextEffectComponent
|
||||
├~~ LWOChatBubbleComponent
|
||||
├~~ LWOGuildComponent
|
||||
├~~ LWOPlayerPetTamingComponent
|
||||
├~~ LWOLocalSystemsComponent
|
||||
├~~ LWOSlashCommandComponent
|
||||
├~~ LWOMissionComponent
|
||||
├~~ LWOPropertyEditorComponent
|
||||
├~~ LWOComponent115
|
||||
├~~ LWOTeamsComponent
|
||||
├~~ LWOChatComponent
|
||||
├~~ LWOPetControlComponent
|
||||
├~~ LWOTradeComponent
|
||||
├~~ LWOPreconditionComponent
|
||||
├~~ LWOFlagComponent
|
||||
├~~ LWOFactionTriggerComponent
|
||||
|
||||
```
|
||||
@@ -3,6 +3,7 @@
|
||||
#include "BitStream.h"
|
||||
#include "dLogger.h"
|
||||
#include "Game.h"
|
||||
#include "dServer.h"
|
||||
|
||||
#include "dpWorld.h"
|
||||
#include "dpEntity.h"
|
||||
@@ -51,34 +52,30 @@ ControllablePhysicsComponent::ControllablePhysicsComponent(Entity* entity) : Com
|
||||
m_ImmuneToStunTurnCount = 0;
|
||||
m_ImmuneToStunUseItemCount = 0;
|
||||
|
||||
if (entity->GetLOT() != 1) // Other physics entities we care about will be added by BaseCombatAI
|
||||
return;
|
||||
// Other physics entities we care about will be added by BaseCombatAI
|
||||
if (entity->GetLOT() != 1) return;
|
||||
|
||||
if (entity->GetLOT() == 1) {
|
||||
Game::logger->Log("ControllablePhysicsComponent", "Using patch to load minifig physics");
|
||||
Game::logger->Log("ControllablePhysicsComponent", "Using patch to load minifig physics");
|
||||
|
||||
float radius = 1.5f;
|
||||
m_dpEntity = new dpEntity(m_Parent->GetObjectID(), radius, false);
|
||||
m_dpEntity->SetCollisionGroup(COLLISION_GROUP_DYNAMIC | COLLISION_GROUP_FRIENDLY);
|
||||
dpWorld::Instance().AddEntity(m_dpEntity);
|
||||
}
|
||||
float radius = 1.5f;
|
||||
m_dpEntity = new dpEntity(m_ParentEntity->GetObjectID(), radius, false);
|
||||
m_dpEntity->SetCollisionGroup(COLLISION_GROUP_DYNAMIC | COLLISION_GROUP_FRIENDLY);
|
||||
dpWorld::Instance().AddEntity(m_dpEntity);
|
||||
}
|
||||
|
||||
ControllablePhysicsComponent::~ControllablePhysicsComponent() {
|
||||
if (m_dpEntity) {
|
||||
dpWorld::Instance().RemoveEntity(m_dpEntity);
|
||||
}
|
||||
if (!m_dpEntity) return;
|
||||
dpWorld::Instance().RemoveEntity(m_dpEntity);
|
||||
}
|
||||
|
||||
void ControllablePhysicsComponent::Update(float deltaTime) {
|
||||
|
||||
void ControllablePhysicsComponent::Startup() {
|
||||
NiPoint3 pos = m_ParentEntity->GetDefaultPosition();
|
||||
NiQuaternion rot = m_ParentEntity->GetDefaultRotation();
|
||||
SetPosition(pos);
|
||||
SetRotation(rot);
|
||||
}
|
||||
|
||||
void ControllablePhysicsComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags) {
|
||||
//If this is a creation, then we assume the position is dirty, even when it isn't.
|
||||
//This is because new clients will still need to receive the position.
|
||||
//if (bIsInitialUpdate) m_DirtyPosition = true;
|
||||
|
||||
if (bIsInitialUpdate) {
|
||||
outBitStream->Write(m_InJetpackMode);
|
||||
if (m_InJetpackMode) {
|
||||
@@ -99,33 +96,32 @@ void ControllablePhysicsComponent::Serialize(RakNet::BitStream* outBitStream, bo
|
||||
|
||||
if (m_IgnoreMultipliers) m_DirtyCheats = false;
|
||||
|
||||
outBitStream->Write(m_DirtyCheats);
|
||||
if (m_DirtyCheats) {
|
||||
outBitStream->Write(bIsInitialUpdate || m_DirtyCheats);
|
||||
if (bIsInitialUpdate || m_DirtyCheats) {
|
||||
outBitStream->Write(m_GravityScale);
|
||||
outBitStream->Write(m_SpeedMultiplier);
|
||||
|
||||
m_DirtyCheats = false;
|
||||
if (!bIsInitialUpdate) m_DirtyCheats = false;
|
||||
}
|
||||
|
||||
outBitStream->Write(m_DirtyEquippedItemInfo);
|
||||
if (m_DirtyEquippedItemInfo) {
|
||||
outBitStream->Write(bIsInitialUpdate || m_DirtyEquippedItemInfo);
|
||||
if (bIsInitialUpdate || m_DirtyEquippedItemInfo) {
|
||||
outBitStream->Write(m_PickupRadius);
|
||||
outBitStream->Write(m_InJetpackMode);
|
||||
m_DirtyEquippedItemInfo = false;
|
||||
if (!bIsInitialUpdate) m_DirtyEquippedItemInfo = false;
|
||||
}
|
||||
|
||||
outBitStream->Write(m_DirtyBubble);
|
||||
if (m_DirtyBubble) {
|
||||
outBitStream->Write(bIsInitialUpdate || m_DirtyBubble);
|
||||
if (bIsInitialUpdate || m_DirtyBubble) {
|
||||
outBitStream->Write(m_IsInBubble);
|
||||
if (m_IsInBubble) {
|
||||
outBitStream->Write(m_BubbleType);
|
||||
outBitStream->Write(m_SpecialAnims);
|
||||
}
|
||||
m_DirtyBubble = false;
|
||||
if (!bIsInitialUpdate) m_DirtyBubble = false;
|
||||
}
|
||||
|
||||
outBitStream->Write(m_DirtyPosition || bIsInitialUpdate);
|
||||
if (m_DirtyPosition || bIsInitialUpdate) {
|
||||
outBitStream->Write(bIsInitialUpdate || m_DirtyPosition);
|
||||
if (bIsInitialUpdate || m_DirtyPosition) {
|
||||
outBitStream->Write(m_Position.x);
|
||||
outBitStream->Write(m_Position.y);
|
||||
outBitStream->Write(m_Position.z);
|
||||
@@ -138,20 +134,22 @@ void ControllablePhysicsComponent::Serialize(RakNet::BitStream* outBitStream, bo
|
||||
outBitStream->Write(m_IsOnGround);
|
||||
outBitStream->Write(m_IsOnRail);
|
||||
|
||||
outBitStream->Write(m_DirtyVelocity);
|
||||
if (m_DirtyVelocity) {
|
||||
outBitStream->Write(bIsInitialUpdate || m_DirtyVelocity);
|
||||
if (bIsInitialUpdate || m_DirtyVelocity) {
|
||||
outBitStream->Write(m_Velocity.x);
|
||||
outBitStream->Write(m_Velocity.y);
|
||||
outBitStream->Write(m_Velocity.z);
|
||||
if (!bIsInitialUpdate) m_DirtyVelocity = false;
|
||||
}
|
||||
|
||||
outBitStream->Write(m_DirtyAngularVelocity);
|
||||
if (m_DirtyAngularVelocity) {
|
||||
outBitStream->Write(bIsInitialUpdate || m_DirtyAngularVelocity);
|
||||
if (bIsInitialUpdate || m_DirtyAngularVelocity) {
|
||||
outBitStream->Write(m_AngularVelocity.x);
|
||||
outBitStream->Write(m_AngularVelocity.y);
|
||||
outBitStream->Write(m_AngularVelocity.z);
|
||||
if (!bIsInitialUpdate) m_DirtyAngularVelocity = false;
|
||||
}
|
||||
|
||||
if (!bIsInitialUpdate) m_DirtyPosition = false;
|
||||
outBitStream->Write0();
|
||||
}
|
||||
|
||||
@@ -162,22 +160,45 @@ void ControllablePhysicsComponent::Serialize(RakNet::BitStream* outBitStream, bo
|
||||
}
|
||||
|
||||
void ControllablePhysicsComponent::LoadFromXml(tinyxml2::XMLDocument* doc) {
|
||||
tinyxml2::XMLElement* character = doc->FirstChildElement("obj")->FirstChildElement("char");
|
||||
if (!character) {
|
||||
tinyxml2::XMLElement* characterElem = doc->FirstChildElement("obj")->FirstChildElement("char");
|
||||
if (!characterElem) {
|
||||
Game::logger->Log("ControllablePhysicsComponent", "Failed to find char tag!");
|
||||
return;
|
||||
}
|
||||
|
||||
m_Parent->GetCharacter()->LoadXmlRespawnCheckpoints();
|
||||
m_ParentEntity->GetCharacter()->LoadXmlRespawnCheckpoints();
|
||||
|
||||
character->QueryAttribute("lzx", &m_Position.x);
|
||||
character->QueryAttribute("lzy", &m_Position.y);
|
||||
character->QueryAttribute("lzz", &m_Position.z);
|
||||
character->QueryAttribute("lzrx", &m_Rotation.x);
|
||||
character->QueryAttribute("lzry", &m_Rotation.y);
|
||||
character->QueryAttribute("lzrz", &m_Rotation.z);
|
||||
character->QueryAttribute("lzrw", &m_Rotation.w);
|
||||
characterElem->QueryAttribute("lzx", &m_Position.x);
|
||||
characterElem->QueryAttribute("lzy", &m_Position.y);
|
||||
characterElem->QueryAttribute("lzz", &m_Position.z);
|
||||
characterElem->QueryAttribute("lzrx", &m_Rotation.x);
|
||||
characterElem->QueryAttribute("lzry", &m_Rotation.y);
|
||||
characterElem->QueryAttribute("lzrz", &m_Rotation.z);
|
||||
characterElem->QueryAttribute("lzrw", &m_Rotation.w);
|
||||
|
||||
auto* character = GetParentEntity()->GetCharacter();
|
||||
const auto mapID = Game::server->GetZoneID();
|
||||
|
||||
//If we came from another zone, put us in the starting loc
|
||||
if (character->GetZoneID() != Game::server->GetZoneID() || mapID == 1603) { // Exception for Moon Base as you tend to spawn on the roof.
|
||||
const auto& targetSceneName = character->GetTargetScene();
|
||||
auto* targetScene = EntityManager::Instance()->GetSpawnPointEntity(targetSceneName);
|
||||
|
||||
NiPoint3 pos;
|
||||
NiQuaternion rot;
|
||||
if (character->HasBeenToWorld(mapID) && targetSceneName.empty()) {
|
||||
pos = character->GetRespawnPoint(mapID);
|
||||
rot = dZoneManager::Instance()->GetZone()->GetSpawnRot();
|
||||
} else if (targetScene) {
|
||||
pos = targetScene->GetPosition();
|
||||
rot = targetScene->GetRotation();
|
||||
} else {
|
||||
pos = dZoneManager::Instance()->GetZone()->GetSpawnPos();
|
||||
rot = dZoneManager::Instance()->GetZone()->GetSpawnRot();
|
||||
}
|
||||
SetPosition(pos);
|
||||
SetRotation(rot);
|
||||
}
|
||||
m_DirtyPosition = true;
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -19,18 +19,18 @@ enum class eStateChangeType : uint32_t;
|
||||
/**
|
||||
* Handles the movement of controllable Entities, e.g. enemies and players
|
||||
*/
|
||||
class ControllablePhysicsComponent : public Component {
|
||||
class ControllablePhysicsComponent final : public Component {
|
||||
public:
|
||||
static const eReplicaComponentType ComponentType = eReplicaComponentType::CONTROLLABLE_PHYSICS;
|
||||
inline static const eReplicaComponentType ComponentType = eReplicaComponentType::CONTROLLABLE_PHYSICS;
|
||||
|
||||
ControllablePhysicsComponent(Entity* entity);
|
||||
~ControllablePhysicsComponent() override;
|
||||
|
||||
void Update(float deltaTime) override;
|
||||
void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags);
|
||||
void LoadFromXml(tinyxml2::XMLDocument* doc) override;
|
||||
void ResetFlags();
|
||||
void UpdateXml(tinyxml2::XMLDocument* doc) override;
|
||||
void Startup() override;
|
||||
|
||||
/**
|
||||
* Sets the position of this entity, also ensures this update is serialized next tick.
|
||||
@@ -115,19 +115,19 @@ public:
|
||||
* Mark the position as dirty, forcing a serialization update next tick
|
||||
* @param val whether or not the position is dirty
|
||||
*/
|
||||
void SetDirtyPosition(bool val);
|
||||
void SetDirtyPosition(bool val) { m_DirtyPosition = val; }
|
||||
|
||||
/**
|
||||
* Mark the velocity as dirty, forcing a serializtion update next tick
|
||||
* @param val whether or not the velocity is dirty
|
||||
*/
|
||||
void SetDirtyVelocity(bool val);
|
||||
void SetDirtyVelocity(bool val) { m_DirtyVelocity = val; }
|
||||
|
||||
/**
|
||||
* Mark the angular velocity as dirty, forcing a serialization update next tick
|
||||
* @param val whether or not the angular velocity is dirty
|
||||
*/
|
||||
void SetDirtyAngularVelocity(bool val);
|
||||
void SetDirtyAngularVelocity(bool val) { m_DirtyAngularVelocity = val; }
|
||||
|
||||
/**
|
||||
* Sets whether or not the entity is currently wearing a jetpack
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
#include "CDClientManager.h"
|
||||
#include "CDDestructibleComponentTable.h"
|
||||
#include "EntityManager.h"
|
||||
#include "RebuildComponent.h"
|
||||
#include "QuickBuildComponent.h"
|
||||
#include "CppScripts.h"
|
||||
#include "Loot.h"
|
||||
#include "Character.h"
|
||||
@@ -28,7 +28,7 @@
|
||||
#include "MissionComponent.h"
|
||||
#include "CharacterComponent.h"
|
||||
#include "PossessableComponent.h"
|
||||
#include "PossessorComponent.h"
|
||||
#include "PossessionComponent.h"
|
||||
#include "InventoryComponent.h"
|
||||
#include "dZoneManager.h"
|
||||
#include "WorldConfig.h"
|
||||
@@ -37,8 +37,9 @@
|
||||
#include "eGameActivity.h"
|
||||
|
||||
#include "CDComponentsRegistryTable.h"
|
||||
#include "CDCurrencyTableTable.h"
|
||||
|
||||
DestroyableComponent::DestroyableComponent(Entity* parent) : Component(parent) {
|
||||
DestroyableComponent::DestroyableComponent(Entity* parent, int32_t componentId) : Component(parent) {
|
||||
m_iArmor = 0;
|
||||
m_fMaxArmor = 0.0f;
|
||||
m_iImagination = 0;
|
||||
@@ -51,7 +52,7 @@ DestroyableComponent::DestroyableComponent(Entity* parent) : Component(parent) {
|
||||
m_IsGMImmune = false;
|
||||
m_IsShielded = false;
|
||||
m_DamageToAbsorb = 0;
|
||||
m_IsModuleAssembly = m_Parent->HasComponent(eReplicaComponentType::MODULE_ASSEMBLY);
|
||||
m_IsModuleAssembly = m_ParentEntity->HasComponent(eReplicaComponentType::MODULE_ASSEMBLY);
|
||||
m_DirtyThreatList = false;
|
||||
m_HasThreats = false;
|
||||
m_ExplodeFactor = 1.0f;
|
||||
@@ -62,6 +63,7 @@ DestroyableComponent::DestroyableComponent(Entity* parent) : Component(parent) {
|
||||
m_MinCoins = 0;
|
||||
m_MaxCoins = 0;
|
||||
m_DamageReduction = 0;
|
||||
m_ComponentId = componentId;
|
||||
|
||||
m_ImmuneToBasicAttackCount = 0;
|
||||
m_ImmuneToDamageOverTimeCount = 0;
|
||||
@@ -73,49 +75,71 @@ DestroyableComponent::DestroyableComponent(Entity* parent) : Component(parent) {
|
||||
m_ImmuneToQuickbuildInterruptCount = 0;
|
||||
m_ImmuneToPullToPointCount = 0;
|
||||
}
|
||||
void DestroyableComponent::Startup() {
|
||||
|
||||
DestroyableComponent::~DestroyableComponent() {
|
||||
}
|
||||
|
||||
void DestroyableComponent::Reinitialize(LOT templateID) {
|
||||
CDComponentsRegistryTable* compRegistryTable = CDClientManager::Instance().GetTable<CDComponentsRegistryTable>();
|
||||
|
||||
int32_t buffComponentID = compRegistryTable->GetByIDAndType(templateID, eReplicaComponentType::BUFF);
|
||||
int32_t collectibleComponentID = compRegistryTable->GetByIDAndType(templateID, eReplicaComponentType::COLLECTIBLE);
|
||||
int32_t rebuildComponentID = compRegistryTable->GetByIDAndType(templateID, eReplicaComponentType::QUICK_BUILD);
|
||||
|
||||
int32_t componentID = 0;
|
||||
if (collectibleComponentID > 0) componentID = collectibleComponentID;
|
||||
if (rebuildComponentID > 0) componentID = rebuildComponentID;
|
||||
if (buffComponentID > 0) componentID = buffComponentID;
|
||||
|
||||
CDDestructibleComponentTable* destCompTable = CDClientManager::Instance().GetTable<CDDestructibleComponentTable>();
|
||||
std::vector<CDDestructibleComponent> destCompData = destCompTable->Query([=](CDDestructibleComponent entry) { return (entry.id == componentID); });
|
||||
|
||||
if (componentID > 0) {
|
||||
std::vector<CDDestructibleComponent> destCompData = destCompTable->Query([=](CDDestructibleComponent entry) { return (entry.id == componentID); });
|
||||
|
||||
if (destCompData.size() > 0) {
|
||||
SetHealth(destCompData[0].life);
|
||||
SetImagination(destCompData[0].imagination);
|
||||
SetArmor(destCompData[0].armor);
|
||||
|
||||
SetMaxHealth(destCompData[0].life);
|
||||
SetMaxImagination(destCompData[0].imagination);
|
||||
SetMaxArmor(destCompData[0].armor);
|
||||
|
||||
SetIsSmashable(destCompData[0].isSmashable);
|
||||
}
|
||||
} else {
|
||||
void DestroyableComponent::LoadConfigData() {
|
||||
SetIsSmashable(GetIsSmashable() | (m_ParentEntity->GetVarAs<int32_t>(u"is_smashable") != 0));
|
||||
}
|
||||
void DestroyableComponent::LoadTemplateData() {
|
||||
if (m_ParentEntity->IsPlayer()) return;
|
||||
auto* destroyableComponentTable = CDClientManager::Instance().GetTable<CDDestructibleComponentTable>();
|
||||
auto destroyableDataLookup = destroyableComponentTable->Query([this](CDDestructibleComponent entry) { return (entry.id == this->m_ComponentId); });
|
||||
if (m_ComponentId == -1 || destroyableDataLookup.empty()) {
|
||||
SetHealth(1);
|
||||
SetImagination(0);
|
||||
SetArmor(0);
|
||||
|
||||
SetMaxHealth(1);
|
||||
SetMaxImagination(0);
|
||||
SetMaxArmor(0);
|
||||
|
||||
SetIsSmashable(true);
|
||||
AddFaction(-1);
|
||||
AddFaction(6); //Smashables
|
||||
|
||||
// A race car has 60 imagination, other entities defaults to 0.
|
||||
SetImagination(m_ParentEntity->HasComponent(eReplicaComponentType::RACING_STATS) ? 60 : 0);
|
||||
SetMaxImagination(m_ParentEntity->HasComponent(eReplicaComponentType::RACING_STATS) ? 60 : 0);
|
||||
return;
|
||||
}
|
||||
|
||||
auto destroyableData = destroyableDataLookup.at(0);
|
||||
if (m_ParentEntity->HasComponent(eReplicaComponentType::RACING_STATS)) {
|
||||
destroyableData.imagination = 60;
|
||||
}
|
||||
|
||||
SetHealth(destroyableData.life);
|
||||
SetImagination(destroyableData.imagination);
|
||||
SetArmor(destroyableData.armor);
|
||||
|
||||
SetMaxHealth(destroyableData.life);
|
||||
SetMaxImagination(destroyableData.imagination);
|
||||
SetMaxArmor(destroyableData.armor);
|
||||
|
||||
SetIsSmashable(destroyableData.isSmashable);
|
||||
|
||||
SetLootMatrixID(destroyableData.LootMatrixIndex);
|
||||
|
||||
// Now get currency information
|
||||
uint32_t npcMinLevel = destroyableData.level;
|
||||
uint32_t currencyIndex = destroyableData.CurrencyIndex;
|
||||
|
||||
auto* currencyTable = CDClientManager::Instance().GetTable<CDCurrencyTableTable>();
|
||||
auto currencyValues = currencyTable->Query([=](CDCurrencyTable entry) { return (entry.currencyIndex == currencyIndex && entry.npcminlevel == npcMinLevel); });
|
||||
|
||||
if (currencyValues.size() > 0) {
|
||||
// Set the coins
|
||||
SetMinCoins(currencyValues.at(0).minvalue);
|
||||
SetMaxCoins(currencyValues.at(0).maxvalue);
|
||||
}
|
||||
AddFaction(destroyableData.faction);
|
||||
std::stringstream ss(destroyableData.factionList);
|
||||
std::string tokenStr;
|
||||
|
||||
while (std::getline(ss, tokenStr, ',')) {
|
||||
int32_t factionToAdd = -2;
|
||||
if (!GeneralUtils::TryParse(tokenStr, factionToAdd) || factionToAdd == -2 || factionToAdd == destroyableData.faction) continue;
|
||||
|
||||
AddFaction(factionToAdd);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -185,7 +209,7 @@ void DestroyableComponent::LoadFromXml(tinyxml2::XMLDocument* doc) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto* buffComponent = m_Parent->GetComponent<BuffComponent>();
|
||||
auto* buffComponent = m_ParentEntity->GetComponent<BuffComponent>();
|
||||
|
||||
if (buffComponent != nullptr) {
|
||||
buffComponent->LoadFromXml(doc);
|
||||
@@ -207,7 +231,7 @@ void DestroyableComponent::UpdateXml(tinyxml2::XMLDocument* doc) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto* buffComponent = m_Parent->GetComponent<BuffComponent>();
|
||||
auto* buffComponent = m_ParentEntity->GetComponent<BuffComponent>();
|
||||
|
||||
if (buffComponent != nullptr) {
|
||||
buffComponent->UpdateXml(doc);
|
||||
@@ -224,7 +248,7 @@ void DestroyableComponent::UpdateXml(tinyxml2::XMLDocument* doc) {
|
||||
void DestroyableComponent::SetHealth(int32_t value) {
|
||||
m_DirtyHealth = true;
|
||||
|
||||
auto* characterComponent = m_Parent->GetComponent<CharacterComponent>();
|
||||
auto* characterComponent = m_ParentEntity->GetComponent<CharacterComponent>();
|
||||
if (characterComponent != nullptr) {
|
||||
characterComponent->TrackHealthDelta(value - m_iHealth);
|
||||
}
|
||||
@@ -244,16 +268,16 @@ void DestroyableComponent::SetMaxHealth(float value, bool playAnim) {
|
||||
|
||||
if (playAnim) {
|
||||
// Now update the player bar
|
||||
if (!m_Parent->GetParentUser()) return;
|
||||
if (!m_ParentEntity->GetParentUser()) return;
|
||||
|
||||
AMFArrayValue args;
|
||||
args.Insert("amount", std::to_string(difference));
|
||||
args.Insert("type", "health");
|
||||
|
||||
GameMessages::SendUIMessageServerToSingleClient(m_Parent, m_Parent->GetParentUser()->GetSystemAddress(), "MaxPlayerBarUpdate", args);
|
||||
GameMessages::SendUIMessageServerToSingleClient(m_ParentEntity, m_ParentEntity->GetParentUser()->GetSystemAddress(), "MaxPlayerBarUpdate", args);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
23
dGame/dComponents/DonationVendorComponent.cpp
Normal file
23
dGame/dComponents/DonationVendorComponent.cpp
Normal 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();
|
||||
}
|
||||
44
dGame/dComponents/DonationVendorComponent.h
Normal file
44
dGame/dComponents/DonationVendorComponent.h
Normal 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__
|
||||
5
dGame/dComponents/GateRushComponent.cpp
Normal file
5
dGame/dComponents/GateRushComponent.cpp
Normal file
@@ -0,0 +1,5 @@
|
||||
#include "GateRushComponent.h"
|
||||
|
||||
GateRushComponent::GateRushComponent(Entity* parent, int32_t componentId) : RacingControlComponent(parent, componentId) {
|
||||
|
||||
}
|
||||
15
dGame/dComponents/GateRushComponent.h
Normal file
15
dGame/dComponents/GateRushComponent.h
Normal 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__
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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
@@ -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;
|
||||
|
||||
|
||||
32
dGame/dComponents/ItemComponent.cpp
Normal file
32
dGame/dComponents/ItemComponent.cpp
Normal 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;
|
||||
}
|
||||
|
||||
}
|
||||
55
dGame/dComponents/ItemComponent.h
Normal file
55
dGame/dComponents/ItemComponent.h
Normal 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__
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
7
dGame/dComponents/MinigameControlComponent.cpp
Normal file
7
dGame/dComponents/MinigameControlComponent.cpp
Normal file
@@ -0,0 +1,7 @@
|
||||
|
||||
#include "Entity.h"
|
||||
#include "MinigameControlComponent.h"
|
||||
|
||||
MinigameControlComponent::MinigameControlComponent(Entity* parent, int32_t componentId) : ActivityComponent(parent, componentId) {
|
||||
|
||||
}
|
||||
15
dGame/dComponents/MinigameControlComponent.h
Normal file
15
dGame/dComponents/MinigameControlComponent.h
Normal 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__
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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__
|
||||
|
||||
23
dGame/dComponents/ModelBehaviorComponent.cpp
Normal file
23
dGame/dComponents/ModelBehaviorComponent.cpp
Normal 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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
@@ -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
|
||||
}
|
||||
@@ -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) {
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -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__
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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__
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
|
||||
32
dGame/dComponents/MultiZoneEntranceComponent.cpp
Normal file
32
dGame/dComponents/MultiZoneEntranceComponent.cpp
Normal 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);
|
||||
}
|
||||
@@ -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;
|
||||
};
|
||||
29
dGame/dComponents/MutableModelBehaviorComponent.cpp
Normal file
29
dGame/dComponents/MutableModelBehaviorComponent.cpp
Normal 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;
|
||||
}
|
||||
}
|
||||
|
||||
53
dGame/dComponents/MutableModelBehaviorComponent.h
Normal file
53
dGame/dComponents/MutableModelBehaviorComponent.h
Normal 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;
|
||||
};
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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:
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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; }
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
82
dGame/dComponents/PossessionComponent.cpp
Normal file
82
dGame/dComponents/PossessionComponent.cpp
Normal 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);
|
||||
}
|
||||
@@ -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
Reference in New Issue
Block a user