diff --git a/.editorconfig b/.editorconfig index ebdfa7ac1..447413cc5 100644 --- a/.editorconfig +++ b/.editorconfig @@ -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 diff --git a/CMakeLists.txt b/CMakeLists.txt index 41d4219f6..4675dbfba 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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 diff --git a/CMakeVariables.txt b/CMakeVariables.txt index d3c8b36f2..647f43c68 100644 --- a/CMakeVariables.txt +++ b/CMakeVariables.txt @@ -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. diff --git a/dCommon/LDFFormat.h b/dCommon/LDFFormat.h index 0921d04c0..6f0977a5c 100644 --- a/dCommon/LDFFormat.h +++ b/dCommon/LDFFormat.h @@ -33,7 +33,7 @@ class LDFBaseData { 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 @@ class LDFData: public LDFBaseData { /*! \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 /*! diff --git a/dCommon/dEnums/dCommonVars.h b/dCommon/dEnums/dCommonVars.h index f67145da3..afdd4ec39 100644 --- a/dCommon/dEnums/dCommonVars.h +++ b/dCommon/dEnums/dCommonVars.h @@ -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; diff --git a/dCommon/dEnums/ePhysicsBehaviorType.h b/dCommon/dEnums/ePhysicsBehaviorType.h new file mode 100644 index 000000000..52862daa1 --- /dev/null +++ b/dCommon/dEnums/ePhysicsBehaviorType.h @@ -0,0 +1,14 @@ +#ifndef __EPHYSICSBEHAVIORTYPE__H__ +#define __EPHYSICSBEHAVIORTYPE__H__ + +#include + +enum class ePhysicsBehaviorType : int32_t { + INVALID = -1, + GROUND, + FLYING, + STANDARD, + DYNAMIC +}; + +#endif //!__EPHYSICSBEHAVIORTYPE__H__ diff --git a/dCommon/dEnums/eReplicaComponentType.h b/dCommon/dEnums/eReplicaComponentType.h index 2d24c19e4..e7444c6cf 100644 --- a/dCommon/dEnums/eReplicaComponentType.h +++ b/dCommon/dEnums/eReplicaComponentType.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__ diff --git a/dCommon/dEnums/eUgcModerationStatus.h b/dCommon/dEnums/eUgcModerationStatus.h new file mode 100644 index 000000000..ae289b74a --- /dev/null +++ b/dCommon/dEnums/eUgcModerationStatus.h @@ -0,0 +1,13 @@ + +#ifndef __EUGCMODERATIONSTATUS__H__ +#define __EUGCMODERATIONSTATUS__H__ + +#include + +enum class eUgcModerationStatus : uint32_t { + NoStatus, + Approved, + Rejected, +}; + +#endif //!__EUGCMODERATIONSTATUS__H__ diff --git a/dDatabase/Tables/CDComponentsRegistryTable.cpp b/dDatabase/Tables/CDComponentsRegistryTable.cpp index 32012f6ce..c053de911 100644 --- a/dDatabase/Tables/CDComponentsRegistryTable.cpp +++ b/dDatabase/Tables/CDComponentsRegistryTable.cpp @@ -90,4 +90,11 @@ int32_t CDComponentsRegistryTable::GetByIDAndType(uint32_t id, eReplicaComponent return defaultValue; #endif } - +std::vector> CDComponentsRegistryTable::GetTemplateComponents(LOT templateId) { + std::vector> components; + for (int8_t i = 0; static_cast(i) < eReplicaComponentType::NUMBER_OF_COMPONENTS; i++) { + auto compId = GetByIDAndType(templateId, static_cast(i), -1); + if (compId != -1) components.push_back(std::make_pair(static_cast(i), compId)); + } + return components; +} diff --git a/dDatabase/Tables/CDComponentsRegistryTable.h b/dDatabase/Tables/CDComponentsRegistryTable.h index 990072c99..855679d58 100644 --- a/dDatabase/Tables/CDComponentsRegistryTable.h +++ b/dDatabase/Tables/CDComponentsRegistryTable.h @@ -2,6 +2,7 @@ // Custom Classes #include "CDTable.h" +#include "dCommonVars.h" enum class eReplicaComponentType : uint32_t; struct CDComponentsRegistry { @@ -18,4 +19,5 @@ class CDComponentsRegistryTable : public CDTable { public: CDComponentsRegistryTable(); int32_t GetByIDAndType(uint32_t id, eReplicaComponentType componentType, int32_t defaultValue = 0); + std::vector> GetTemplateComponents(LOT templateId); }; diff --git a/dDatabase/Tables/CDItemComponentTable.cpp b/dDatabase/Tables/CDItemComponentTable.cpp index 54afc417f..d44b9d281 100644 --- a/dDatabase/Tables/CDItemComponentTable.cpp +++ b/dDatabase/Tables/CDItemComponentTable.cpp @@ -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(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; } diff --git a/dDatabase/Tables/CDItemComponentTable.h b/dDatabase/Tables/CDItemComponentTable.h index 11c34dd69..3e1eaf1d4 100644 --- a/dDatabase/Tables/CDItemComponentTable.h +++ b/dDatabase/Tables/CDItemComponentTable.h @@ -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 @@ class CDItemComponentTable : public CDTable { static std::map ParseCraftingCurrencies(const CDItemComponent& itemComponent); // Gets an entry by ID - const CDItemComponent& GetItemComponentByID(unsigned int skillID); + const CDItemComponent& GetItemComponentByID(unsigned int id); static CDItemComponent Default; }; diff --git a/dDatabase/Tables/CDPhysicsComponentTable.cpp b/dDatabase/Tables/CDPhysicsComponentTable.cpp index bb21ed7f4..0a7e2005f 100644 --- a/dDatabase/Tables/CDPhysicsComponentTable.cpp +++ b/dDatabase/Tables/CDPhysicsComponentTable.cpp @@ -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; } diff --git a/dGame/CMakeLists.txt b/dGame/CMakeLists.txt index 80f160426..c7b5e82c3 100644 --- a/dGame/CMakeLists.txt +++ b/dGame/CMakeLists.txt @@ -1,5 +1,4 @@ set(DGAME_SOURCES "Character.cpp" - "Entity.cpp" "EntityManager.cpp" "LeaderboardManager.cpp" "Player.cpp" diff --git a/dGame/Entity.cpp b/dGame/Entity.cpp deleted file mode 100644 index bac07713b..000000000 --- a/dGame/Entity.cpp +++ /dev/null @@ -1,2056 +0,0 @@ -#include "dCommonVars.h" -#include "Entity.h" -#include "CDClientManager.h" -#include "Game.h" -#include "dLogger.h" -#include -#include -#include "CDDestructibleComponentTable.h" -#include "CDClientDatabase.h" -#include -#include "dServer.h" -#include "GameMessages.h" -#include "EntityManager.h" -#include "dZoneManager.h" -#include "Zone.h" -#include "Spawner.h" -#include "UserManager.h" -#include "dpWorld.h" -#include "Player.h" -#include "LUTriggers.h" -#include "User.h" -#include "EntityTimer.h" -#include "EntityCallbackTimer.h" -#include "Loot.h" -#include "eMissionTaskType.h" -#include "eTriggerEventType.h" -#include "eObjectBits.h" - -//Component includes: -#include "Component.h" -#include "ControllablePhysicsComponent.h" -#include "RenderComponent.h" -#include "RocketLaunchLupComponent.h" -#include "CharacterComponent.h" -#include "DestroyableComponent.h" -#include "BuffComponent.h" -#include "BouncerComponent.h" -#include "InventoryComponent.h" -#include "LevelProgressionComponent.h" -#include "PlayerForcedMovementComponent.h" -#include "ScriptComponent.h" -#include "SkillComponent.h" -#include "SimplePhysicsComponent.h" -#include "SwitchComponent.h" -#include "PhantomPhysicsComponent.h" -#include "RigidbodyPhantomPhysicsComponent.h" -#include "MovingPlatformComponent.h" -#include "MissionComponent.h" -#include "MissionOfferComponent.h" -#include "RebuildComponent.h" -#include "BuildBorderComponent.h" -#include "MovementAIComponent.h" -#include "VendorComponent.h" -#include "RocketLaunchpadControlComponent.h" -#include "PropertyComponent.h" -#include "BaseCombatAIComponent.h" -#include "PropertyManagementComponent.h" -#include "PropertyVendorComponent.h" -#include "ProximityMonitorComponent.h" -#include "PropertyEntranceComponent.h" -#include "ModelComponent.h" -#include "ZCompression.h" -#include "PetComponent.h" -#include "VehiclePhysicsComponent.h" -#include "PossessableComponent.h" -#include "PossessorComponent.h" -#include "ModuleAssemblyComponent.h" -#include "RacingControlComponent.h" -#include "SoundTriggerComponent.h" -#include "ShootingGalleryComponent.h" -#include "RailActivatorComponent.h" -#include "LUPExhibitComponent.h" -#include "TriggerComponent.h" -#include "eGameMasterLevel.h" -#include "eReplicaComponentType.h" -#include "eReplicaPacketType.h" - -// Table includes -#include "CDComponentsRegistryTable.h" -#include "CDCurrencyTableTable.h" -#include "CDMovementAIComponentTable.h" -#include "CDProximityMonitorComponentTable.h" -#include "CDRebuildComponentTable.h" -#include "CDObjectSkillsTable.h" -#include "CDObjectsTable.h" -#include "CDScriptComponentTable.h" -#include "CDSkillBehaviorTable.h" -#include "CDZoneTableTable.h" - -Entity::Entity(const LWOOBJID& objectID, EntityInfo info, Entity* parentEntity) { - m_ObjectID = objectID; - m_TemplateID = info.lot; - m_ParentEntity = parentEntity; - m_Character = nullptr; - m_GMLevel = eGameMasterLevel::CIVILIAN; - m_CollectibleID = 0; - m_NetworkID = 0; - m_Groups = {}; - m_OwnerOverride = LWOOBJID_EMPTY; - m_Timers = {}; - m_ChildEntities = {}; - m_ScheduleKiller = nullptr; - m_TargetsInPhantom = {}; - m_Components = {}; - m_DieCallbacks = {}; - m_PhantomCollisionCallbacks = {}; - m_IsParentChildDirty = true; - - m_Settings = info.settings; - m_NetworkSettings = info.networkSettings; - m_DefaultPosition = info.pos; - m_DefaultRotation = info.rot; - m_Scale = info.scale; - m_Spawner = info.spawner; - m_SpawnerID = info.spawnerID; - m_HasSpawnerNodeID = info.hasSpawnerNodeID; - m_SpawnerNodeID = info.spawnerNodeID; - - if (info.lot != 1) m_PlayerIsReadyForUpdates = true; -} - -Entity::~Entity() { - if (m_Character) { - m_Character->SaveXMLToDatabase(); - } - - CancelAllTimers(); - CancelCallbackTimers(); - - const auto components = m_Components; - - for (const auto& pair : components) { - delete pair.second; - - m_Components.erase(pair.first); - } - - for (auto child : m_ChildEntities) { - if (child) child->RemoveParent(); - } - - if (m_ParentEntity) { - m_ParentEntity->RemoveChild(this); - } -} - -void Entity::Initialize() { - /** - * Setup trigger - */ - - const auto triggerInfo = GetVarAsString(u"trigger_id"); - - if (!triggerInfo.empty()) m_Components.emplace(eReplicaComponentType::TRIGGER, new TriggerComponent(this, triggerInfo)); - - /** - * Setup groups - */ - - const auto groupIDs = GetVarAsString(u"groupID"); - - if (!groupIDs.empty()) { - m_Groups = GeneralUtils::SplitString(groupIDs, ';'); - m_Groups.erase(m_Groups.end() - 1); - } - - /** - * Set ourselves as a child of our parent - */ - - if (m_ParentEntity != nullptr) { - m_ParentEntity->AddChild(this); - } - - // Get the registry table - CDComponentsRegistryTable* compRegistryTable = CDClientManager::Instance().GetTable(); - - /** - * Special case for BBB models. They have components not corresponding to the registry. - */ - - if (m_TemplateID == 14) { - const auto simplePhysicsComponentID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::SIMPLE_PHYSICS); - - SimplePhysicsComponent* comp = new SimplePhysicsComponent(simplePhysicsComponentID, this); - m_Components.insert(std::make_pair(eReplicaComponentType::SIMPLE_PHYSICS, comp)); - - ModelComponent* modelcomp = new ModelComponent(this); - m_Components.insert(std::make_pair(eReplicaComponentType::MODEL, modelcomp)); - - RenderComponent* render = new RenderComponent(this); - m_Components.insert(std::make_pair(eReplicaComponentType::RENDER, render)); - - auto destroyableComponent = new DestroyableComponent(this); - destroyableComponent->SetHealth(1); - destroyableComponent->SetMaxHealth(1.0f); - destroyableComponent->SetFaction(-1, true); - destroyableComponent->SetIsSmashable(true); - m_Components.insert(std::make_pair(eReplicaComponentType::DESTROYABLE, destroyableComponent)); - // We have all our components. - return; - } - - /** - * Go through all the components and check if this entity has them. - * - * Not all components are implemented. Some are represented by a nullptr, as they hold no data. - */ - - if (GetParentUser()) { - auto missions = new MissionComponent(this); - m_Components.insert(std::make_pair(eReplicaComponentType::MISSION, missions)); - missions->LoadFromXml(m_Character->GetXMLDoc()); - } - - uint32_t petComponentId = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::PET); - if (petComponentId > 0) { - m_Components.insert(std::make_pair(eReplicaComponentType::PET, new PetComponent(this, petComponentId))); - } - - if (compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::ZONE_CONTROL) > 0) { - m_Components.insert(std::make_pair(eReplicaComponentType::ZONE_CONTROL, nullptr)); - } - - uint32_t possessableComponentId = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::POSSESSABLE); - if (possessableComponentId > 0) { - m_Components.insert(std::make_pair(eReplicaComponentType::POSSESSABLE, new PossessableComponent(this, possessableComponentId))); - } - - if (compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::MODULE_ASSEMBLY) > 0) { - m_Components.insert(std::make_pair(eReplicaComponentType::MODULE_ASSEMBLY, new ModuleAssemblyComponent(this))); - } - - if (compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::RACING_STATS) > 0) { - m_Components.insert(std::make_pair(eReplicaComponentType::RACING_STATS, nullptr)); - } - - if (compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::LUP_EXHIBIT, -1) >= 0) { - m_Components.insert(std::make_pair(eReplicaComponentType::LUP_EXHIBIT, new LUPExhibitComponent(this))); - } - - if (compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::RACING_CONTROL) > 0) { - m_Components.insert(std::make_pair(eReplicaComponentType::RACING_CONTROL, new RacingControlComponent(this))); - } - - const auto propertyEntranceComponentID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::PROPERTY_ENTRANCE); - if (propertyEntranceComponentID > 0) { - m_Components.insert(std::make_pair(eReplicaComponentType::PROPERTY_ENTRANCE, - new PropertyEntranceComponent(propertyEntranceComponentID, this))); - } - - if (compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::CONTROLLABLE_PHYSICS) > 0) { - ControllablePhysicsComponent* controllablePhysics = new ControllablePhysicsComponent(this); - - if (m_Character) { - controllablePhysics->LoadFromXml(m_Character->GetXMLDoc()); - - const auto mapID = Game::server->GetZoneID(); - - //If we came from another zone, put us in the starting loc - if (m_Character->GetZoneID() != Game::server->GetZoneID() || mapID == 1603) { // Exception for Moon Base as you tend to spawn on the roof. - NiPoint3 pos; - NiQuaternion rot; - - const auto& targetSceneName = m_Character->GetTargetScene(); - auto* targetScene = EntityManager::Instance()->GetSpawnPointEntity(targetSceneName); - - if (m_Character->HasBeenToWorld(mapID) && targetSceneName.empty()) { - pos = m_Character->GetRespawnPoint(mapID); - rot = dZoneManager::Instance()->GetZone()->GetSpawnRot(); - } else if (targetScene != nullptr) { - pos = targetScene->GetPosition(); - rot = targetScene->GetRotation(); - } else { - pos = dZoneManager::Instance()->GetZone()->GetSpawnPos(); - rot = dZoneManager::Instance()->GetZone()->GetSpawnRot(); - } - - controllablePhysics->SetPosition(pos); - controllablePhysics->SetRotation(rot); - } - } else { - controllablePhysics->SetPosition(m_DefaultPosition); - controllablePhysics->SetRotation(m_DefaultRotation); - } - - m_Components.insert(std::make_pair(eReplicaComponentType::CONTROLLABLE_PHYSICS, controllablePhysics)); - } - - // If an entity is marked a phantom, simple physics is made into phantom phyics. - bool markedAsPhantom = GetVar(u"markedAsPhantom"); - - const auto simplePhysicsComponentID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::SIMPLE_PHYSICS); - if (!markedAsPhantom && simplePhysicsComponentID > 0) { - SimplePhysicsComponent* comp = new SimplePhysicsComponent(simplePhysicsComponentID, this); - m_Components.insert(std::make_pair(eReplicaComponentType::SIMPLE_PHYSICS, comp)); - } - - if (compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::RIGID_BODY_PHANTOM_PHYSICS) > 0) { - RigidbodyPhantomPhysicsComponent* comp = new RigidbodyPhantomPhysicsComponent(this); - m_Components.insert(std::make_pair(eReplicaComponentType::RIGID_BODY_PHANTOM_PHYSICS, comp)); - } - - if (markedAsPhantom || compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::PHANTOM_PHYSICS) > 0) { - PhantomPhysicsComponent* phantomPhysics = new PhantomPhysicsComponent(this); - phantomPhysics->SetPhysicsEffectActive(false); - m_Components.insert(std::make_pair(eReplicaComponentType::PHANTOM_PHYSICS, phantomPhysics)); - } - - if (compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::VEHICLE_PHYSICS) > 0) { - VehiclePhysicsComponent* vehiclePhysicsComponent = new VehiclePhysicsComponent(this); - m_Components.insert(std::make_pair(eReplicaComponentType::VEHICLE_PHYSICS, vehiclePhysicsComponent)); - vehiclePhysicsComponent->SetPosition(m_DefaultPosition); - vehiclePhysicsComponent->SetRotation(m_DefaultRotation); - } - - if (compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::SOUND_TRIGGER, -1) != -1) { - auto* comp = new SoundTriggerComponent(this); - m_Components.insert(std::make_pair(eReplicaComponentType::SOUND_TRIGGER, comp)); - } - - //Also check for the collectible id: - m_CollectibleID = GetVarAs(u"collectible_id"); - - if (compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::BUFF) > 0) { - BuffComponent* comp = new BuffComponent(this); - m_Components.insert(std::make_pair(eReplicaComponentType::BUFF, comp)); - } - - int collectibleComponentID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::COLLECTIBLE); - - if (collectibleComponentID > 0) { - m_Components.insert(std::make_pair(eReplicaComponentType::COLLECTIBLE, nullptr)); - } - - /** - * Multiple components require the destructible component. - */ - - int buffComponentID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::BUFF); - int rebuildComponentID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::QUICK_BUILD); - - int componentID = 0; - if (collectibleComponentID > 0) componentID = collectibleComponentID; - if (rebuildComponentID > 0) componentID = rebuildComponentID; - if (buffComponentID > 0) componentID = buffComponentID; - - CDDestructibleComponentTable* destCompTable = CDClientManager::Instance().GetTable(); - std::vector destCompData = destCompTable->Query([=](CDDestructibleComponent entry) { return (entry.id == componentID); }); - - if (buffComponentID > 0 || collectibleComponentID > 0) { - DestroyableComponent* comp = new DestroyableComponent(this); - if (m_Character) { - comp->LoadFromXml(m_Character->GetXMLDoc()); - } else { - if (componentID > 0) { - std::vector destCompData = destCompTable->Query([=](CDDestructibleComponent entry) { return (entry.id == componentID); }); - - if (destCompData.size() > 0) { - if (HasComponent(eReplicaComponentType::RACING_STATS)) { - destCompData[0].imagination = 60; - } - - comp->SetHealth(destCompData[0].life); - comp->SetImagination(destCompData[0].imagination); - comp->SetArmor(destCompData[0].armor); - - comp->SetMaxHealth(destCompData[0].life); - comp->SetMaxImagination(destCompData[0].imagination); - comp->SetMaxArmor(destCompData[0].armor); - - comp->SetIsSmashable(destCompData[0].isSmashable); - - comp->SetLootMatrixID(destCompData[0].LootMatrixIndex); - - // Now get currency information - uint32_t npcMinLevel = destCompData[0].level; - uint32_t currencyIndex = destCompData[0].CurrencyIndex; - - CDCurrencyTableTable* currencyTable = CDClientManager::Instance().GetTable(); - std::vector currencyValues = currencyTable->Query([=](CDCurrencyTable entry) { return (entry.currencyIndex == currencyIndex && entry.npcminlevel == npcMinLevel); }); - - if (currencyValues.size() > 0) { - // Set the coins - comp->SetMinCoins(currencyValues[0].minvalue); - comp->SetMaxCoins(currencyValues[0].maxvalue); - } - - // extraInfo overrides. Client ORs the database smashable and the luz smashable. - comp->SetIsSmashable(comp->GetIsSmashable() | (GetVarAs(u"is_smashable") != 0)); - } - } else { - comp->SetHealth(1); - comp->SetArmor(0); - - comp->SetMaxHealth(1); - comp->SetMaxArmor(0); - - comp->SetIsSmashable(true); - comp->AddFaction(-1); - comp->AddFaction(6); //Smashables - - // A race car has 60 imagination, other entities defaults to 0. - comp->SetImagination(HasComponent(eReplicaComponentType::RACING_STATS) ? 60 : 0); - comp->SetMaxImagination(HasComponent(eReplicaComponentType::RACING_STATS) ? 60 : 0); - } - } - - if (destCompData.size() > 0) { - comp->AddFaction(destCompData[0].faction); - std::stringstream ss(destCompData[0].factionList); - std::string token; - - while (std::getline(ss, token, ',')) { - if (std::stoi(token) == destCompData[0].faction) continue; - - if (token != "") { - comp->AddFaction(std::stoi(token)); - } - } - } - - m_Components.insert(std::make_pair(eReplicaComponentType::DESTROYABLE, comp)); - } - - if (compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::CHARACTER) > 0 || m_Character) { - // Character Component always has a possessor, level, and forced movement components - m_Components.insert(std::make_pair(eReplicaComponentType::POSSESSOR, new PossessorComponent(this))); - - // load in the xml for the level - auto* levelComp = new LevelProgressionComponent(this); - levelComp->LoadFromXml(m_Character->GetXMLDoc()); - m_Components.insert(std::make_pair(eReplicaComponentType::LEVEL_PROGRESSION, levelComp)); - - m_Components.insert(std::make_pair(eReplicaComponentType::PLAYER_FORCED_MOVEMENT, new PlayerForcedMovementComponent(this))); - - CharacterComponent* charComp = new CharacterComponent(this, m_Character); - charComp->LoadFromXml(m_Character->GetXMLDoc()); - m_Components.insert(std::make_pair(eReplicaComponentType::CHARACTER, charComp)); - } - - if (compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::INVENTORY) > 0 || m_Character) { - InventoryComponent* comp = nullptr; - if (m_Character) comp = new InventoryComponent(this, m_Character->GetXMLDoc()); - else comp = new InventoryComponent(this); - m_Components.insert(std::make_pair(eReplicaComponentType::INVENTORY, comp)); - } - // if this component exists, then we initialize it. it's value is always 0 - if (compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::ROCKET_LAUNCH_LUP, -1) != -1) { - auto comp = new RocketLaunchLupComponent(this); - m_Components.insert(std::make_pair(eReplicaComponentType::ROCKET_LAUNCH_LUP, comp)); - } - - /** - * This is a bit of a mess - */ - - CDScriptComponentTable* scriptCompTable = CDClientManager::Instance().GetTable(); - int32_t scriptComponentID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::SCRIPT, -1); - - std::string scriptName = ""; - bool client = false; - if (scriptComponentID > 0 || m_Character) { - std::string clientScriptName; - if (!m_Character) { - CDScriptComponent scriptCompData = scriptCompTable->GetByID(scriptComponentID); - scriptName = scriptCompData.script_name; - clientScriptName = scriptCompData.client_script_name; - } else { - scriptName = ""; - } - - if (scriptName != "" || (scriptName == "" && m_Character)) { - - } else if (clientScriptName != "") { - client = true; - } else if (!m_Character) { - client = true; - } - } - - std::string customScriptServer; - bool hasCustomServerScript = false; - - const auto customScriptServerName = GetVarAsString(u"custom_script_server"); - const auto customScriptClientName = GetVarAsString(u"custom_script_client"); - - if (!customScriptServerName.empty()) { - customScriptServer = customScriptServerName; - hasCustomServerScript = true; - } - - if (!customScriptClientName.empty()) { - client = true; - } - - if (hasCustomServerScript && scriptName.empty()) { - scriptName = customScriptServer; - } - - if (!scriptName.empty() || client || m_Character || scriptComponentID >= 0) { - m_Components.insert(std::make_pair(eReplicaComponentType::SCRIPT, new ScriptComponent(this, scriptName, true, client && scriptName.empty()))); - } - - // ZoneControl script - if (m_TemplateID == 2365) { - CDZoneTableTable* zoneTable = CDClientManager::Instance().GetTable(); - const auto zoneID = dZoneManager::Instance()->GetZoneID(); - const CDZoneTable* zoneData = zoneTable->Query(zoneID.GetMapID()); - - if (zoneData != nullptr) { - int zoneScriptID = zoneData->scriptID; - CDScriptComponent zoneScriptData = scriptCompTable->GetByID(zoneScriptID); - - ScriptComponent* comp = new ScriptComponent(this, zoneScriptData.script_name, true); - m_Components.insert(std::make_pair(eReplicaComponentType::SCRIPT, comp)); - } - } - - if (compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::SKILL, -1) != -1 || m_Character) { - SkillComponent* comp = new SkillComponent(this); - m_Components.insert(std::make_pair(eReplicaComponentType::SKILL, comp)); - } - - const auto combatAiId = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::BASE_COMBAT_AI); - if (combatAiId > 0) { - BaseCombatAIComponent* comp = new BaseCombatAIComponent(this, combatAiId); - m_Components.insert(std::make_pair(eReplicaComponentType::BASE_COMBAT_AI, comp)); - } - - if (int componentID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::QUICK_BUILD) > 0) { - RebuildComponent* comp = new RebuildComponent(this); - m_Components.insert(std::make_pair(eReplicaComponentType::QUICK_BUILD, comp)); - - CDRebuildComponentTable* rebCompTable = CDClientManager::Instance().GetTable(); - std::vector rebCompData = rebCompTable->Query([=](CDRebuildComponent entry) { return (entry.id == rebuildComponentID); }); - - if (rebCompData.size() > 0) { - comp->SetResetTime(rebCompData[0].reset_time); - comp->SetCompleteTime(rebCompData[0].complete_time); - comp->SetTakeImagination(rebCompData[0].take_imagination); - comp->SetInterruptible(rebCompData[0].interruptible); - comp->SetSelfActivator(rebCompData[0].self_activator); - comp->SetActivityId(rebCompData[0].activityID); - comp->SetPostImaginationCost(rebCompData[0].post_imagination_cost); - comp->SetTimeBeforeSmash(rebCompData[0].time_before_smash); - - const auto rebuildResetTime = GetVar(u"rebuild_reset_time"); - - if (rebuildResetTime != 0.0f) { - comp->SetResetTime(rebuildResetTime); - - if (m_TemplateID == 9483) // Look away! - { - comp->SetResetTime(comp->GetResetTime() + 25); - } - } - - const auto activityID = GetVar(u"activityID"); - - if (activityID > 0) { - comp->SetActivityId(activityID); - } - - const auto compTime = GetVar(u"compTime"); - - if (compTime > 0) { - comp->SetCompleteTime(compTime); - } - } - } - - if (compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::SWITCH, -1) != -1) { - SwitchComponent* comp = new SwitchComponent(this); - m_Components.insert(std::make_pair(eReplicaComponentType::SWITCH, comp)); - } - - if ((compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::VENDOR) > 0)) { - VendorComponent* comp = new VendorComponent(this); - m_Components.insert(std::make_pair(eReplicaComponentType::VENDOR, comp)); - } - - if (compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::PROPERTY_VENDOR, -1) != -1) { - auto* component = new PropertyVendorComponent(this); - m_Components.insert_or_assign(eReplicaComponentType::PROPERTY_VENDOR, component); - } - - if (compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::PROPERTY_MANAGEMENT, -1) != -1) { - auto* component = new PropertyManagementComponent(this); - m_Components.insert_or_assign(eReplicaComponentType::PROPERTY_MANAGEMENT, component); - } - - if (compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::BOUNCER, -1) != -1) { // you have to determine it like this because all bouncers have a componentID of 0 - BouncerComponent* comp = new BouncerComponent(this); - m_Components.insert(std::make_pair(eReplicaComponentType::BOUNCER, comp)); - } - - int32_t renderComponentId = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::RENDER); - if ((renderComponentId > 0 && m_TemplateID != 2365) || m_Character) { - RenderComponent* render = new RenderComponent(this, renderComponentId); - m_Components.insert(std::make_pair(eReplicaComponentType::RENDER, render)); - } - - if ((compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::MISSION_OFFER) > 0) || m_Character) { - m_Components.insert(std::make_pair(eReplicaComponentType::MISSION_OFFER, new MissionOfferComponent(this, m_TemplateID))); - } - - if (compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::BUILD_BORDER, -1) != -1) { - m_Components.insert(std::make_pair(eReplicaComponentType::BUILD_BORDER, new BuildBorderComponent(this))); - } - - // Scripted activity component - int scriptedActivityID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::SCRIPTED_ACTIVITY); - if ((scriptedActivityID > 0)) { - m_Components.insert(std::make_pair(eReplicaComponentType::SCRIPTED_ACTIVITY, new ScriptedActivityComponent(this, scriptedActivityID))); - } - - if (compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::MODEL, -1) != -1 && !GetComponent()) { - m_Components.insert(std::make_pair(eReplicaComponentType::MODEL, new ModelComponent(this))); - if (m_Components.find(eReplicaComponentType::DESTROYABLE) == m_Components.end()) { - auto destroyableComponent = new DestroyableComponent(this); - destroyableComponent->SetHealth(1); - destroyableComponent->SetMaxHealth(1.0f); - destroyableComponent->SetFaction(-1, true); - destroyableComponent->SetIsSmashable(true); - m_Components.insert(std::make_pair(eReplicaComponentType::DESTROYABLE, destroyableComponent)); - } - } - - PetComponent* petComponent; - if (compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::ITEM) > 0 && !TryGetComponent(eReplicaComponentType::PET, petComponent) && !HasComponent(eReplicaComponentType::MODEL)) { - m_Components.insert(std::make_pair(eReplicaComponentType::ITEM, nullptr)); - } - - // Shooting gallery component - if (compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::SHOOTING_GALLERY) > 0) { - m_Components.insert(std::make_pair(eReplicaComponentType::SHOOTING_GALLERY, new ShootingGalleryComponent(this))); - } - - if (compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::PROPERTY, -1) != -1) { - m_Components.insert(std::make_pair(eReplicaComponentType::PROPERTY, new PropertyComponent(this))); - } - - const int rocketId = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::ROCKET_LAUNCH); - if ((rocketId > 0)) { - m_Components.insert(std::make_pair(eReplicaComponentType::ROCKET_LAUNCH, new RocketLaunchpadControlComponent(this, rocketId))); - } - - const int32_t railComponentID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::RAIL_ACTIVATOR); - if (railComponentID > 0) { - m_Components.insert(std::make_pair(eReplicaComponentType::RAIL_ACTIVATOR, new RailActivatorComponent(this, railComponentID))); - } - - int movementAIID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::MOVEMENT_AI); - if (movementAIID > 0) { - CDMovementAIComponentTable* moveAITable = CDClientManager::Instance().GetTable(); - std::vector moveAIComp = moveAITable->Query([=](CDMovementAIComponent entry) {return (entry.id == movementAIID); }); - - if (moveAIComp.size() > 0) { - MovementAIInfo moveInfo = MovementAIInfo(); - - moveInfo.movementType = moveAIComp[0].MovementType; - moveInfo.wanderChance = moveAIComp[0].WanderChance; - moveInfo.wanderRadius = moveAIComp[0].WanderRadius; - moveInfo.wanderSpeed = moveAIComp[0].WanderSpeed; - moveInfo.wanderDelayMax = moveAIComp[0].WanderDelayMax; - moveInfo.wanderDelayMin = moveAIComp[0].WanderDelayMin; - - bool useWanderDB = GetVar(u"usewanderdb"); - - if (!useWanderDB) { - const auto wanderOverride = GetVarAs(u"wanderRadius"); - - if (wanderOverride != 0.0f) { - moveInfo.wanderRadius = wanderOverride; - } - } - - m_Components.insert(std::make_pair(eReplicaComponentType::MOVEMENT_AI, new MovementAIComponent(this, moveInfo))); - } - } else if (petComponentId > 0 || combatAiId > 0 && GetComponent()->GetTetherSpeed() > 0) { - MovementAIInfo moveInfo = MovementAIInfo(); - moveInfo.movementType = ""; - moveInfo.wanderChance = 0; - moveInfo.wanderRadius = 16; - moveInfo.wanderSpeed = 2.5f; - moveInfo.wanderDelayMax = 5; - moveInfo.wanderDelayMin = 2; - - m_Components.insert(std::make_pair(eReplicaComponentType::MOVEMENT_AI, new MovementAIComponent(this, moveInfo))); - } - - std::string pathName = GetVarAsString(u"attached_path"); - const Path* path = dZoneManager::Instance()->GetZone()->GetPath(pathName); - - //Check to see if we have an attached path and add the appropiate component to handle it: - if (path){ - // if we have a moving platform path, then we need a moving platform component - if (path->pathType == PathType::MovingPlatform) { - MovingPlatformComponent* plat = new MovingPlatformComponent(this, pathName); - m_Components.insert(std::make_pair(eReplicaComponentType::MOVING_PLATFORM, plat)); - // else if we are a movement path - } /*else if (path->pathType == PathType::Movement) { - auto movementAIcomp = GetComponent(); - if (movementAIcomp){ - // TODO: set path in existing movementAIComp - } else { - // TODO: create movementAIcomp and set path - } - }*/ - } else { - // else we still need to setup moving platform if it has a moving platform comp but no path - int32_t movingPlatformComponentId = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::MOVING_PLATFORM, -1); - if (movingPlatformComponentId >= 0) { - MovingPlatformComponent* plat = new MovingPlatformComponent(this, pathName); - m_Components.insert(std::make_pair(eReplicaComponentType::MOVING_PLATFORM, plat)); - } - } - - int proximityMonitorID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::PROXIMITY_MONITOR); - if (proximityMonitorID > 0) { - CDProximityMonitorComponentTable* proxCompTable = CDClientManager::Instance().GetTable(); - std::vector proxCompData = proxCompTable->Query([=](CDProximityMonitorComponent entry) { return (entry.id == proximityMonitorID); }); - if (proxCompData.size() > 0) { - std::vector proximityStr = GeneralUtils::SplitString(proxCompData[0].Proximities, ','); - ProximityMonitorComponent* comp = new ProximityMonitorComponent(this, std::stoi(proximityStr[0]), std::stoi(proximityStr[1])); - m_Components.insert(std::make_pair(eReplicaComponentType::PROXIMITY_MONITOR, comp)); - } - } - - // Hacky way to trigger these when the object has had a chance to get constructed - AddCallbackTimer(0, [this]() { - for (CppScripts::Script* script : CppScripts::GetEntityScripts(this)) { - script->OnStartup(this); - } - }); - - if (!m_Character && EntityManager::Instance()->GetGhostingEnabled()) { - // Don't ghost what is likely large scene elements - if (HasComponent(eReplicaComponentType::SIMPLE_PHYSICS) && HasComponent(eReplicaComponentType::RENDER) && (m_Components.size() == 2 || (HasComponent(eReplicaComponentType::TRIGGER) && m_Components.size() == 3))) { - goto no_ghosting; - } - - /* Filter for ghosting candidates. - * - * Don't ghost moving platforms, until we've got proper syncing for those. - * Don't ghost big phantom physics triggers, as putting those to sleep might prevent interactions. - * Don't ghost property related objects, as the client expects those to always be loaded. - */ - if ( - !EntityManager::IsExcludedFromGhosting(GetLOT()) && - !HasComponent(eReplicaComponentType::SCRIPTED_ACTIVITY) && - !HasComponent(eReplicaComponentType::MOVING_PLATFORM) && - !HasComponent(eReplicaComponentType::PHANTOM_PHYSICS) && - !HasComponent(eReplicaComponentType::PROPERTY) && - !HasComponent(eReplicaComponentType::RACING_CONTROL) && - !HasComponent(eReplicaComponentType::VEHICLE_PHYSICS) - ) - //if (HasComponent(eReplicaComponentType::BASE_COMBAT_AI)) - { - m_IsGhostingCandidate = true; - } - - if (GetLOT() == 6368) { - m_IsGhostingCandidate = true; - } - - // Special case for collectibles in Ninjago - if (HasComponent(eReplicaComponentType::COLLECTIBLE) && Game::server->GetZoneID() == 2000) { - m_IsGhostingCandidate = true; - } - } - -no_ghosting: - - TriggerEvent(eTriggerEventType::CREATE, this); - - if (m_Character) { - auto* controllablePhysicsComponent = GetComponent(); - auto* levelComponent = GetComponent(); - - if (controllablePhysicsComponent && levelComponent) { - controllablePhysicsComponent->SetSpeedMultiplier(levelComponent->GetSpeedBase() / 500.0f); - } - } -} - -bool Entity::operator==(const Entity& other) const { - return other.m_ObjectID == m_ObjectID; -} - -bool Entity::operator!=(const Entity& other) const { - return other.m_ObjectID != m_ObjectID; -} - -User* Entity::GetParentUser() const { - if (!IsPlayer()) { - return nullptr; - } - - return static_cast(this)->GetParentUser(); -} - -Component* Entity::GetComponent(eReplicaComponentType componentID) const { - const auto& index = m_Components.find(componentID); - - if (index == m_Components.end()) { - return nullptr; - } - - return index->second; -} - -bool Entity::HasComponent(const eReplicaComponentType componentId) const { - return m_Components.find(componentId) != m_Components.end(); -} - -void Entity::AddComponent(const eReplicaComponentType componentId, Component* component) { - if (HasComponent(componentId)) { - return; - } - - m_Components.insert_or_assign(componentId, component); -} - -std::vector Entity::GetScriptComponents() { - std::vector comps; - for (std::pair p : m_Components) { - if (p.first == eReplicaComponentType::SCRIPT) { - comps.push_back(static_cast(p.second)); - } - } - - return comps; -} - -void Entity::Subscribe(LWOOBJID scriptObjId, CppScripts::Script* scriptToAdd, const std::string& notificationName) { - if (notificationName == "HitOrHealResult" || notificationName == "Hit") { - auto* destroyableComponent = GetComponent(); - if (!destroyableComponent) return; - destroyableComponent->Subscribe(scriptObjId, scriptToAdd); - } -} - -void Entity::Unsubscribe(LWOOBJID scriptObjId, const std::string& notificationName) { - if (notificationName == "HitOrHealResult" || notificationName == "Hit") { - auto* destroyableComponent = GetComponent(); - if (!destroyableComponent) return; - destroyableComponent->Unsubscribe(scriptObjId); - } -} - -void Entity::SetProximityRadius(float proxRadius, std::string name) { - ProximityMonitorComponent* proxMon = GetComponent(); - if (!proxMon) { - proxMon = new ProximityMonitorComponent(this); - m_Components.insert_or_assign(eReplicaComponentType::PROXIMITY_MONITOR, proxMon); - } - proxMon->SetProximityRadius(proxRadius, name); -} - -void Entity::SetProximityRadius(dpEntity* entity, std::string name) { - ProximityMonitorComponent* proxMon = GetComponent(); - if (!proxMon) { - proxMon = new ProximityMonitorComponent(this); - m_Components.insert_or_assign(eReplicaComponentType::PROXIMITY_MONITOR, proxMon); - } - proxMon->SetProximityRadius(entity, name); -} - -void Entity::SetGMLevel(eGameMasterLevel value) { - m_GMLevel = value; - if (GetParentUser()) { - Character* character = GetParentUser()->GetLastUsedChar(); - - if (character) { - character->SetGMLevel(value); - } - } - - CharacterComponent* character = GetComponent(); - if (character) character->SetGMLevel(value); - - GameMessages::SendGMLevelBroadcast(m_ObjectID, value); -} - -void Entity::WriteBaseReplicaData(RakNet::BitStream* outBitStream, eReplicaPacketType packetType) { - if (packetType == eReplicaPacketType::CONSTRUCTION) { - outBitStream->Write(m_ObjectID); - outBitStream->Write(m_TemplateID); - - if (IsPlayer()) { - std::string name = m_Character != nullptr ? m_Character->GetName() : "Invalid"; - outBitStream->Write(uint8_t(name.size())); - - for (size_t i = 0; i < name.size(); ++i) { - outBitStream->Write(name[i]); - } - } else { - const auto& name = GetVar(u"npcName"); - outBitStream->Write(uint8_t(name.size())); - - for (size_t i = 0; i < name.size(); ++i) { - outBitStream->Write(name[i]); - } - } - - outBitStream->Write(0); //Time since created on server - - const auto& syncLDF = GetVar>(u"syncLDF"); - - // Only sync for models. - if (m_Settings.size() > 0 && (GetComponent() && !GetComponent())) { - outBitStream->Write1(); //ldf data - - RakNet::BitStream settingStream; - settingStream.Write(m_Settings.size()); - - for (LDFBaseData* data : m_Settings) { - if (data) { - data->WriteToPacket(&settingStream); - } - } - - outBitStream->Write(settingStream.GetNumberOfBytesUsed() + 1); - outBitStream->Write(0); //no compression used - outBitStream->Write(settingStream); - } else if (!syncLDF.empty()) { - std::vector ldfData; - - for (const auto& data : syncLDF) { - ldfData.push_back(GetVarData(data)); - } - - outBitStream->Write1(); //ldf data - - RakNet::BitStream settingStream; - settingStream.Write(ldfData.size()); - - for (LDFBaseData* data : ldfData) { - if (data) { - data->WriteToPacket(&settingStream); - } - } - - outBitStream->Write(settingStream.GetNumberOfBytesUsed() + 1); - outBitStream->Write(0); //no compression used - outBitStream->Write(settingStream); - } else { - outBitStream->Write0(); //No ldf data - } - - TriggerComponent* triggerComponent; - if (TryGetComponent(eReplicaComponentType::TRIGGER, triggerComponent)) { - // has trigger component, check to see if we have events to handle - auto* trigger = triggerComponent->GetTrigger(); - outBitStream->Write(trigger && trigger->events.size() > 0); - } else { // no trigger componenet, so definitely no triggers - outBitStream->Write0(); - } - - - if (m_ParentEntity != nullptr || m_SpawnerID != 0) { - outBitStream->Write1(); - if (m_ParentEntity != nullptr) outBitStream->Write(GeneralUtils::SetBit(m_ParentEntity->GetObjectID(), static_cast(eObjectBits::CLIENT))); - else if (m_Spawner != nullptr && m_Spawner->m_Info.isNetwork) outBitStream->Write(m_SpawnerID); - else outBitStream->Write(GeneralUtils::SetBit(m_SpawnerID, static_cast(eObjectBits::CLIENT))); - } else outBitStream->Write0(); - - outBitStream->Write(m_HasSpawnerNodeID); - if (m_HasSpawnerNodeID) outBitStream->Write(m_SpawnerNodeID); - - //outBitStream->Write0(); //Spawner node id - - if (m_Scale == 1.0f || m_Scale == 0.0f) outBitStream->Write0(); - else { - outBitStream->Write1(); - outBitStream->Write(m_Scale); - } - - outBitStream->Write0(); //ObjectWorldState - - if (m_GMLevel != eGameMasterLevel::CIVILIAN) { - outBitStream->Write1(); - outBitStream->Write(m_GMLevel); - } else outBitStream->Write0(); //No GM Level - } - - // Only serialize parent / child info should the info be dirty (changed) or if this is the construction of the entity. - outBitStream->Write(m_IsParentChildDirty || packetType == eReplicaPacketType::CONSTRUCTION); - if (m_IsParentChildDirty || packetType == eReplicaPacketType::CONSTRUCTION) { - m_IsParentChildDirty = false; - outBitStream->Write(m_ParentEntity != nullptr); - if (m_ParentEntity) { - outBitStream->Write(m_ParentEntity->GetObjectID()); - outBitStream->Write0(); - } - outBitStream->Write(m_ChildEntities.size() > 0); - if (m_ChildEntities.size() > 0) { - outBitStream->Write((uint16_t)m_ChildEntities.size()); - for (Entity* child : m_ChildEntities) { - outBitStream->Write((uint64_t)child->GetObjectID()); - } - } - } -} - -void Entity::WriteComponents(RakNet::BitStream* outBitStream, eReplicaPacketType packetType) { - - /** - * This has to be done in a specific order. - */ - - bool destroyableSerialized = false; - bool bIsInitialUpdate = false; - if (packetType == eReplicaPacketType::CONSTRUCTION) bIsInitialUpdate = true; - unsigned int flags = 0; - - PossessableComponent* possessableComponent; - if (TryGetComponent(eReplicaComponentType::POSSESSABLE, possessableComponent)) { - possessableComponent->Serialize(outBitStream, bIsInitialUpdate, flags); - } - - ModuleAssemblyComponent* moduleAssemblyComponent; - if (TryGetComponent(eReplicaComponentType::MODULE_ASSEMBLY, moduleAssemblyComponent)) { - moduleAssemblyComponent->Serialize(outBitStream, bIsInitialUpdate, flags); - } - - ControllablePhysicsComponent* controllablePhysicsComponent; - if (TryGetComponent(eReplicaComponentType::CONTROLLABLE_PHYSICS, controllablePhysicsComponent)) { - controllablePhysicsComponent->Serialize(outBitStream, bIsInitialUpdate, flags); - } - - SimplePhysicsComponent* simplePhysicsComponent; - if (TryGetComponent(eReplicaComponentType::SIMPLE_PHYSICS, simplePhysicsComponent)) { - simplePhysicsComponent->Serialize(outBitStream, bIsInitialUpdate, flags); - } - - RigidbodyPhantomPhysicsComponent* rigidbodyPhantomPhysics; - if (TryGetComponent(eReplicaComponentType::RIGID_BODY_PHANTOM_PHYSICS, rigidbodyPhantomPhysics)) { - rigidbodyPhantomPhysics->Serialize(outBitStream, bIsInitialUpdate, flags); - } - - VehiclePhysicsComponent* vehiclePhysicsComponent; - if (TryGetComponent(eReplicaComponentType::VEHICLE_PHYSICS, vehiclePhysicsComponent)) { - vehiclePhysicsComponent->Serialize(outBitStream, bIsInitialUpdate, flags); - } - - PhantomPhysicsComponent* phantomPhysicsComponent; - if (TryGetComponent(eReplicaComponentType::PHANTOM_PHYSICS, phantomPhysicsComponent)) { - phantomPhysicsComponent->Serialize(outBitStream, bIsInitialUpdate, flags); - } - - SoundTriggerComponent* soundTriggerComponent; - if (TryGetComponent(eReplicaComponentType::SOUND_TRIGGER, soundTriggerComponent)) { - soundTriggerComponent->Serialize(outBitStream, bIsInitialUpdate, flags); - } - - BuffComponent* buffComponent; - if (TryGetComponent(eReplicaComponentType::BUFF, buffComponent)) { - buffComponent->Serialize(outBitStream, bIsInitialUpdate, flags); - - DestroyableComponent* destroyableComponent; - if (TryGetComponent(eReplicaComponentType::DESTROYABLE, destroyableComponent)) { - destroyableComponent->Serialize(outBitStream, bIsInitialUpdate, flags); - } - destroyableSerialized = true; - } - - if (HasComponent(eReplicaComponentType::COLLECTIBLE)) { - DestroyableComponent* destroyableComponent; - if (TryGetComponent(eReplicaComponentType::DESTROYABLE, destroyableComponent) && !destroyableSerialized) { - destroyableComponent->Serialize(outBitStream, bIsInitialUpdate, flags); - } - destroyableSerialized = true; - outBitStream->Write(m_CollectibleID); // Collectable component - } - - PetComponent* petComponent; - if (TryGetComponent(eReplicaComponentType::PET, petComponent)) { - petComponent->Serialize(outBitStream, bIsInitialUpdate, flags); - } - - CharacterComponent* characterComponent; - if (TryGetComponent(eReplicaComponentType::CHARACTER, characterComponent)) { - - PossessorComponent* possessorComponent; - if (TryGetComponent(eReplicaComponentType::POSSESSOR, possessorComponent)) { - possessorComponent->Serialize(outBitStream, bIsInitialUpdate, flags); - } else { - // Should never happen, but just to be safe - outBitStream->Write0(); - } - - LevelProgressionComponent* levelProgressionComponent; - if (TryGetComponent(eReplicaComponentType::LEVEL_PROGRESSION, levelProgressionComponent)) { - levelProgressionComponent->Serialize(outBitStream, bIsInitialUpdate, flags); - } else { - // Should never happen, but just to be safe - outBitStream->Write0(); - } - - PlayerForcedMovementComponent* playerForcedMovementComponent; - if (TryGetComponent(eReplicaComponentType::PLAYER_FORCED_MOVEMENT, playerForcedMovementComponent)) { - playerForcedMovementComponent->Serialize(outBitStream, bIsInitialUpdate, flags); - } else { - // Should never happen, but just to be safe - outBitStream->Write0(); - } - - characterComponent->Serialize(outBitStream, bIsInitialUpdate, flags); - } - - if (HasComponent(eReplicaComponentType::ITEM)) { - outBitStream->Write0(); - } - - InventoryComponent* inventoryComponent; - if (TryGetComponent(eReplicaComponentType::INVENTORY, inventoryComponent)) { - inventoryComponent->Serialize(outBitStream, bIsInitialUpdate, flags); - } - - ScriptComponent* scriptComponent; - if (TryGetComponent(eReplicaComponentType::SCRIPT, scriptComponent)) { - scriptComponent->Serialize(outBitStream, bIsInitialUpdate, flags); - } - - SkillComponent* skillComponent; - if (TryGetComponent(eReplicaComponentType::SKILL, skillComponent)) { - skillComponent->Serialize(outBitStream, bIsInitialUpdate, flags); - } - - BaseCombatAIComponent* baseCombatAiComponent; - if (TryGetComponent(eReplicaComponentType::BASE_COMBAT_AI, baseCombatAiComponent)) { - baseCombatAiComponent->Serialize(outBitStream, bIsInitialUpdate, flags); - } - - RebuildComponent* rebuildComponent; - if (TryGetComponent(eReplicaComponentType::QUICK_BUILD, rebuildComponent)) { - DestroyableComponent* destroyableComponent; - if (TryGetComponent(eReplicaComponentType::DESTROYABLE, destroyableComponent) && !destroyableSerialized) { - destroyableComponent->Serialize(outBitStream, bIsInitialUpdate, flags); - } - destroyableSerialized = true; - rebuildComponent->Serialize(outBitStream, bIsInitialUpdate, flags); - } - - MovingPlatformComponent* movingPlatformComponent; - if (TryGetComponent(eReplicaComponentType::MOVING_PLATFORM, movingPlatformComponent)) { - movingPlatformComponent->Serialize(outBitStream, bIsInitialUpdate, flags); - } - - SwitchComponent* switchComponent; - if (TryGetComponent(eReplicaComponentType::SWITCH, switchComponent)) { - switchComponent->Serialize(outBitStream, bIsInitialUpdate, flags); - } - - VendorComponent* vendorComponent; - if (TryGetComponent(eReplicaComponentType::VENDOR, vendorComponent)) { - vendorComponent->Serialize(outBitStream, bIsInitialUpdate, flags); - } - - BouncerComponent* bouncerComponent; - if (TryGetComponent(eReplicaComponentType::BOUNCER, bouncerComponent)) { - bouncerComponent->Serialize(outBitStream, bIsInitialUpdate, flags); - } - - ScriptedActivityComponent* scriptedActivityComponent; - if (TryGetComponent(eReplicaComponentType::SCRIPTED_ACTIVITY, scriptedActivityComponent)) { - scriptedActivityComponent->Serialize(outBitStream, bIsInitialUpdate, flags); - } - - ShootingGalleryComponent* shootingGalleryComponent; - if (TryGetComponent(eReplicaComponentType::SHOOTING_GALLERY, shootingGalleryComponent)) { - shootingGalleryComponent->Serialize(outBitStream, bIsInitialUpdate, flags); - } - - RacingControlComponent* racingControlComponent; - if (TryGetComponent(eReplicaComponentType::RACING_CONTROL, racingControlComponent)) { - racingControlComponent->Serialize(outBitStream, bIsInitialUpdate, flags); - } - - LUPExhibitComponent* lupExhibitComponent; - if (TryGetComponent(eReplicaComponentType::LUP_EXHIBIT, lupExhibitComponent)) { - lupExhibitComponent->Serialize(outBitStream, bIsInitialUpdate, flags); - } - - ModelComponent* modelComponent; - if (TryGetComponent(eReplicaComponentType::MODEL, modelComponent)) { - modelComponent->Serialize(outBitStream, bIsInitialUpdate, flags); - } - - RenderComponent* renderComponent; - if (TryGetComponent(eReplicaComponentType::RENDER, renderComponent)) { - renderComponent->Serialize(outBitStream, bIsInitialUpdate, flags); - } - - if (modelComponent) { - DestroyableComponent* destroyableComponent; - if (TryGetComponent(eReplicaComponentType::DESTROYABLE, destroyableComponent) && !destroyableSerialized) { - destroyableComponent->Serialize(outBitStream, bIsInitialUpdate, flags); - destroyableSerialized = true; - } - } - - if (HasComponent(eReplicaComponentType::ZONE_CONTROL)) { - outBitStream->Write(0x40000000); - } - - // BBB Component, unused currently - // Need to to write0 so that is serialized correctly - // TODO: Implement BBB Component - outBitStream->Write0(); -} - -void Entity::ResetFlags() { - // Unused -} - -void Entity::UpdateXMLDoc(tinyxml2::XMLDocument* doc) { - //This function should only ever be called from within Character, meaning doc should always exist when this is called. - //Naturally, we don't include any non-player components in this update function. - - for (const auto& pair : m_Components) { - if (pair.second == nullptr) continue; - - pair.second->UpdateXml(doc); - } -} - -void Entity::Update(const float deltaTime) { - uint32_t timerPosition; - timerPosition = 0; - while (timerPosition < m_Timers.size()) { - m_Timers[timerPosition]->Update(deltaTime); - if (m_Timers[timerPosition]->GetTime() <= 0) { - const auto timerName = m_Timers[timerPosition]->GetName(); - - delete m_Timers[timerPosition]; - m_Timers.erase(m_Timers.begin() + timerPosition); - - for (CppScripts::Script* script : CppScripts::GetEntityScripts(this)) { - script->OnTimerDone(this, timerName); - } - TriggerEvent(eTriggerEventType::TIMER_DONE, this); - } else { - timerPosition++; - } - } - - for (int i = 0; i < m_CallbackTimers.size(); i++) { - m_CallbackTimers[i]->Update(deltaTime); - if (m_CallbackTimers[i]->GetTime() <= 0) { - m_CallbackTimers[i]->GetCallback()(); - delete m_CallbackTimers[i]; - m_CallbackTimers.erase(m_CallbackTimers.begin() + i); - } - } - - // Add pending timers to the list of timers so they start next tick. - if (m_PendingTimers.size() > 0) { - for (auto namedTimer : m_PendingTimers) { - m_Timers.push_back(namedTimer); - } - m_PendingTimers.clear(); - } - - if (IsSleeping()) { - Sleep(); - - return; - } else { - Wake(); - } - - for (CppScripts::Script* script : CppScripts::GetEntityScripts(this)) { - script->OnUpdate(this); - } - - for (const auto& pair : m_Components) { - if (pair.second == nullptr) continue; - - pair.second->Update(deltaTime); - } - - if (m_ShouldDestroyAfterUpdate) { - EntityManager::Instance()->DestroyEntity(this->GetObjectID()); - } -} - -void Entity::OnCollisionProximity(LWOOBJID otherEntity, const std::string& proxName, const std::string& status) { - Entity* other = EntityManager::Instance()->GetEntity(otherEntity); - if (!other) return; - - for (CppScripts::Script* script : CppScripts::GetEntityScripts(this)) { - script->OnProximityUpdate(this, other, proxName, status); - } - - RocketLaunchpadControlComponent* rocketComp = GetComponent(); - if (!rocketComp) return; - - rocketComp->OnProximityUpdate(other, proxName, status); -} - -void Entity::OnCollisionPhantom(const LWOOBJID otherEntity) { - auto* other = EntityManager::Instance()->GetEntity(otherEntity); - if (!other) return; - - for (CppScripts::Script* script : CppScripts::GetEntityScripts(this)) { - script->OnCollisionPhantom(this, other); - } - - for (const auto& callback : m_PhantomCollisionCallbacks) { - callback(other); - } - - SwitchComponent* switchComp = GetComponent(); - if (switchComp) { - switchComp->EntityEnter(other); - } - - TriggerEvent(eTriggerEventType::ENTER, other); - - // POI system - const auto& poi = GetVar(u"POI"); - - if (!poi.empty()) { - auto* missionComponent = other->GetComponent(); - - if (missionComponent != nullptr) { - missionComponent->Progress(eMissionTaskType::EXPLORE, 0, 0, GeneralUtils::UTF16ToWTF8(poi)); - } - } - - if (!other->GetIsDead()) { - auto* combat = GetComponent(); - - if (combat != nullptr) { - const auto index = std::find(m_TargetsInPhantom.begin(), m_TargetsInPhantom.end(), otherEntity); - - if (index != m_TargetsInPhantom.end()) return; - - const auto valid = combat->IsEnemy(otherEntity); - - if (!valid) return; - - m_TargetsInPhantom.push_back(otherEntity); - } - } -} - -void Entity::OnCollisionLeavePhantom(const LWOOBJID otherEntity) { - auto* other = EntityManager::Instance()->GetEntity(otherEntity); - if (!other) return; - - for (CppScripts::Script* script : CppScripts::GetEntityScripts(this)) { - script->OnOffCollisionPhantom(this, other); - } - - TriggerEvent(eTriggerEventType::EXIT, other); - - SwitchComponent* switchComp = GetComponent(); - if (switchComp) { - switchComp->EntityLeave(other); - } - - const auto index = std::find(m_TargetsInPhantom.begin(), m_TargetsInPhantom.end(), otherEntity); - - if (index == m_TargetsInPhantom.end()) return; - - m_TargetsInPhantom.erase(index); -} - -void Entity::OnFireEventServerSide(Entity* sender, std::string args, int32_t param1, int32_t param2, int32_t param3) { - for (CppScripts::Script* script : CppScripts::GetEntityScripts(this)) { - script->OnFireEventServerSide(this, sender, args, param1, param2, param3); - } -} - -void Entity::OnActivityStateChangeRequest(LWOOBJID senderID, int32_t value1, int32_t value2, const std::u16string& stringValue) { - for (CppScripts::Script* script : CppScripts::GetEntityScripts(this)) { - script->OnActivityStateChangeRequest(this, senderID, value1, value2, stringValue); - } -} - -void Entity::OnCinematicUpdate(Entity* self, Entity* sender, eCinematicEvent event, const std::u16string& pathName, - float_t pathTime, float_t totalTime, int32_t waypoint) { - for (CppScripts::Script* script : CppScripts::GetEntityScripts(this)) { - script->OnCinematicUpdate(self, sender, event, pathName, pathTime, totalTime, waypoint); - } -} - -void Entity::NotifyObject(Entity* sender, const std::string& name, int32_t param1, int32_t param2) { - GameMessages::SendNotifyObject(GetObjectID(), sender->GetObjectID(), GeneralUtils::ASCIIToUTF16(name), UNASSIGNED_SYSTEM_ADDRESS); - - for (CppScripts::Script* script : CppScripts::GetEntityScripts(this)) { - script->OnNotifyObject(this, sender, name, param1, param2); - } -} - -void Entity::OnEmoteReceived(const int32_t emote, Entity* target) { - for (auto* script : CppScripts::GetEntityScripts(this)) { - script->OnEmoteReceived(this, emote, target); - } -} - -void Entity::OnUse(Entity* originator) { - TriggerEvent(eTriggerEventType::INTERACT, originator); - - for (CppScripts::Script* script : CppScripts::GetEntityScripts(this)) { - script->OnUse(this, originator); - } - - // component base class when - - for (const auto& pair : m_Components) { - if (pair.second == nullptr) continue; - - pair.second->OnUse(originator); - } -} - -void Entity::OnHitOrHealResult(Entity* attacker, int32_t damage) { - for (CppScripts::Script* script : CppScripts::GetEntityScripts(this)) { - script->OnHitOrHealResult(this, attacker, damage); - } -} - -void Entity::OnHit(Entity* attacker) { - TriggerEvent(eTriggerEventType::HIT, attacker); - for (CppScripts::Script* script : CppScripts::GetEntityScripts(this)) { - script->OnHit(this, attacker); - } -} - -void Entity::OnZonePropertyEditBegin() { - for (CppScripts::Script* script : CppScripts::GetEntityScripts(this)) { - script->OnZonePropertyEditBegin(this); - } -} - -void Entity::OnZonePropertyEditEnd() { - for (CppScripts::Script* script : CppScripts::GetEntityScripts(this)) { - script->OnZonePropertyEditEnd(this); - } -} - -void Entity::OnZonePropertyModelEquipped() { - for (CppScripts::Script* script : CppScripts::GetEntityScripts(this)) { - script->OnZonePropertyModelEquipped(this); - } -} - -void Entity::OnZonePropertyModelPlaced(Entity* player) { - for (CppScripts::Script* script : CppScripts::GetEntityScripts(this)) { - script->OnZonePropertyModelPlaced(this, player); - } -} - -void Entity::OnZonePropertyModelPickedUp(Entity* player) { - for (CppScripts::Script* script : CppScripts::GetEntityScripts(this)) { - script->OnZonePropertyModelPickedUp(this, player); - } -} - -void Entity::OnZonePropertyModelRemoved(Entity* player) { - for (CppScripts::Script* script : CppScripts::GetEntityScripts(this)) { - script->OnZonePropertyModelRemoved(this, player); - } -} - -void Entity::OnZonePropertyModelRemovedWhileEquipped(Entity* player) { - for (CppScripts::Script* script : CppScripts::GetEntityScripts(this)) { - script->OnZonePropertyModelRemovedWhileEquipped(this, player); - } -} - -void Entity::OnZonePropertyModelRotated(Entity* player) { - for (CppScripts::Script* script : CppScripts::GetEntityScripts(this)) { - script->OnZonePropertyModelRotated(this, player); - } -} - -void Entity::OnMessageBoxResponse(Entity* sender, int32_t button, const std::u16string& identifier, const std::u16string& userData) { - for (CppScripts::Script* script : CppScripts::GetEntityScripts(this)) { - script->OnMessageBoxResponse(this, sender, button, identifier, userData); - } -} - -void Entity::OnChoiceBoxResponse(Entity* sender, int32_t button, const std::u16string& buttonIdentifier, const std::u16string& identifier) { - for (CppScripts::Script* script : CppScripts::GetEntityScripts(this)) { - script->OnChoiceBoxResponse(this, sender, button, buttonIdentifier, identifier); - } -} - -void Entity::RequestActivityExit(Entity* sender, LWOOBJID player, bool canceled) { - for (CppScripts::Script* script : CppScripts::GetEntityScripts(this)) { - script->OnRequestActivityExit(sender, player, canceled); - } -} - -void Entity::Smash(const LWOOBJID source, const eKillType killType, const std::u16string& deathType) { - if (!m_PlayerIsReadyForUpdates) return; - - auto* destroyableComponent = GetComponent(); - if (destroyableComponent == nullptr) { - Kill(EntityManager::Instance()->GetEntity(source)); - return; - } - auto* possessorComponent = GetComponent(); - if (possessorComponent) { - if (possessorComponent->GetPossessable() != LWOOBJID_EMPTY) { - auto* mount = EntityManager::Instance()->GetEntity(possessorComponent->GetPossessable()); - if (mount) possessorComponent->Dismount(mount, true); - } - } - - destroyableComponent->Smash(source, killType, deathType); -} - -void Entity::Kill(Entity* murderer) { - if (!m_PlayerIsReadyForUpdates) return; - - for (const auto& cb : m_DieCallbacks) { - cb(); - } - - m_DieCallbacks.clear(); - - //OMAI WA MOU, SHINDERIU - - for (CppScripts::Script* script : CppScripts::GetEntityScripts(this)) { - script->OnDie(this, murderer); - } - - if (m_Spawner != nullptr) { - m_Spawner->NotifyOfEntityDeath(m_ObjectID); - } - - if (!IsPlayer()) { - EntityManager::Instance()->DestroyEntity(this); - } - - const auto& grpNameQBShowBricks = GetVar(u"grpNameQBShowBricks"); - - if (!grpNameQBShowBricks.empty()) { - auto spawners = dZoneManager::Instance()->GetSpawnersByName(grpNameQBShowBricks); - - Spawner* spawner = nullptr; - - if (!spawners.empty()) { - spawner = spawners[0]; - } else { - spawners = dZoneManager::Instance()->GetSpawnersInGroup(grpNameQBShowBricks); - - if (!spawners.empty()) { - spawner = spawners[0]; - } - } - - if (spawner != nullptr) { - spawner->Spawn(); - } - } - - // Track a player being smashed - auto* characterComponent = GetComponent(); - if (characterComponent != nullptr) { - characterComponent->UpdatePlayerStatistic(TimesSmashed); - } - - // Track a player smashing something else - if (murderer != nullptr) { - auto* murdererCharacterComponent = murderer->GetComponent(); - if (murdererCharacterComponent != nullptr) { - murdererCharacterComponent->UpdatePlayerStatistic(SmashablesSmashed); - } - } -} - -void Entity::AddDieCallback(const std::function& callback) { - m_DieCallbacks.push_back(callback); -} - -void Entity::AddCollisionPhantomCallback(const std::function& callback) { - m_PhantomCollisionCallbacks.push_back(callback); -} - -void Entity::AddRebuildCompleteCallback(const std::function& callback) const { - auto* rebuildComponent = GetComponent(); - if (rebuildComponent != nullptr) { - rebuildComponent->AddRebuildCompleteCallback(callback); - } -} - -bool Entity::GetIsDead() const { - DestroyableComponent* dest = GetComponent(); - if (dest && dest->GetArmor() == 0 && dest->GetHealth() == 0) return true; - - return false; -} - -void Entity::AddLootItem(const Loot::Info& info) { - if (!IsPlayer()) return; - auto& droppedLoot = static_cast(this)->GetDroppedLoot(); - droppedLoot.insert(std::make_pair(info.id, info)); -} - -void Entity::PickupItem(const LWOOBJID& objectID) { - if (!IsPlayer()) return; - InventoryComponent* inv = GetComponent(); - if (!inv) return; - - CDObjectsTable* objectsTable = CDClientManager::Instance().GetTable(); - - auto& droppedLoot = static_cast(this)->GetDroppedLoot(); - - for (const auto& p : droppedLoot) { - if (p.first == objectID) { - auto* characterComponent = GetComponent(); - if (characterComponent != nullptr) { - characterComponent->TrackLOTCollection(p.second.lot); - } - - const CDObjects& object = objectsTable->GetByID(p.second.lot); - if (object.id != 0 && object.type == "Powerup") { - CDObjectSkillsTable* skillsTable = CDClientManager::Instance().GetTable(); - std::vector skills = skillsTable->Query([=](CDObjectSkills entry) {return (entry.objectTemplate == p.second.lot); }); - for (CDObjectSkills skill : skills) { - CDSkillBehaviorTable* skillBehTable = CDClientManager::Instance().GetTable(); - CDSkillBehavior behaviorData = skillBehTable->GetSkillByID(skill.skillID); - - SkillComponent::HandleUnmanaged(behaviorData.behaviorID, GetObjectID()); - - auto* missionComponent = GetComponent(); - - if (missionComponent != nullptr) { - missionComponent->Progress(eMissionTaskType::POWERUP, skill.skillID); - } - } - } else { - inv->AddItem(p.second.lot, p.second.count, eLootSourceType::PICKUP, eInventoryType::INVALID, {}, LWOOBJID_EMPTY, true, false, LWOOBJID_EMPTY, eInventoryType::INVALID, 1); - } - } - } - - droppedLoot.erase(objectID); -} - -bool Entity::CanPickupCoins(uint64_t count) { - if (!IsPlayer()) return false; - auto* player = static_cast(this); - auto droppedCoins = player->GetDroppedCoins(); - if (count > droppedCoins) { - return false; - } else { - player->SetDroppedCoins(droppedCoins - count); - return true; - } -} - -void Entity::RegisterCoinDrop(uint64_t count) { - if (!IsPlayer()) return; - auto* player = static_cast(this); - auto droppedCoins = player->GetDroppedCoins(); - droppedCoins += count; - player->SetDroppedCoins(droppedCoins); -} - -void Entity::AddChild(Entity* child) { - m_IsParentChildDirty = true; - m_ChildEntities.push_back(child); -} - -void Entity::RemoveChild(Entity* child) { - if (!child) return; - uint32_t entityPosition = 0; - while (entityPosition < m_ChildEntities.size()) { - if (!m_ChildEntities[entityPosition] || (m_ChildEntities[entityPosition])->GetObjectID() == child->GetObjectID()) { - m_IsParentChildDirty = true; - m_ChildEntities.erase(m_ChildEntities.begin() + entityPosition); - } else { - entityPosition++; - } - } -} - -void Entity::RemoveParent() { - this->m_ParentEntity = nullptr; -} - -void Entity::AddTimer(std::string name, float time) { - EntityTimer* timer = new EntityTimer(name, time); - m_PendingTimers.push_back(timer); -} - -void Entity::AddCallbackTimer(float time, std::function callback) { - EntityCallbackTimer* timer = new EntityCallbackTimer(time, callback); - m_CallbackTimers.push_back(timer); -} - -bool Entity::HasTimer(const std::string& name) { - for (auto* timer : m_Timers) { - if (timer->GetName() == name) { - return true; - } - } - - return false; -} - -void Entity::CancelCallbackTimers() { - for (auto* callback : m_CallbackTimers) { - delete callback; - } - - m_CallbackTimers.clear(); -} - -void Entity::ScheduleKillAfterUpdate(Entity* murderer) { - //if (m_Info.spawner) m_Info.spawner->ScheduleKill(this); - EntityManager::Instance()->ScheduleForKill(this); - - if (murderer) m_ScheduleKiller = murderer; -} - -void Entity::CancelTimer(const std::string& name) { - for (int i = 0; i < m_Timers.size(); i++) { - if (m_Timers[i]->GetName() == name) { - delete m_Timers[i]; - m_Timers.erase(m_Timers.begin() + i); - return; - } - } -} - -void Entity::CancelAllTimers() { - /*for (auto timer : m_Timers) { - if (timer) delete timer; - }*/ - - for (auto* timer : m_Timers) { - delete timer; - } - - m_Timers.clear(); - - for (auto* callBackTimer : m_CallbackTimers) { - delete callBackTimer; - } - - m_CallbackTimers.clear(); -} - -bool Entity::IsPlayer() const { - return m_TemplateID == 1 && GetSystemAddress() != UNASSIGNED_SYSTEM_ADDRESS; -} - -void Entity::TriggerEvent(eTriggerEventType event, Entity* optionalTarget) { - auto* triggerComponent = GetComponent(); - if (triggerComponent) triggerComponent->TriggerEvent(event, optionalTarget); -} - -Entity* Entity::GetOwner() const { - if (m_OwnerOverride != LWOOBJID_EMPTY) { - auto* other = EntityManager::Instance()->GetEntity(m_OwnerOverride); - - if (other != nullptr) { - return other->GetOwner(); - } - } - - return const_cast(this); -} - -const NiPoint3& Entity::GetDefaultPosition() const { - return m_DefaultPosition; -} - -const NiQuaternion& Entity::GetDefaultRotation() const { - return m_DefaultRotation; -} - -float Entity::GetDefaultScale() const { - return m_Scale; -} - -void Entity::SetOwnerOverride(const LWOOBJID value) { - m_OwnerOverride = value; -} - -bool Entity::GetIsGhostingCandidate() const { - return m_IsGhostingCandidate; -} - -int8_t Entity::GetObservers() const { - return m_Observers; -} - -void Entity::SetObservers(int8_t value) { - if (value < 0) { - value = 0; - } - - m_Observers = value; -} - -void Entity::Sleep() { - auto* baseCombatAIComponent = GetComponent(); - - if (baseCombatAIComponent != nullptr) { - baseCombatAIComponent->Sleep(); - } -} - -void Entity::Wake() { - auto* baseCombatAIComponent = GetComponent(); - - if (baseCombatAIComponent != nullptr) { - baseCombatAIComponent->Wake(); - } -} - -bool Entity::IsSleeping() const { - return m_IsGhostingCandidate && m_Observers == 0; -} - - -const NiPoint3& Entity::GetPosition() const { - auto* controllable = GetComponent(); - - if (controllable != nullptr) { - return controllable->GetPosition(); - } - - auto* phantom = GetComponent(); - - if (phantom != nullptr) { - return phantom->GetPosition(); - } - - auto* simple = GetComponent(); - - if (simple != nullptr) { - return simple->GetPosition(); - } - - auto* vehicel = GetComponent(); - - if (vehicel != nullptr) { - return vehicel->GetPosition(); - } - - return NiPoint3::ZERO; -} - -const NiQuaternion& Entity::GetRotation() const { - auto* controllable = GetComponent(); - - if (controllable != nullptr) { - return controllable->GetRotation(); - } - - auto* phantom = GetComponent(); - - if (phantom != nullptr) { - return phantom->GetRotation(); - } - - auto* simple = GetComponent(); - - if (simple != nullptr) { - return simple->GetRotation(); - } - - auto* vehicel = GetComponent(); - - if (vehicel != nullptr) { - return vehicel->GetRotation(); - } - - return NiQuaternion::IDENTITY; -} - -void Entity::SetPosition(NiPoint3 position) { - auto* controllable = GetComponent(); - - if (controllable != nullptr) { - controllable->SetPosition(position); - } - - auto* phantom = GetComponent(); - - if (phantom != nullptr) { - phantom->SetPosition(position); - } - - auto* simple = GetComponent(); - - if (simple != nullptr) { - simple->SetPosition(position); - } - - auto* vehicel = GetComponent(); - - if (vehicel != nullptr) { - vehicel->SetPosition(position); - } - - EntityManager::Instance()->SerializeEntity(this); -} - -void Entity::SetRotation(NiQuaternion rotation) { - auto* controllable = GetComponent(); - - if (controllable != nullptr) { - controllable->SetRotation(rotation); - } - - auto* phantom = GetComponent(); - - if (phantom != nullptr) { - phantom->SetRotation(rotation); - } - - auto* simple = GetComponent(); - - if (simple != nullptr) { - simple->SetRotation(rotation); - } - - auto* vehicel = GetComponent(); - - if (vehicel != nullptr) { - vehicel->SetRotation(rotation); - } - - EntityManager::Instance()->SerializeEntity(this); -} - -bool Entity::GetBoolean(const std::u16string& name) const { - return GetVar(name); -} - -int32_t Entity::GetI32(const std::u16string& name) const { - return GetVar(name); -} - -int64_t Entity::GetI64(const std::u16string& name) const { - return GetVar(name); -} - -void Entity::SetBoolean(const std::u16string& name, const bool value) { - SetVar(name, value); -} - -void Entity::SetI32(const std::u16string& name, const int32_t value) { - SetVar(name, value); -} - -void Entity::SetI64(const std::u16string& name, const int64_t value) { - SetVar(name, value); -} - -bool Entity::HasVar(const std::u16string& name) const { - for (auto* data : m_Settings) { - if (data->GetKey() == name) { - return true; - } - } - - return false; -} - -uint16_t Entity::GetNetworkId() const { - return m_NetworkID; -} - -void Entity::SetNetworkId(const uint16_t id) { - m_NetworkID = id; -} - -std::vector& Entity::GetTargetsInPhantom() { - std::vector valid; - - // Clean up invalid targets, like disconnected players - for (auto i = 0u; i < m_TargetsInPhantom.size(); ++i) { - const auto id = m_TargetsInPhantom.at(i); - - auto* entity = EntityManager::Instance()->GetEntity(id); - - if (entity == nullptr) { - continue; - } - - valid.push_back(id); - } - - m_TargetsInPhantom = valid; - - return m_TargetsInPhantom; -} - -void Entity::SendNetworkVar(const std::string& data, const SystemAddress& sysAddr) { - GameMessages::SendSetNetworkScriptVar(this, sysAddr, data); -} - -LDFBaseData* Entity::GetVarData(const std::u16string& name) const { - for (auto* data : m_Settings) { - if (data == nullptr) { - continue; - } - - if (data->GetKey() != name) { - continue; - } - - return data; - } - - return nullptr; -} - -std::string Entity::GetVarAsString(const std::u16string& name) const { - auto* data = GetVarData(name); - - if (data == nullptr) { - return ""; - } - - return data->GetValueAsString(); -} - -void Entity::Resurrect() { - if (IsPlayer()) { - GameMessages::SendResurrect(this); - } -} - -void Entity::AddToGroup(const std::string& group) { - if (std::find(m_Groups.begin(), m_Groups.end(), group) == m_Groups.end()) { - m_Groups.push_back(group); - } -} - -void Entity::RetroactiveVaultSize() { - auto inventoryComponent = GetComponent(); - if (!inventoryComponent) return; - - auto itemsVault = inventoryComponent->GetInventory(eInventoryType::VAULT_ITEMS); - auto modelVault = inventoryComponent->GetInventory(eInventoryType::VAULT_MODELS); - - if (itemsVault->GetSize() == modelVault->GetSize()) return; - - modelVault->SetSize(itemsVault->GetSize()); -} diff --git a/dGame/Entity.h b/dGame/Entity.h deleted file mode 100644 index 90f2a34fb..000000000 --- a/dGame/Entity.h +++ /dev/null @@ -1,503 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include -#include - -#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& GetGroups() { return m_Groups; }; - - Spawner* GetSpawner() const { return m_Spawner; } - - LWOOBJID GetSpawnerID() const { return m_SpawnerID; } - - const std::vector& GetSettings() const { return m_Settings; } - - const std::vector& 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 - T* GetComponent() const; - - template - bool TryGetComponent(eReplicaComponentType componentId, T*& component) const; - - bool HasComponent(eReplicaComponentType componentId) const; - - void AddComponent(eReplicaComponentType componentId, Component* component); - - std::vector 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 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& 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& callback) const; - void AddCollisionPhantomCallback(const std::function& callback); - void AddDieCallback(const std::function& 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 - const T& GetVar(const std::u16string& name) const; - - template - void SetVar(const std::u16string& name, T value); - - void SendNetworkVar(const std::string& data, const SystemAddress& sysAddr); - - template - void SetNetworkVar(const std::u16string& name, T value, const SystemAddress& sysAddr = UNASSIGNED_SYSTEM_ADDRESS); - - template - void SetNetworkVar(const std::u16string& name, std::vector value, const SystemAddress& sysAddr = UNASSIGNED_SYSTEM_ADDRESS); - - template - T GetNetworkVar(const std::u16string& name); - - /** - * Get the LDF value and cast it as T. - */ - template - 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& GetTargetsInPhantom(); - - Entity* GetScheduledKiller() { return m_ScheduleKiller; } - -protected: - LWOOBJID m_ObjectID; - - LOT m_TemplateID; - - std::vector m_Settings; - std::vector 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 m_ChildEntities; - eGameMasterLevel m_GMLevel; - uint16_t m_CollectibleID; - std::vector m_Groups; - uint16_t m_NetworkID; - std::vector> m_DieCallbacks; - std::vector> m_PhantomCollisionCallbacks; - - std::unordered_map m_Components; - std::vector m_Timers; - std::vector m_PendingTimers; - std::vector 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 m_TargetsInPhantom; -}; - -/** - * Template definitions. - */ - -template -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(index->second); - - return true; -} - -template -T* Entity::GetComponent() const { - return dynamic_cast(GetComponent(T::ComponentType)); -} - - -template -const T& Entity::GetVar(const std::u16string& name) const { - auto* data = GetVarData(name); - - if (data == nullptr) { - return LDFData::Default; - } - - auto* typed = dynamic_cast*>(data); - - if (typed == nullptr) { - return LDFData::Default; - } - - return typed->GetValue(); -} - -template -T Entity::GetVarAs(const std::u16string& name) const { - const auto data = GetVarAsString(name); - - T value; - - if (!GeneralUtils::TryParse(data, value)) { - return LDFData::Default; - } - - return value; -} - -template -void Entity::SetVar(const std::u16string& name, T value) { - auto* data = GetVarData(name); - - if (data == nullptr) { - auto* data = new LDFData(name, value); - - m_Settings.push_back(data); - - return; - } - - auto* typed = dynamic_cast*>(data); - - if (typed == nullptr) { - return; - } - - typed->SetValue(value); -} - -template -void Entity::SetNetworkVar(const std::u16string& name, T value, const SystemAddress& sysAddr) { - LDFData* newData = nullptr; - - for (auto* data : m_NetworkSettings) { - if (data->GetKey() != name) - continue; - - newData = dynamic_cast*>(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(name, value); - } - - m_NetworkSettings.push_back(newData); - SendNetworkVar(newData->GetString(true), sysAddr); -} - -template -void Entity::SetNetworkVar(const std::u16string& name, std::vector values, const SystemAddress& sysAddr) { - std::stringstream updates; - auto index = 1; - - for (const auto& value : values) { - LDFData* newData = nullptr; - const auto& indexedName = name + u"." + GeneralUtils::to_u16string(index); - - for (auto* data : m_NetworkSettings) { - if (data->GetKey() != indexedName) - continue; - - newData = dynamic_cast*>(data); - newData->SetValue(value); - break; - } - - if (newData == nullptr) { - newData = new LDFData(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 -T Entity::GetNetworkVar(const std::u16string& name) { - for (auto* data : m_NetworkSettings) { - if (data == nullptr || data->GetKey() != name) - continue; - - auto* typed = dynamic_cast*>(data); - if (typed == nullptr) - continue; - - return typed->GetValue(); - } - - return LDFData::Default; -} diff --git a/dGame/EntityManager.cpp b/dGame/EntityManager.cpp index 0fc859bd7..18602c896 100644 --- a/dGame/EntityManager.cpp +++ b/dGame/EntityManager.cpp @@ -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(); + if (collectibleComponent) { + uint32_t collectionId = collectibleComponent->GetCollectibleId(); - if (collectionId != 0) { - collectionId = static_cast(collectionId) + static_cast(Game::server->GetZoneID() << 8); + if (collectionId != 0) { + collectionId = static_cast(collectionId) + static_cast(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(); + auto* switchComp = entity->GetComponent(); if (switchComp) { entity->TriggerEvent(eTriggerEventType::DEACTIVATED, entity); } diff --git a/dGame/Player.cpp b/dGame/Player.cpp index 2e194e6af..9ffa17751 100644 --- a/dGame/Player.cpp +++ b/dGame/Player.cpp @@ -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::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 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); } } } diff --git a/dGame/Player.h b/dGame/Player.h index 287ee613c..d3e9a1468 100644 --- a/dGame/Player.h +++ b/dGame/Player.h @@ -20,7 +20,7 @@ class Player final : public Entity User* GetParentUser() const override; - SystemAddress GetSystemAddress() const override; + const SystemAddress GetSystemAddress() const override { return m_SystemAddress; } NiPoint3 GetRespawnPosition() const override; @@ -36,7 +36,7 @@ class Player final : public Entity std::map& GetDroppedLoot(); - uint64_t GetDroppedCoins(); + uint64_t GetDroppedCoins() const; /** * Setters @@ -44,9 +44,9 @@ class Player final : public Entity 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); diff --git a/dGame/TradingManager.cpp b/dGame/TradingManager.cpp index 281c003d1..9984633b4 100644 --- a/dGame/TradingManager.cpp +++ b/dGame/TradingManager.cpp @@ -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; diff --git a/dGame/dBehaviors/ApplyBuffBehavior.cpp b/dGame/dBehaviors/ApplyBuffBehavior.cpp index 35b0f269d..5b4b6ae24 100644 --- a/dGame/dBehaviors/ApplyBuffBehavior.cpp +++ b/dGame/dBehaviors/ApplyBuffBehavior.cpp @@ -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); diff --git a/dGame/dBehaviors/AreaOfEffectBehavior.cpp b/dGame/dBehaviors/AreaOfEffectBehavior.cpp index dedede2ad..3e74f9ec0 100644 --- a/dGame/dBehaviors/AreaOfEffectBehavior.cpp +++ b/dGame/dBehaviors/AreaOfEffectBehavior.cpp @@ -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" diff --git a/dGame/dBehaviors/BasicAttackBehavior.cpp b/dGame/dBehaviors/BasicAttackBehavior.cpp index f86937953..d0f5ae907 100644 --- a/dGame/dBehaviors/BasicAttackBehavior.cpp +++ b/dGame/dBehaviors/BasicAttackBehavior.cpp @@ -162,7 +162,7 @@ void BasicAttackBehavior::DoBehaviorCalculation(BehaviorContext* context, RakNet } auto* destroyableComponent = targetEntity->GetComponent(); - 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); diff --git a/dGame/dBehaviors/BehaviorContext.cpp b/dGame/dBehaviors/BehaviorContext.cpp index c7db42087..9a91a1853 100644 --- a/dGame/dBehaviors/BehaviorContext.cpp +++ b/dGame/dBehaviors/BehaviorContext.cpp @@ -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 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(); + if (!destroyableComponent) { return targets; } diff --git a/dGame/dBehaviors/HealBehavior.cpp b/dGame/dBehaviors/HealBehavior.cpp index 66fe2c791..d5eebae9a 100644 --- a/dGame/dBehaviors/HealBehavior.cpp +++ b/dGame/dBehaviors/HealBehavior.cpp @@ -16,7 +16,7 @@ void HealBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bit_strea return; } - auto* destroyable = static_cast(entity->GetComponent(eReplicaComponentType::DESTROYABLE)); + auto* destroyable = entity->GetComponent(); if (destroyable == nullptr) { Game::logger->Log("HealBehavior", "Failed to find destroyable component for %(llu)!", branch.target); diff --git a/dGame/dBehaviors/LootBuffBehavior.cpp b/dGame/dBehaviors/LootBuffBehavior.cpp index 6e5634fc6..d5ae88cdc 100644 --- a/dGame/dBehaviors/LootBuffBehavior.cpp +++ b/dGame/dBehaviors/LootBuffBehavior.cpp @@ -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(); + auto* controllablePhysicsComponent = target->GetComponent(); 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(); + auto* controllablePhysicsComponent = target->GetComponent(); if (!controllablePhysicsComponent) return; controllablePhysicsComponent->RemovePickupRadiusScale(m_Scale); diff --git a/dGame/dBehaviors/RepairBehavior.cpp b/dGame/dBehaviors/RepairBehavior.cpp index ce2e5fd20..2467d2a03 100644 --- a/dGame/dBehaviors/RepairBehavior.cpp +++ b/dGame/dBehaviors/RepairBehavior.cpp @@ -16,7 +16,7 @@ void RepairBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bit_str return; } - auto* destroyable = static_cast(entity->GetComponent(eReplicaComponentType::DESTROYABLE)); + auto* destroyable = entity->GetComponent(); if (destroyable == nullptr) { Game::logger->Log("RepairBehavior", "Failed to find destroyable component for %(llu)!", branch.target); diff --git a/dGame/dBehaviors/SkillEventBehavior.cpp b/dGame/dBehaviors/SkillEventBehavior.cpp index 837d70c93..77efbf2f0 100644 --- a/dGame/dBehaviors/SkillEventBehavior.cpp +++ b/dGame/dBehaviors/SkillEventBehavior.cpp @@ -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); } } diff --git a/dGame/dBehaviors/SpawnBehavior.cpp b/dGame/dBehaviors/SpawnBehavior.cpp index 75c84f6c5..3ee7a7cde 100644 --- a/dGame/dBehaviors/SpawnBehavior.cpp +++ b/dGame/dBehaviors/SpawnBehavior.cpp @@ -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(); + auto* quickBuildComponent = entity->GetComponent(); - 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(entity->GetComponent(eReplicaComponentType::DESTROYABLE)); + auto* destroyable = entity->GetComponent(); - if (destroyable == nullptr) { + if (!destroyable) { entity->Smash(context->originator); return; diff --git a/dGame/dBehaviors/StunBehavior.cpp b/dGame/dBehaviors/StunBehavior.cpp index 4e34d3a23..bbe175339 100644 --- a/dGame/dBehaviors/StunBehavior.cpp +++ b/dGame/dBehaviors/StunBehavior.cpp @@ -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(target->GetComponent(eReplicaComponentType::BASE_COMBAT_AI)); + auto* combatAiComponent = target->GetComponent(); 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(self->GetComponent(eReplicaComponentType::BASE_COMBAT_AI)); + auto* combatAiComponent = self->GetComponent(); 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(target->GetComponent(eReplicaComponentType::BASE_COMBAT_AI)); + auto* combatAiComponent = target->GetComponent(); if (combatAiComponent == nullptr) { return; diff --git a/dGame/dBehaviors/TacArcBehavior.cpp b/dGame/dBehaviors/TacArcBehavior.cpp index 91df3879c..57c6644a5 100644 --- a/dGame/dBehaviors/TacArcBehavior.cpp +++ b/dGame/dBehaviors/TacArcBehavior.cpp @@ -6,7 +6,7 @@ #include "BehaviorContext.h" #include "BaseCombatAIComponent.h" #include "EntityManager.h" -#include "RebuildComponent.h" +#include "QuickBuildComponent.h" #include "DestroyableComponent.h" #include @@ -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(); diff --git a/dGame/dBehaviors/VentureVisionBehavior.cpp b/dGame/dBehaviors/VentureVisionBehavior.cpp index 93feb8e9a..1fdf3ff34 100644 --- a/dGame/dBehaviors/VentureVisionBehavior.cpp +++ b/dGame/dBehaviors/VentureVisionBehavior.cpp @@ -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(); + auto* characterComponent = targetEntity->GetComponent(); 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(); + auto* characterComponent = targetEntity->GetComponent(); if (characterComponent) { if (m_show_collectibles) characterComponent->RemoveVentureVisionEffect(m_ShowCollectibles); diff --git a/dGame/dComponents/AchievementCacheKey.h b/dGame/dComponents/AchievementCacheKey.h index 398e02319..2e0b3628e 100644 --- a/dGame/dComponents/AchievementCacheKey.h +++ b/dGame/dComponents/AchievementCacheKey.h @@ -1,8 +1,9 @@ -#include "eMissionTaskType.h" - #ifndef __ACHIEVEMENTCACHEKEY__H__ #define __ACHIEVEMENTCACHEKEY__H__ +#include "eMissionTaskType.h" +#include "GeneralUtils.h" + class AchievementCacheKey { public: AchievementCacheKey() { diff --git a/dGame/dComponents/AchievementVendorComponent.cpp b/dGame/dComponents/AchievementVendorComponent.cpp new file mode 100644 index 000000000..65042b154 --- /dev/null +++ b/dGame/dComponents/AchievementVendorComponent.cpp @@ -0,0 +1,5 @@ +#include "AchievementVendorComponent.h" + +AchievementVendorComponent::AchievementVendorComponent(Entity* parent) : VendorComponent(parent) { + +} diff --git a/dGame/dComponents/AchievementVendorComponent.h b/dGame/dComponents/AchievementVendorComponent.h new file mode 100644 index 000000000..cfd42a664 --- /dev/null +++ b/dGame/dComponents/AchievementVendorComponent.h @@ -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__ diff --git a/dGame/dComponents/ActivityComponent.cpp b/dGame/dComponents/ActivityComponent.cpp new file mode 100644 index 000000000..f4cdf9819 --- /dev/null +++ b/dGame/dComponents/ActivityComponent.cpp @@ -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 +#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(); + std::vector activities = activitiesTable->Query([=](CDActivities entry) {return (entry.ActivityID == m_ActivityID); }); + + for (CDActivities activity : activities) { + m_ActivityInfo = activity; + if (static_cast(activity.leaderboardType) == LeaderboardType::Racing && Game::config->GetValue("solo_racing") == "1") { + m_ActivityInfo.minTeamSize = 1; + m_ActivityInfo.minTeams = 1; + } + + const auto& transferOverride = parent->GetVar(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(); + + if (destroyableComponent) { + // check for LMIs and set the loot LMIs + CDActivityRewardsTable* activityRewardsTable = CDClientManager::Instance().GetTable(); + std::vector 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 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(m_ActivityPlayers.size()); + + if (!m_ActivityPlayers.empty()) { + for (const auto& activityPlayer : m_ActivityPlayers) { + + outBitStream->Write(activityPlayer->playerID); + for (const auto& activityValue : activityPlayer->values) { + outBitStream->Write(activityValue); + } + } + } +} + +void ActivityComponent::ReloadConfig() { + CDActivitiesTable* activitiesTable = CDClientManager::Instance().GetTable(); + std::vector 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 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->CompleteMission(229); + } + + ChatPackets::SendSystemMessage(player->GetSystemAddress(), u"Sorry, this activity is not ready."); + static_cast(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(); + 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& lobby) const { + for (LobbyPlayer* player : lobby) { + auto* entity = player->GetEntity(); + if (entity == nullptr || !TakeCost(entity)) { + continue; + } + + instance->AddParticipant(entity); + } +} + +const std::vector& 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(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(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(); + if (missionComponent) { + missionComponent->Progress(eMissionTaskType::ACTIVITY, m_ActivityInfo.ActivityID); + } + + // First, get the activity data + auto* activityRewardsTable = CDClientManager::Instance().GetTable(); + std::vector 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(); + std::vector 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 ActivityInstance::GetParticipants() const { + std::vector 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); +} diff --git a/dGame/dComponents/ActivityComponent.h b/dGame/dComponents/ActivityComponent.h new file mode 100644 index 000000000..1164df997 --- /dev/null +++ b/dGame/dComponents/ActivityComponent.h @@ -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 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 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 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& 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& 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 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 m_Instances; + + /** + * The current lobbies for this activity + */ + std::vector m_Queue; + + /** + * All the activity score for the players in this activity + */ + std::vector m_ActivityPlayers; + + /** + * LMIs for team sizes + */ + std::unordered_map m_ActivityLootMatrices; + + /** + * The activity id + * + */ + int32_t m_ActivityID; +}; + +#endif // ACTIVITYCOMPONENT_H diff --git a/dGame/dComponents/BaseCombatAIComponent.cpp b/dGame/dComponents/BaseCombatAIComponent.cpp index cccaad230..a74650464 100644 --- a/dGame/dComponents/BaseCombatAIComponent.cpp +++ b/dGame/dComponents/BaseCombatAIComponent.cpp @@ -1,6 +1,10 @@ #include "BaseCombatAIComponent.h" -#include +#include +#include +#include + +#include "BitStream.h" #include "Entity.h" #include "EntityManager.h" #include "ControllablePhysicsComponent.h" @@ -15,32 +19,32 @@ #include "CDClientManager.h" #include "DestroyableComponent.h" -#include -#include -#include - #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(u"aggroRadius"); - m_AggroRadius = aggroRadius != 0 ? aggroRadius : m_AggroRadius; - auto tetherRadius = m_Parent->GetVar(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(); } +} - Stun(1.0f); +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(u"aggroRadius"); + m_AggroRadius = aggroRadius != 0 ? aggroRadius : m_AggroRadius; + auto tetherRadius = m_ParentEntity->GetVar(u"tetherRadius"); + m_HardTetherRadius = tetherRadius != 0 ? tetherRadius : m_HardTetherRadius; + } +} - /* - * Add physics - */ +void BaseCombatAIComponent::Startup() { + Stun(1.0f); + // Add physics int32_t collisionGroup = (COLLISION_GROUP_DYNAMIC | COLLISION_GROUP_ENEMY); - CDComponentsRegistryTable* componentRegistryTable = CDClientManager::Instance().GetTable(); - auto componentID = componentRegistryTable->GetByIDAndType(parent->GetLOT(), eReplicaComponentType::CONTROLLABLE_PHYSICS); + auto* componentRegistryTable = CDClientManager::Instance().GetTable(); + if (!componentRegistryTable) return; - CDPhysicsComponentTable* physicsComponentTable = CDClientManager::Instance().GetTable(); + 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(); + + 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(); - - if (m_MovementAI == nullptr) { - return; + if (!m_MovementAI) { + m_MovementAI = m_ParentEntity->GetComponent(); + 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(); + auto* rebuild = m_ParentEntity->GetComponent(); if (rebuild != nullptr) { const auto state = rebuild->GetState(); @@ -253,11 +253,9 @@ void BaseCombatAIComponent::CalculateCombat(const float deltaTime) { } } - auto* skillComponent = m_Parent->GetComponent(); + auto* skillComponent = m_ParentEntity->GetComponent(); - 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(); + auto* destroyableComponent = m_ParentEntity->GetComponent(); 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 BaseCombatAIComponent::GetTargetWithinAggroRange() const { std::vector 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 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(); - if (destroyable == nullptr) { - return false; - } + if (!destroyable) return false; - auto* referenceDestroyable = m_Parent->GetComponent(); + auto* referenceDestroyable = m_ParentEntity->GetComponent(); - 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(); + auto* quickbuild = entity->GetComponent(); - 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; - } - - m_Parent->SetRotation(NiQuaternion::LookAt(m_Parent->GetPosition(), point)); -} - -void BaseCombatAIComponent::SetDisabled(bool value) { - m_Disabled = value; -} + if (m_Stunned) return; -bool BaseCombatAIComponent::GetDistabled() const { - return m_Disabled; + m_ParentEntity->SetRotation(NiQuaternion::LookAt(m_ParentEntity->GetPosition(), point)); } void BaseCombatAIComponent::Sleep() { diff --git a/dGame/dComponents/BaseCombatAIComponent.h b/dGame/dComponents/BaseCombatAIComponent.h index 8bf6140ab..ed883ebdb 100644 --- a/dGame/dComponents/BaseCombatAIComponent.h +++ b/dGame/dComponents/BaseCombatAIComponent.h @@ -10,6 +10,7 @@ #include "Component.h" #include "eReplicaComponentType.h" +#include #include #include @@ -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 @@ class BaseCombatAIComponent : public Component { * 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 @@ class BaseCombatAIComponent : public Component { * 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 @@ class BaseCombatAIComponent : public Component { * (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 @@ class BaseCombatAIComponent : public Component { * 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 diff --git a/dGame/dComponents/BouncerComponent.cpp b/dGame/dComponents/BouncerComponent.cpp index f6a532615..b9fd4fed4 100644 --- a/dGame/dComponents/BouncerComponent.cpp +++ b/dGame/dComponents/BouncerComponent.cpp @@ -6,66 +6,50 @@ #include "Game.h" #include "dLogger.h" #include "GameMessages.h" -#include +#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(); - 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(); + }); } diff --git a/dGame/dComponents/BouncerComponent.h b/dGame/dComponents/BouncerComponent.h index 15665cc1f..cce65eb41 100644 --- a/dGame/dComponents/BouncerComponent.h +++ b/dGame/dComponents/BouncerComponent.h @@ -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 @@ class BouncerComponent : public Component { 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 diff --git a/dGame/dComponents/BuffComponent.cpp b/dGame/dComponents/BuffComponent.cpp index 68b5182c7..b048fa610 100644 --- a/dGame/dComponents/BuffComponent.cpp +++ b/dGame/dComponents/BuffComponent.cpp @@ -14,20 +14,12 @@ std::unordered_map> 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(m_Buffs.size()); + outBitStream->Write(!m_Buffs.empty()); + if (!m_Buffs.empty()) { + outBitStream->Write(m_Buffs.size()); for (const auto& buff : m_Buffs) { outBitStream->Write(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(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(); for (const auto& parameter : parameters) { if (parameter.name == "overtime") { - auto* behaviorTemplateTable = CDClientManager::Instance().GetTable(); - behaviorID = behaviorTemplateTable->GetSkillByID(parameter.values[0]).behaviorID; - stacks = static_cast(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(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(); + auto* destroyable = this->GetParentEntity()->GetComponent(); 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(); + auto* destroyable = this->GetParentEntity()->GetComponent(); 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(); + auto* destroyable = this->GetParentEntity()->GetComponent(); if (destroyable == nullptr) return; destroyable->SetMaxImagination(destroyable->GetMaxImagination() + maxImagination); } else if (parameter.name == "speed") { - auto* controllablePhysicsComponent = this->GetParent()->GetComponent(); + auto* controllablePhysicsComponent = this->GetParentEntity()->GetComponent(); 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(); + auto* destroyable = this->GetParentEntity()->GetComponent(); - 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(); + auto* destroyable = this->GetParentEntity()->GetComponent(); - 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(); + auto* destroyable = this->GetParentEntity()->GetComponent(); - if (destroyable == nullptr) return; + if (!destroyable) return; destroyable->SetMaxImagination(destroyable->GetMaxImagination() - maxImagination); } else if (parameter.name == "speed") { - auto* controllablePhysicsComponent = this->GetParent()->GetComponent(); + auto* controllablePhysicsComponent = this->GetParentEntity()->GetComponent(); 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& 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& 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); } } diff --git a/dGame/dComponents/BuffComponent.h b/dGame/dComponents/BuffComponent.h index d91758835..c3f9066b6 100644 --- a/dGame/dComponents/BuffComponent.h +++ b/dGame/dComponents/BuffComponent.h @@ -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 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 @@ class BuffComponent : public Component { * @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 @@ class BuffComponent : public Component { /** * 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 diff --git a/dGame/dComponents/BuildBorderComponent.cpp b/dGame/dComponents/BuildBorderComponent.cpp index f9ead9e4d..217d2b786 100644 --- a/dGame/dComponents/BuildBorderComponent.cpp +++ b/dGame/dComponents/BuildBorderComponent.cpp @@ -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"); - - auto buildArea = m_Parent->GetObjectID(); - - if (!entities.empty()) { - buildArea = entities[0]->GetObjectID(); + if (!originator->GetCharacter()) return; - Game::logger->Log("BuildBorderComponent", "Using PropertyPlaque"); - } + const auto& entities = EntityManager::Instance()->GetEntitiesInGroup("PropertyPlaque"); - auto* inventoryComponent = originator->GetComponent(); + auto buildArea = entities.empty() ? m_ParentEntity->GetObjectID() : entities.front()->GetObjectID(); - if (inventoryComponent == nullptr) { - return; - } + auto* inventoryComponent = originator->GetComponent(); - auto* thinkingHat = inventoryComponent->FindItemByLot(6086); + if (!inventoryComponent) return; - if (thinkingHat == nullptr) { - return; - } + auto* thinkingHat = inventoryComponent->FindItemByLot(LOT_THINKING_CAP); - inventoryComponent->PushEquippedItems(); + if (!thinkingHat) return; - Game::logger->Log("BuildBorderComponent", "Starting with %llu", buildArea); + Game::logger->Log("BuildBorderComponent", "Using BuildArea %llu for player %llu", buildArea, originator->GetObjectID()); - 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->PushEquippedItems(); - InventoryComponent* inv = m_Parent->GetComponent(); - 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()); } } diff --git a/dGame/dComponents/BuildBorderComponent.h b/dGame/dComponents/BuildBorderComponent.h index dc5afc8ad..49693a217 100644 --- a/dGame/dComponents/BuildBorderComponent.h +++ b/dGame/dComponents/BuildBorderComponent.h @@ -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 diff --git a/dGame/dComponents/CMakeLists.txt b/dGame/dComponents/CMakeLists.txt index b396829ab..88e84506c 100644 --- a/dGame/dComponents/CMakeLists.txt +++ b/dGame/dComponents/CMakeLists.txt @@ -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) diff --git a/dGame/dComponents/CharacterComponent.cpp b/dGame/dComponents/CharacterComponent.cpp index 32fe564c7..37ea6fad2 100644 --- a/dGame/dComponents/CharacterComponent.cpp +++ b/dGame/dComponents/CharacterComponent.cpp @@ -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(static_cast(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(distance)); } else { - UpdatePlayerStatistic(MetersTraveled, (uint64_t)distance); + UpdatePlayerStatistic(MetersTraveled, static_cast(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 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); } diff --git a/dGame/dComponents/CharacterComponent.h b/dGame/dComponents/CharacterComponent.h index e5ca6da5d..fc8f8e3ba 100644 --- a/dGame/dComponents/CharacterComponent.h +++ b/dGame/dComponents/CharacterComponent.h @@ -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; diff --git a/dGame/dComponents/CollectibleComponent.cpp b/dGame/dComponents/CollectibleComponent.cpp new file mode 100644 index 000000000..781d8d984 --- /dev/null +++ b/dGame/dComponents/CollectibleComponent.cpp @@ -0,0 +1,7 @@ +#include "CollectibleComponent.h" + +#include "Entity.h" + +void CollectibleComponent::Startup() { + m_CollectibleId = GetParentEntity()->GetVarAs(u"collectible_id"); +} diff --git a/dGame/dComponents/CollectibleComponent.h b/dGame/dComponents/CollectibleComponent.h new file mode 100644 index 000000000..e5cf42359 --- /dev/null +++ b/dGame/dComponents/CollectibleComponent.h @@ -0,0 +1,22 @@ +#ifndef __COLLECTIBLECOMPONENT__H__ +#define __COLLECTIBLECOMPONENT__H__ + +#include "Component.h" +#include "eReplicaComponentType.h" + +#include + +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__ diff --git a/dGame/dComponents/Component.cpp b/dGame/dComponents/Component.cpp index ca018c29c..4b73b1bf3 100644 --- a/dGame/dComponents/Component.cpp +++ b/dGame/dComponents/Component.cpp @@ -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; } diff --git a/dGame/dComponents/Component.h b/dGame/dComponents/Component.h index 9b0df9fdf..57359c93b 100644 --- a/dGame/dComponents/Component.h +++ b/dGame/dComponents/Component.h @@ -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; }; diff --git a/dGame/dComponents/ComponentHeirachy.md b/dGame/dComponents/ComponentHeirachy.md new file mode 100644 index 000000000..920971081 --- /dev/null +++ b/dGame/dComponents/ComponentHeirachy.md @@ -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 + +``` diff --git a/dGame/dComponents/ControllablePhysicsComponent.cpp b/dGame/dComponents/ControllablePhysicsComponent.cpp index d1b442001..d70d979ba 100644 --- a/dGame/dComponents/ControllablePhysicsComponent.cpp +++ b/dGame/dComponents/ControllablePhysicsComponent.cpp @@ -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(); - - 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); - + m_ParentEntity->GetCharacter()->LoadXmlRespawnCheckpoints(); + + 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(); + auto* levelProgressionComponent = m_ParentEntity->GetComponent(); 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, diff --git a/dGame/dComponents/ControllablePhysicsComponent.h b/dGame/dComponents/ControllablePhysicsComponent.h index 470a7af44..f77c73694 100644 --- a/dGame/dComponents/ControllablePhysicsComponent.h +++ b/dGame/dComponents/ControllablePhysicsComponent.h @@ -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 @@ class ControllablePhysicsComponent : public Component { * 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 diff --git a/dGame/dComponents/DestroyableComponent.cpp b/dGame/dComponents/DestroyableComponent.cpp index db8a20131..e9a2cab25 100644 --- a/dGame/dComponents/DestroyableComponent.cpp +++ b/dGame/dComponents/DestroyableComponent.cpp @@ -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::LoadConfigData() { + SetIsSmashable(GetIsSmashable() | (m_ParentEntity->GetVarAs(u"is_smashable") != 0)); +} +void DestroyableComponent::LoadTemplateData() { + if (m_ParentEntity->IsPlayer()) return; + auto* destroyableComponentTable = CDClientManager::Instance().GetTable(); + auto destroyableDataLookup = destroyableComponentTable->Query([this](CDDestructibleComponent entry) { return (entry.id == this->m_ComponentId); }); + if (m_ComponentId == -1 || destroyableDataLookup.empty()) { + SetHealth(1); + SetArmor(0); -void DestroyableComponent::Reinitialize(LOT templateID) { - CDComponentsRegistryTable* compRegistryTable = CDClientManager::Instance().GetTable(); + SetMaxHealth(1); + SetMaxArmor(0); - 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); + SetIsSmashable(true); + AddFaction(-1); + AddFaction(6); //Smashables - int32_t componentID = 0; - if (collectibleComponentID > 0) componentID = collectibleComponentID; - if (rebuildComponentID > 0) componentID = rebuildComponentID; - if (buffComponentID > 0) componentID = buffComponentID; + // 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; + } - CDDestructibleComponentTable* destCompTable = CDClientManager::Instance().GetTable(); - std::vector destCompData = destCompTable->Query([=](CDDestructibleComponent entry) { return (entry.id == componentID); }); + auto destroyableData = destroyableDataLookup.at(0); + if (m_ParentEntity->HasComponent(eReplicaComponentType::RACING_STATS)) { + destroyableData.imagination = 60; + } - if (componentID > 0) { - std::vector destCompData = destCompTable->Query([=](CDDestructibleComponent entry) { return (entry.id == componentID); }); + SetHealth(destroyableData.life); + SetImagination(destroyableData.imagination); + SetArmor(destroyableData.armor); - if (destCompData.size() > 0) { - SetHealth(destCompData[0].life); - SetImagination(destCompData[0].imagination); - SetArmor(destCompData[0].armor); + SetMaxHealth(destroyableData.life); + SetMaxImagination(destroyableData.imagination); + SetMaxArmor(destroyableData.armor); - SetMaxHealth(destCompData[0].life); - SetMaxImagination(destCompData[0].imagination); - SetMaxArmor(destCompData[0].armor); + SetIsSmashable(destroyableData.isSmashable); - SetIsSmashable(destCompData[0].isSmashable); - } - } else { - SetHealth(1); - SetImagination(0); - SetArmor(0); + SetLootMatrixID(destroyableData.LootMatrixIndex); - SetMaxHealth(1); - SetMaxImagination(0); - SetMaxArmor(0); + // Now get currency information + uint32_t npcMinLevel = destroyableData.level; + uint32_t currencyIndex = destroyableData.CurrencyIndex; - SetIsSmashable(true); + auto* currencyTable = CDClientManager::Instance().GetTable(); + 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(); + auto* buffComponent = m_ParentEntity->GetComponent(); if (buffComponent != nullptr) { buffComponent->LoadFromXml(doc); @@ -207,7 +231,7 @@ void DestroyableComponent::UpdateXml(tinyxml2::XMLDocument* doc) { return; } - auto* buffComponent = m_Parent->GetComponent(); + auto* buffComponent = m_ParentEntity->GetComponent(); 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(); + auto* characterComponent = m_ParentEntity->GetComponent(); 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(); + auto* characterComponent = m_ParentEntity->GetComponent(); if (characterComponent != nullptr) { characterComponent->TrackArmorDelta(value - m_iArmor); } m_iArmor = value; - auto* inventroyComponent = m_Parent->GetComponent(); + auto* inventroyComponent = m_ParentEntity->GetComponent(); 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(); + auto* characterComponent = m_ParentEntity->GetComponent(); if (characterComponent != nullptr) { characterComponent->TrackImaginationDelta(value - m_iImagination); } m_iImagination = value; - auto* inventroyComponent = m_Parent->GetComponent(); + auto* inventroyComponent = m_ParentEntity->GetComponent(); 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(); - auto* inventoryComponent = m_Parent->GetComponent(); + auto* characterComponent = m_ParentEntity->GetComponent(); + auto* inventoryComponent = m_ParentEntity->GetComponent(); 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(); + auto* targetQuickbuild = targetEntity->GetComponent(); 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(); + auto* possessable = m_ParentEntity->GetComponent(); if (possessable && possessable->GetDepossessOnHit()) { possessable->Dismount(); } // Dismount on the possessor hit - auto possessor = m_Parent->GetComponent(); + auto* possessor = m_ParentEntity->GetComponent(); 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(); + auto* combatComponent = m_ParentEntity->GetComponent(); 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() != nullptr; + const auto isEnemy = m_ParentEntity->GetComponent() != nullptr; auto* inventoryComponent = owner->GetComponent(); if (inventoryComponent != nullptr && isEnemy) { - inventoryComponent->TriggerPassiveAbility(PassiveAbilityTrigger::EnemySmashed, m_Parent); + inventoryComponent->TriggerPassiveAbility(PassiveAbilityTrigger::EnemySmashed, m_ParentEntity); } auto* missions = owner->GetComponent(); @@ -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() != nullptr) { + if (team != nullptr && m_ParentEntity->GetComponent() != nullptr) { LWOOBJID specificOwner = LWOOBJID_EMPTY; - auto* scriptedActivityComponent = m_Parent->GetComponent(); + auto* scriptedActivityComponent = m_ParentEntity->GetComponent(); 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 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& 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(); + auto* character = m_ParentEntity->GetComponent(); 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(); + auto* inventory = m_ParentEntity->GetComponent(); 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); } } } diff --git a/dGame/dComponents/DestroyableComponent.h b/dGame/dComponents/DestroyableComponent.h index 5e5133b73..8c4d1127b 100644 --- a/dGame/dComponents/DestroyableComponent.h +++ b/dGame/dComponents/DestroyableComponent.h @@ -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 @@ class DestroyableComponent : public Component { */ 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 @@ class DestroyableComponent : public Component { std::vector> m_OnHitCallbacks; /** - * The list of scripts subscribed to this components actions + * Scripts that are subscribed to this component */ - std::map m_SubscribedScripts; + std::vector m_SubscribedScripts; /** * status immunity counters diff --git a/dGame/dComponents/DonationVendorComponent.cpp b/dGame/dComponents/DonationVendorComponent.cpp new file mode 100644 index 000000000..f65d77e04 --- /dev/null +++ b/dGame/dComponents/DonationVendorComponent.cpp @@ -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(u"activityID"); + VendorComponent::LoadConfigData(); +} diff --git a/dGame/dComponents/DonationVendorComponent.h b/dGame/dComponents/DonationVendorComponent.h new file mode 100644 index 000000000..22832f14a --- /dev/null +++ b/dGame/dComponents/DonationVendorComponent.h @@ -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__ diff --git a/dGame/dComponents/GateRushComponent.cpp b/dGame/dComponents/GateRushComponent.cpp new file mode 100644 index 000000000..79e09e897 --- /dev/null +++ b/dGame/dComponents/GateRushComponent.cpp @@ -0,0 +1,5 @@ +#include "GateRushComponent.h" + +GateRushComponent::GateRushComponent(Entity* parent, int32_t componentId) : RacingControlComponent(parent, componentId) { + +} diff --git a/dGame/dComponents/GateRushComponent.h b/dGame/dComponents/GateRushComponent.h new file mode 100644 index 000000000..baf967656 --- /dev/null +++ b/dGame/dComponents/GateRushComponent.h @@ -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__ diff --git a/dGame/dComponents/VehiclePhysicsComponent.cpp b/dGame/dComponents/HavokVehiclePhysicsComponent.cpp similarity index 66% rename from dGame/dComponents/VehiclePhysicsComponent.cpp rename to dGame/dComponents/HavokVehiclePhysicsComponent.cpp index 58bf7ebb4..bc5c5b95f 100644 --- a/dGame/dComponents/VehiclePhysicsComponent.cpp +++ b/dGame/dComponents/HavokVehiclePhysicsComponent.cpp @@ -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(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; - } -} diff --git a/dGame/dComponents/VehiclePhysicsComponent.h b/dGame/dComponents/HavokVehiclePhysicsComponent.h similarity index 86% rename from dGame/dComponents/VehiclePhysicsComponent.h rename to dGame/dComponents/HavokVehiclePhysicsComponent.h index e314bef14..dfaa10b00 100644 --- a/dGame/dComponents/VehiclePhysicsComponent.h +++ b/dGame/dComponents/HavokVehiclePhysicsComponent.h @@ -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 @@ class VehiclePhysicsComponent : public Component { */ 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: diff --git a/dGame/dComponents/InventoryComponent.cpp b/dGame/dComponents/InventoryComponent.cpp index b0471736e..2f65c62c5 100644 --- a/dGame/dComponents/InventoryComponent.cpp +++ b/dGame/dComponents/InventoryComponent.cpp @@ -16,10 +16,10 @@ #include "ItemSet.h" #include "Player.h" #include "PetComponent.h" -#include "PossessorComponent.h" +#include "PossessionComponent.h" #include "PossessableComponent.h" #include "ModuleAssemblyComponent.h" -#include "VehiclePhysicsComponent.h" +#include "HavokVehiclePhysicsComponent.h" #include "CharacterComponent.h" #include "dZoneManager.h" #include "PropertyManagementComponent.h" @@ -40,14 +40,14 @@ InventoryComponent::InventoryComponent(Entity* parent, tinyxml2::XMLDocument* document) : Component(parent) { this->m_Dirty = true; - this->m_Equipped = {}; - this->m_Pushed = {}; + this->m_Equipped.clear(); + this->m_Pushed.clear(); this->m_Consumable = LOT_NULL; - this->m_Pets = {}; + this->m_Pets.clear(); const auto lot = parent->GetLOT(); - if (lot == 1) { + if (m_ParentEntity->IsPlayer()) { LoadXml(document); CheckProxyIntegrity(); @@ -59,7 +59,7 @@ InventoryComponent::InventoryComponent(Entity* parent, tinyxml2::XMLDocument* do const auto componentId = compRegistryTable->GetByIDAndType(lot, eReplicaComponentType::INVENTORY); auto* inventoryComponentTable = CDClientManager::Instance().GetTable(); - auto items = inventoryComponentTable->Query([=](const CDInventoryComponent entry) { return entry.id == componentId; }); + auto items = inventoryComponentTable->Query([&componentId](const CDInventoryComponent entry) { return entry.id == componentId; }); auto slot = 0u; @@ -72,25 +72,28 @@ InventoryComponent::InventoryComponent(Entity* parent, tinyxml2::XMLDocument* do const auto& info = Inventory::FindItemComponent(item.itemid); - UpdateSlot(info.equipLocation, { id, static_cast(item.itemid), item.count, slot++ }); + UpdateSlot(info.equipLocation, EquippedItem(id, static_cast(item.itemid), item.count, slot++)); // Equip this items proxies. auto subItems = info.subItems; subItems.erase(std::remove_if(subItems.begin(), subItems.end(), ::isspace), subItems.end()); - if (!subItems.empty()) { - const auto subItemsSplit = GeneralUtils::SplitString(subItems, ','); + if (subItems.empty()) continue; + const auto subItemsSplit = GeneralUtils::SplitString(subItems, ','); - for (auto proxyLotAsString : subItemsSplit) { - const auto proxyLOT = static_cast(std::stoi(proxyLotAsString)); + for (const auto& proxyLotAsString : subItemsSplit) { + LOT proxyLot; + if (GeneralUtils::TryParse(proxyLotAsString, proxyLot)) { + Game::logger->Log("InventoryComponent", "Failed to parse %s to lot for entity %i:%llu", proxyLotAsString.c_str(), m_ParentEntity->GetLOT(), m_ParentEntity->GetObjectID()); + continue; + } - const auto& proxyInfo = Inventory::FindItemComponent(proxyLOT); - const LWOOBJID proxyId = ObjectIDManager::Instance()->GenerateObjectID(); + const auto& proxyInfo = Inventory::FindItemComponent(proxyLot); + const LWOOBJID proxyId = ObjectIDManager::Instance()->GenerateObjectID(); - // Use item.count since we equip item.count number of the item this is a requested proxy of - UpdateSlot(proxyInfo.equipLocation, { proxyId, proxyLOT, item.count, slot++ }); - } + // Use item.count since we equip item.count number of the item this is a requested proxy of + UpdateSlot(proxyInfo.equipLocation, EquippedItem(proxyId, proxyLot, item.count, slot++)); } } } @@ -98,9 +101,7 @@ InventoryComponent::InventoryComponent(Entity* parent, tinyxml2::XMLDocument* do Inventory* InventoryComponent::GetInventory(const eInventoryType type) { const auto index = m_Inventories.find(type); - if (index != m_Inventories.end()) { - return index->second; - } + if (index != m_Inventories.end()) return index->second; // Create new empty inventory uint32_t size = 240u; @@ -120,22 +121,18 @@ Inventory* InventoryComponent::GetInventory(const eInventoryType type) { break; } - auto* inventory = new Inventory(type, size, {}, this); + auto* inventory = new Inventory(type, size, std::vector(), this); m_Inventories.insert_or_assign(type, inventory); return inventory; } -const std::map& InventoryComponent::GetInventories() const { - return m_Inventories; -} - uint32_t InventoryComponent::GetLotCount(const LOT lot) const { uint32_t count = 0; - for (const auto& inventory : m_Inventories) { - count += inventory.second->GetLotCount(lot); + for (const auto& [inventoryId, inventory] : m_Inventories) { + count += inventory->GetLotCount(lot); } return count; @@ -144,19 +141,15 @@ uint32_t InventoryComponent::GetLotCount(const LOT lot) const { uint32_t InventoryComponent::GetLotCountNonTransfer(LOT lot) const { uint32_t count = 0; - for (const auto& inventory : m_Inventories) { - if (IsTransferInventory(inventory.second->GetType())) continue; + for (const auto [inventoryId, inventory] : m_Inventories) { + if (IsTransferInventory(inventory->GetType())) continue; - count += inventory.second->GetLotCount(lot); + count += inventory->GetLotCount(lot); } return count; } -const EquipmentMap& InventoryComponent::GetEquippedItems() const { - return m_Equipped; -} - void InventoryComponent::AddItem( const LOT lot, const uint32_t count, @@ -172,46 +165,43 @@ void InventoryComponent::AddItem( const bool bound, int32_t preferredSlot) { if (count == 0) { - Game::logger->Log("InventoryComponent", "Attempted to add 0 of item (%i) to the inventory!", lot); - + Game::logger->Log("InventoryComponent", "Attempted to add 0 of item (%i) to the inventory of %i:%llu!", lot, m_ParentEntity->GetLOT(), m_ParentEntity->GetObjectID()); return; } if (!Inventory::IsValidItem(lot)) { if (lot > 0) { - Game::logger->Log("InventoryComponent", "Attempted to add invalid item (%i) to the inventory!", lot); + Game::logger->Log("InventoryComponent", "Attempted to add invalid item (%i) to the inventory of %i:%llu!", lot, m_ParentEntity->GetLOT(), m_ParentEntity->GetObjectID()); } - return; } - if (inventoryType == INVALID) { - inventoryType = Inventory::FindInventoryTypeForLot(lot); - } + if (inventoryType == INVALID) inventoryType = Inventory::FindInventoryTypeForLot(lot); - auto* missions = static_cast(this->m_Parent->GetComponent(eReplicaComponentType::MISSION)); + auto* missionComponent = m_ParentEntity->GetComponent(); auto* inventory = GetInventory(inventoryType); + // Config items cant stack as they are unique per item. if (!config.empty() || bound) { const auto slot = preferredSlot != -1 && inventory->IsSlotEmpty(preferredSlot) ? preferredSlot : inventory->FindEmptySlot(); if (slot == -1) { - Game::logger->Log("InventoryComponent", "Failed to find empty slot for inventory (%i)!", inventoryType); + Game::logger->Log("InventoryComponent", "Failed to find empty slot for inventory (%i) of %i:%llu!", inventoryType, m_ParentEntity->GetLOT(), m_ParentEntity->GetObjectID()); return; } auto* item = new Item(lot, inventory, slot, count, config, parent, showFlyingLoot, isModMoveAndEquip, subKey, bound, lootSourceType); - if (missions != nullptr && !IsTransferInventory(inventoryType)) { - missions->Progress(eMissionTaskType::GATHER, lot, LWOOBJID_EMPTY, "", count, IsTransferInventory(inventorySourceType)); + if (missionComponent && !IsTransferInventory(inventoryType)) { + missionComponent->Progress(eMissionTaskType::GATHER, lot, LWOOBJID_EMPTY, "", count, IsTransferInventory(inventorySourceType)); } return; } - const auto info = Inventory::FindItemComponent(lot); + const auto& info = Inventory::FindItemComponent(lot); auto left = count; @@ -219,7 +209,7 @@ void InventoryComponent::AddItem( auto stack = static_cast(info.stackSize); - bool isBrick = inventoryType == eInventoryType::BRICKS || (stack == 0 && info.itemType == 1); + bool isBrick = inventoryType == eInventoryType::BRICKS || (stack == 0 && info.itemType == eItemType::BRICK); // info.itemType of 1 is item type brick if (isBrick) { @@ -230,7 +220,7 @@ void InventoryComponent::AddItem( auto* existing = FindItemByLot(lot, inventoryType); - if (existing != nullptr) { + if (existing) { const auto delta = std::min(left, stack - existing->GetCount()); left -= delta; @@ -261,11 +251,9 @@ void InventoryComponent::AddItem( } if (slot == -1) { - auto* player = dynamic_cast(GetParent()); + auto* player = dynamic_cast(GetParentEntity()); - if (player == nullptr) { - return; - } + if (!player) return; outOfSpace += size; @@ -276,7 +264,7 @@ void InventoryComponent::AddItem( case 1: for (size_t i = 0; i < size; i++) { - GameMessages::SendDropClientLoot(this->m_Parent, this->m_Parent->GetObjectID(), lot, 0, this->m_Parent->GetPosition(), 1); + GameMessages::SendDropClientLoot(this->m_ParentEntity, this->m_ParentEntity->GetObjectID(), lot, 0, this->m_ParentEntity->GetPosition(), 1); } break; @@ -292,14 +280,14 @@ void InventoryComponent::AddItem( isModMoveAndEquip = false; } - if (missions != nullptr && !IsTransferInventory(inventoryType)) { - missions->Progress(eMissionTaskType::GATHER, lot, LWOOBJID_EMPTY, "", count - outOfSpace, IsTransferInventory(inventorySourceType)); - } + if (!missionComponent || IsTransferInventory(inventoryType)) return; + + missionComponent->Progress(eMissionTaskType::GATHER, lot, LWOOBJID_EMPTY, "", count - outOfSpace, IsTransferInventory(inventorySourceType)); } void InventoryComponent::RemoveItem(const LOT lot, const uint32_t count, eInventoryType inventoryType, const bool ignoreBound) { if (count == 0) { - Game::logger->Log("InventoryComponent", "Attempted to remove 0 of item (%i) from the inventory!", lot); + Game::logger->Log("InventoryComponent", "Attempted to remove 0 of item (%i) from the inventory of %i:%llu!", lot, m_ParentEntity->GetLOT(), m_ParentEntity->GetObjectID()); return; } @@ -310,18 +298,14 @@ void InventoryComponent::RemoveItem(const LOT lot, const uint32_t count, eInvent auto* inventory = GetInventory(inventoryType); - if (inventory == nullptr) { - return; - } + if (!inventory) return; auto left = std::min(count, inventory->GetLotCount(lot)); while (left > 0) { auto* item = FindItemByLot(lot, inventoryType, false, ignoreBound); - if (item == nullptr) { - break; - } + if (!item) break; const auto delta = std::min(left, item->GetCount()); @@ -332,9 +316,7 @@ void InventoryComponent::RemoveItem(const LOT lot, const uint32_t count, eInvent } void InventoryComponent::MoveItemToInventory(Item* item, const eInventoryType inventory, const uint32_t count, const bool showFlyingLot, bool isModMoveAndEquip, const bool ignoreEquipped, const int32_t preferredSlot) { - if (item == nullptr) { - return; - } + if (!item) return; auto* origin = item->GetInventory(); @@ -346,12 +328,10 @@ void InventoryComponent::MoveItemToInventory(Item* item, const eInventoryType in auto left = std::min(count, origin->GetLotCount(lot)); while (left > 0) { - if (item == nullptr) { + if (!item) { item = origin->FindItemByLot(lot, false); - if (item == nullptr) { - break; - } + if (!item) break; } const auto delta = std::min(item->GetCount(), left); @@ -378,9 +358,9 @@ void InventoryComponent::MoveItemToInventory(Item* item, const eInventoryType in item->SetCount(item->GetCount() - delta, false, false); } - auto* missionComponent = m_Parent->GetComponent(); + auto* missionComponent = m_ParentEntity->GetComponent(); - if (missionComponent != nullptr) { + if (missionComponent) { if (IsTransferInventory(inventory)) { missionComponent->Progress(eMissionTaskType::GATHER, lot, LWOOBJID_EMPTY, "", -static_cast(count)); } @@ -388,6 +368,8 @@ void InventoryComponent::MoveItemToInventory(Item* item, const eInventoryType in } void InventoryComponent::MoveStack(Item* item, const eInventoryType inventory, const uint32_t slot) { + if (!item) return; + if (inventory != INVALID && item->GetInventory()->GetType() != inventory) { auto* newInventory = GetInventory(inventory); @@ -398,12 +380,15 @@ void InventoryComponent::MoveStack(Item* item, const eInventoryType inventory, c } Item* InventoryComponent::FindItemById(const LWOOBJID id) const { - for (const auto& inventory : m_Inventories) { - auto* item = inventory.second->FindItemById(id); + if (id == LWOOBJID_EMPTY) { + Game::logger->Log("InventoryComponent", "Attempted to find item with empty id!"); - if (item != nullptr) { - return item; - } + return nullptr; + } + for (const auto [inventoryType, inventory] : m_Inventories) { + auto* item = inventory->FindItemById(id); + + if (item) return item; } return nullptr; @@ -420,13 +405,17 @@ Item* InventoryComponent::FindItemByLot(const LOT lot, eInventoryType inventoryT } Item* InventoryComponent::FindItemBySubKey(LWOOBJID id, eInventoryType inventoryType) { + if (id == LWOOBJID_EMPTY) { + Game::logger->Log("InventoryComponent", "Attempted to find item by SubKey with empty id!"); + + return nullptr; + } + if (inventoryType == INVALID) { - for (const auto& inventory : m_Inventories) { - auto* item = inventory.second->FindItemBySubKey(id); + for (const auto& [inventoryId, inventory] : m_Inventories) { + auto* item = inventory->FindItemBySubKey(id); - if (item != nullptr) { - return item; - } + if (item) return item; } return nullptr; @@ -440,28 +429,24 @@ bool InventoryComponent::HasSpaceForLoot(const std::unordered_map& uint32_t slotsNeeded = 0; - for (const auto& pair : loot) { - const auto inventoryType = Inventory::FindInventoryTypeForLot(pair.first); + for (const auto& [item, count] : loot) { + const auto inventoryType = Inventory::FindInventoryTypeForLot(item); - if (inventoryType == BRICKS) { - continue; - } + if (inventoryType == BRICKS) continue; auto* inventory = GetInventory(inventoryType); - if (inventory == nullptr) { - return false; - } + if (!inventory) return false; - const auto info = Inventory::FindItemComponent(pair.first); + const auto info = Inventory::FindItemComponent(item); auto stack = static_cast(info.stackSize); - auto left = pair.second; + auto left = count; - auto* partial = inventory->FindItemByLot(pair.first); + auto* partial = inventory->FindItemByLot(item); - if (partial != nullptr && partial->GetCount() < stack) { + if (partial && partial->GetCount() < stack) { left -= stack - partial->GetCount(); } @@ -479,7 +464,7 @@ bool InventoryComponent::HasSpaceForLoot(const std::unordered_map& } if (slotsNeeded > 0) { - GameMessages::SendNotifyNotEnoughInvSpace(m_Parent->GetObjectID(), slotsNeeded, ITEMS, m_Parent->GetSystemAddress()); + GameMessages::SendNotifyNotEnoughInvSpace(m_ParentEntity->GetObjectID(), slotsNeeded, ITEMS, m_ParentEntity->GetSystemAddress()); return false; } @@ -492,7 +477,7 @@ void InventoryComponent::LoadXml(tinyxml2::XMLDocument* document) { auto* inventoryElement = document->FirstChildElement("obj")->FirstChildElement("inv"); - if (inventoryElement == nullptr) { + if (!inventoryElement) { Game::logger->Log("InventoryComponent", "Failed to find 'inv' xml element!"); return; @@ -500,7 +485,7 @@ void InventoryComponent::LoadXml(tinyxml2::XMLDocument* document) { auto* bags = inventoryElement->FirstChildElement("bag"); - if (bags == nullptr) { + if (!bags) { Game::logger->Log("InventoryComponent", "Failed to find 'bags' xml element!"); return; @@ -510,7 +495,7 @@ void InventoryComponent::LoadXml(tinyxml2::XMLDocument* document) { auto* bag = bags->FirstChildElement(); - while (bag != nullptr) { + while (bag) { unsigned int type; unsigned int size; @@ -526,7 +511,7 @@ void InventoryComponent::LoadXml(tinyxml2::XMLDocument* document) { auto* items = inventoryElement->FirstChildElement("items"); - if (items == nullptr) { + if (!items) { Game::logger->Log("InventoryComponent", "Failed to find 'items' xml element!"); return; @@ -534,7 +519,7 @@ void InventoryComponent::LoadXml(tinyxml2::XMLDocument* document) { bag = items->FirstChildElement(); - while (bag != nullptr) { + while (bag) { unsigned int type; bag->QueryAttribute("t", &type); @@ -549,7 +534,7 @@ void InventoryComponent::LoadXml(tinyxml2::XMLDocument* document) { auto* itemElement = bag->FirstChildElement(); - while (itemElement != nullptr) { + while (itemElement) { LWOOBJID id; LOT lot; bool equipped; @@ -589,7 +574,7 @@ void InventoryComponent::LoadXml(tinyxml2::XMLDocument* document) { if (equipped) { const auto info = Inventory::FindItemComponent(lot); - UpdateSlot(info.equipLocation, { item->GetId(), item->GetLot(), item->GetCount(), item->GetSlot() }); + UpdateSlot(info.equipLocation, EquippedItem(item->GetId(), item->GetLot(), item->GetCount(), item->GetSlot())); AddItemSkills(item->GetLot()); } @@ -600,11 +585,11 @@ void InventoryComponent::LoadXml(tinyxml2::XMLDocument* document) { bag = bag->NextSiblingElement(); } - for (const auto inventory : m_Inventories) { - const auto itemCount = inventory.second->GetItems().size(); + for (const auto [inventoryId, inventory] : m_Inventories) { + const auto itemCount = inventory->GetItems().size(); - if (inventory.second->GetSize() < itemCount) { - inventory.second->SetSize(itemCount); + if (inventory->GetSize() < itemCount) { + inventory->SetSize(itemCount); } } } @@ -623,9 +608,7 @@ void InventoryComponent::UpdateXml(tinyxml2::XMLDocument* document) { std::vector inventoriesToSave; // Need to prevent some transfer inventories from being saved - for (const auto& pair : this->m_Inventories) { - auto* inventory = pair.second; - + for (const auto [inventoryId, inventory] : this->m_Inventories) { if (inventory->GetType() == VENDOR_BUYBACK || inventory->GetType() == eInventoryType::MODELS_IN_BBB) { continue; } @@ -637,7 +620,7 @@ void InventoryComponent::UpdateXml(tinyxml2::XMLDocument* document) { auto* bags = inventoryElement->FirstChildElement("bag"); - if (bags == nullptr) { + if (!bags) { Game::logger->Log("InventoryComponent", "Failed to find 'bags' xml element!"); return; @@ -656,7 +639,7 @@ void InventoryComponent::UpdateXml(tinyxml2::XMLDocument* document) { auto* items = inventoryElement->FirstChildElement("items"); - if (items == nullptr) { + if (!items) { Game::logger->Log("InventoryComponent", "Failed to find 'items' xml element!"); return; @@ -665,17 +648,13 @@ void InventoryComponent::UpdateXml(tinyxml2::XMLDocument* document) { items->DeleteChildren(); for (auto* inventory : inventoriesToSave) { - if (inventory->GetSize() == 0) { - continue; - } + if (inventory->GetSize() == 0) continue; auto* bagElement = document->NewElement("in"); bagElement->SetAttribute("t", inventory->GetType()); - for (const auto& pair : inventory->GetItems()) { - auto* item = pair.second; - + for (const auto [itemObjId, item] : inventory->GetItems()) { auto* itemElement = document->NewElement("i"); itemElement->SetAttribute("l", item->GetLot()); @@ -687,7 +666,7 @@ void InventoryComponent::UpdateXml(tinyxml2::XMLDocument* document) { itemElement->SetAttribute("sk", item->GetSubKey()); // Begin custom xml - itemElement->SetAttribute("parent", item->GetParent()); + itemElement->SetAttribute("parent", item->GetParentEntity()); // End custom xml for (auto* data : item->GetConfig()) { @@ -710,17 +689,12 @@ void InventoryComponent::UpdateXml(tinyxml2::XMLDocument* document) { } void InventoryComponent::Serialize(RakNet::BitStream* outBitStream, const bool bIsInitialUpdate, unsigned& flags) { + outBitStream->Write(bIsInitialUpdate || m_Dirty); if (bIsInitialUpdate || m_Dirty) { - outBitStream->Write(true); - outBitStream->Write(m_Equipped.size()); - for (const auto& pair : m_Equipped) { - const auto item = pair.second; - - if (bIsInitialUpdate) { - AddItemSkills(item.lot); - } + for (const auto [itemObjId, item] : m_Equipped) { + if (bIsInitialUpdate) AddItemSkills(item.lot); outBitStream->Write(item.id); outBitStream->Write(item.lot); @@ -759,16 +733,10 @@ void InventoryComponent::Serialize(RakNet::BitStream* outBitStream, const bool b outBitStream->Write1(); } - m_Dirty = false; - } else { - outBitStream->Write(false); + if (!bIsInitialUpdate) m_Dirty = false; } - outBitStream->Write(false); -} - -void InventoryComponent::ResetFlags() { - m_Dirty = false; + outBitStream->Write0(); // Equipped model transforms } void InventoryComponent::Update(float deltaTime) { @@ -791,7 +759,7 @@ void InventoryComponent::UpdateSlot(const std::string& location, EquippedItem it auto* old = FindItemById(index->second.id); - if (old != nullptr) { + if (old) { UnEquipItem(old); } } @@ -802,9 +770,7 @@ void InventoryComponent::UpdateSlot(const std::string& location, EquippedItem it } void InventoryComponent::RemoveSlot(const std::string& location) { - if (m_Equipped.find(location) == m_Equipped.end()) { - return; - } + if (m_Equipped.find(location) == m_Equipped.end()) return; m_Equipped.erase(location); @@ -821,14 +787,14 @@ void InventoryComponent::EquipItem(Item* item, const bool skipChecks) { return; } - auto* character = m_Parent->GetCharacter(); + auto* character = m_ParentEntity->GetCharacter(); - if (character != nullptr && !skipChecks) { + if (character && !skipChecks) { // Hacky proximity rocket - if (item->GetLot() == 6416) { - const auto rocketLauchPads = EntityManager::Instance()->GetEntitiesByComponent(eReplicaComponentType::ROCKET_LAUNCH); + if (item->GetLot() == LOT_ROCKET) { + const auto rocketLauchPads = EntityManager::Instance()->GetEntitiesByComponent(eReplicaComponentType::ROCKET_LAUNCHPAD_CONTROL); - const auto position = m_Parent->GetPosition(); + const auto position = m_ParentEntity->GetPosition(); for (auto* launchPad : rocketLauchPads) { if (!launchPad) continue; @@ -836,16 +802,16 @@ void InventoryComponent::EquipItem(Item* item, const bool skipChecks) { auto prereq = launchPad->GetVarAsString(u"rocketLaunchPreCondition"); if (!prereq.empty()) { PreconditionExpression expression(prereq); - if (!expression.Check(m_Parent)) continue; + if (!expression.Check(m_ParentEntity)) continue; } if (Vector3::DistanceSquared(launchPad->GetPosition(), position) > 13 * 13) continue; - auto* characterComponent = m_Parent->GetComponent(); + auto* characterComponent = m_ParentEntity->GetComponent(); - if (characterComponent != nullptr) characterComponent->SetLastRocketItemID(item->GetId()); + if (characterComponent) characterComponent->SetLastRocketItemID(item->GetId()); - launchPad->OnUse(m_Parent); + launchPad->OnUse(m_ParentEntity); break; } @@ -855,13 +821,13 @@ void InventoryComponent::EquipItem(Item* item, const bool skipChecks) { const auto building = character->GetBuildMode(); - const auto type = static_cast(item->GetInfo().itemType); + const auto type = item->GetInfo().itemType; - if (!building && (item->GetLot() == 6086 || type == eItemType::LOOT_MODEL || type == eItemType::VEHICLE)) return; + if (!building && (item->GetLot() == LOT_THINKING_CAP || type == eItemType::LOOT_MODEL || type == eItemType::VEHICLE)) return; if (type != eItemType::LOOT_MODEL && type != eItemType::MODEL) { - if (!item->GetBound() && !item->GetPreconditionExpression()->Check(m_Parent)) { + if (!item->GetBound() && !item->GetPreconditionExpression()->Check(m_ParentEntity)) { return; } } @@ -879,7 +845,7 @@ void InventoryComponent::EquipItem(Item* item, const bool skipChecks) { GenerateProxies(item); - UpdateSlot(item->GetInfo().equipLocation, { item->GetId(), item->GetLot(), item->GetCount(), item->GetSlot(), item->GetConfig() }); + UpdateSlot(item->GetInfo().equipLocation, EquippedItem(item->GetId(), item->GetLot(), item->GetCount(), item->GetSlot(), item->GetConfig())); ApplyBuff(item); @@ -887,19 +853,15 @@ void InventoryComponent::EquipItem(Item* item, const bool skipChecks) { EquipScripts(item); - EntityManager::Instance()->SerializeEntity(m_Parent); + EntityManager::Instance()->SerializeEntity(m_ParentEntity); } void InventoryComponent::UnEquipItem(Item* item) { - if (!item->IsEquipped()) { - return; - } + if (!item->IsEquipped()) return; const auto lot = item->GetLot(); - if (!Inventory::IsValidItem(lot)) { - return; - } + if (!Inventory::IsValidItem(lot)) return; CheckItemSet(lot); @@ -917,119 +879,113 @@ void InventoryComponent::UnEquipItem(Item* item) { UnequipScripts(item); - EntityManager::Instance()->SerializeEntity(m_Parent); + EntityManager::Instance()->SerializeEntity(m_ParentEntity); // Trigger property event if (PropertyManagementComponent::Instance() != nullptr && item->GetCount() > 0 && Inventory::FindInventoryTypeForLot(item->GetLot()) == MODELS) { - PropertyManagementComponent::Instance()->GetParent()->OnZonePropertyModelRemovedWhileEquipped(m_Parent); - dZoneManager::Instance()->GetZoneControlObject()->OnZonePropertyModelRemovedWhileEquipped(m_Parent); + PropertyManagementComponent::Instance()->GetParentEntity()->OnZonePropertyModelRemovedWhileEquipped(m_ParentEntity); + dZoneManager::Instance()->GetZoneControlObject()->OnZonePropertyModelRemovedWhileEquipped(m_ParentEntity); } } -void InventoryComponent::EquipScripts(Item* equippedItem) { - CDComponentsRegistryTable* compRegistryTable = CDClientManager::Instance().GetTable(); - if (!compRegistryTable) return; +void InventoryComponent::EquipScripts(Item* equippedItem) const { + auto* compRegistryTable = CDClientManager::Instance().GetTable(); + int32_t scriptComponentID = compRegistryTable->GetByIDAndType(equippedItem->GetLot(), eReplicaComponentType::SCRIPT, -1); if (scriptComponentID > -1) { - CDScriptComponentTable* scriptCompTable = CDClientManager::Instance().GetTable(); - CDScriptComponent scriptCompData = scriptCompTable->GetByID(scriptComponentID); - auto* itemScript = CppScripts::GetScript(m_Parent, scriptCompData.script_name); - if (!itemScript) { - Game::logger->Log("InventoryComponent", "null script?"); - } - itemScript->OnFactionTriggerItemEquipped(m_Parent, equippedItem->GetId()); + auto* scriptCompTable = CDClientManager::Instance().GetTable(); + auto scriptCompData = scriptCompTable->GetByID(scriptComponentID); + auto* itemScript = CppScripts::GetScript(m_ParentEntity, scriptCompData.script_name); + DluAssert(itemScript != nullptr); + itemScript->OnFactionTriggerItemEquipped(m_ParentEntity, equippedItem->GetId()); } } -void InventoryComponent::UnequipScripts(Item* unequippedItem) { - CDComponentsRegistryTable* compRegistryTable = CDClientManager::Instance().GetTable(); - if (!compRegistryTable) return; +void InventoryComponent::UnequipScripts(Item* unequippedItem) const { + auto* compRegistryTable = CDClientManager::Instance().GetTable(); + int32_t scriptComponentID = compRegistryTable->GetByIDAndType(unequippedItem->GetLot(), eReplicaComponentType::SCRIPT, -1); if (scriptComponentID > -1) { - CDScriptComponentTable* scriptCompTable = CDClientManager::Instance().GetTable(); - CDScriptComponent scriptCompData = scriptCompTable->GetByID(scriptComponentID); - auto* itemScript = CppScripts::GetScript(m_Parent, scriptCompData.script_name); - if (!itemScript) { - Game::logger->Log("InventoryComponent", "null script?"); - } - itemScript->OnFactionTriggerItemUnequipped(m_Parent, unequippedItem->GetId()); + auto* scriptCompTable = CDClientManager::Instance().GetTable(); + auto scriptCompData = scriptCompTable->GetByID(scriptComponentID); + auto* itemScript = CppScripts::GetScript(m_ParentEntity, scriptCompData.script_name); + DluAssert(itemScript != nullptr); + itemScript->OnFactionTriggerItemUnequipped(m_ParentEntity, unequippedItem->GetId()); } } -void InventoryComponent::HandlePossession(Item* item) { - auto* characterComponent = m_Parent->GetComponent(); +void InventoryComponent::HandlePossession(Item* item) const { + auto* characterComponent = m_ParentEntity->GetComponent(); if (!characterComponent) return; - auto* possessorComponent = m_Parent->GetComponent(); - if (!possessorComponent) return; + auto* possessionComponent = m_ParentEntity->GetComponent(); + if (!possessionComponent) return; // Don't do anything if we are busy dismounting - if (possessorComponent->GetIsDismounting()) return; + if (possessionComponent->GetIsDismounting()) return; // Check to see if we are already mounting something - auto* currentlyPossessedEntity = EntityManager::Instance()->GetEntity(possessorComponent->GetPossessable()); - auto currentlyPossessedItem = possessorComponent->GetMountItemID(); + auto* currentlyPossessedEntity = EntityManager::Instance()->GetEntity(possessionComponent->GetPossessable()); + auto currentlyPossessedItem = possessionComponent->GetMountItemID(); if (currentlyPossessedItem) { - if (currentlyPossessedEntity) possessorComponent->Dismount(currentlyPossessedEntity); + if (currentlyPossessedEntity) possessionComponent->Dismount(currentlyPossessedEntity); return; } - GameMessages::SendSetStunned(m_Parent->GetObjectID(), eStateChangeType::PUSH, m_Parent->GetSystemAddress(), LWOOBJID_EMPTY, true, false, true, false, false, false, false, true, true, true, true, true, true, true, true, true); + 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); // Set the mount Item ID so that we know what were handling - possessorComponent->SetMountItemID(item->GetId()); - GameMessages::SendSetMountInventoryID(m_Parent, item->GetId(), UNASSIGNED_SYSTEM_ADDRESS); + possessionComponent->SetMountItemID(item->GetId()); + GameMessages::SendSetMountInventoryID(m_ParentEntity, item->GetId(), UNASSIGNED_SYSTEM_ADDRESS); // Create entity to mount - auto startRotation = m_Parent->GetRotation(); + auto startRotation = m_ParentEntity->GetRotation(); EntityInfo info{}; info.lot = item->GetLot(); - info.pos = m_Parent->GetPosition(); + info.pos = m_ParentEntity->GetPosition(); info.rot = startRotation; - info.spawnerID = m_Parent->GetObjectID(); + info.spawnerID = m_ParentEntity->GetObjectID(); - auto* mount = EntityManager::Instance()->CreateEntity(info, nullptr, m_Parent); + auto* mount = EntityManager::Instance()->CreateEntity(info, nullptr, m_ParentEntity); // Check to see if the mount is a vehicle, if so, flip it - auto* vehicleComponent = mount->GetComponent(); - if (vehicleComponent) characterComponent->SetIsRacing(true); + auto* havokVehiclePhysicsComponent = mount->GetComponent(); + if (havokVehiclePhysicsComponent) characterComponent->SetIsRacing(true); // Setup the destroyable stats auto* destroyableComponent = mount->GetComponent(); - if (destroyableComponent) { - destroyableComponent->SetIsImmune(true); - } + if (destroyableComponent) destroyableComponent->SetIsImmune(true); // Mount it auto* possessableComponent = mount->GetComponent(); if (possessableComponent) { possessableComponent->SetIsItemSpawned(true); - possessableComponent->SetPossessor(m_Parent->GetObjectID()); + possessableComponent->SetPossessor(m_ParentEntity->GetObjectID()); // Possess it - possessorComponent->SetPossessable(mount->GetObjectID()); - possessorComponent->SetPossessableType(possessableComponent->GetPossessionType()); + possessionComponent->SetPossessable(mount->GetObjectID()); + possessionComponent->SetPossessableType(possessableComponent->GetPossessionType()); } - GameMessages::SendSetJetPackMode(m_Parent, false); + GameMessages::SendSetJetPackMode(m_ParentEntity, false); // Make it go to the client EntityManager::Instance()->ConstructEntity(mount); // Update the possessor - EntityManager::Instance()->SerializeEntity(m_Parent); + EntityManager::Instance()->SerializeEntity(m_ParentEntity); // have to unlock the input so it vehicle can be driven - if (vehicleComponent) GameMessages::SendVehicleUnlockInput(mount->GetObjectID(), false, m_Parent->GetSystemAddress()); - GameMessages::SendMarkInventoryItemAsActive(m_Parent->GetObjectID(), true, eUnequippableActiveType::MOUNT, item->GetId(), m_Parent->GetSystemAddress()); + if (havokVehiclePhysicsComponent) GameMessages::SendVehicleUnlockInput(mount->GetObjectID(), false, m_ParentEntity->GetSystemAddress()); + GameMessages::SendMarkInventoryItemAsActive(m_ParentEntity->GetObjectID(), true, eUnequippableActiveType::MOUNT, item->GetId(), m_ParentEntity->GetSystemAddress()); } void InventoryComponent::ApplyBuff(Item* item) const { const auto buffs = FindBuffs(item, true); for (const auto buff : buffs) { - SkillComponent::HandleUnmanaged(buff, m_Parent->GetObjectID()); + SkillComponent::HandleUnmanaged(buff, m_ParentEntity->GetObjectID()); } } @@ -1038,7 +994,7 @@ void InventoryComponent::RemoveBuff(Item* item) const { const auto buffs = FindBuffs(item, false); for (const auto buff : buffs) { - SkillComponent::HandleUnCast(buff, m_Parent->GetObjectID()); + SkillComponent::HandleUnCast(buff, m_ParentEntity->GetObjectID()); } } @@ -1054,33 +1010,25 @@ void InventoryComponent::PopEquippedItems() { for (const auto& pair : current) { auto* const item = FindItemById(pair.second.id); - if (item == nullptr) { - continue; - } - - item->UnEquip(); + if (item) item->UnEquip(); } for (const auto& pair : m_Pushed) { auto* const item = FindItemById(pair.second.id); - if (item == nullptr) { - continue; - } - - item->Equip(); + if (item) item->Equip(); } m_Pushed.clear(); - auto destroyableComponent = m_Parent->GetComponent(); + auto* destroyableComponent = m_ParentEntity->GetComponent(); // Reset stats to full if (destroyableComponent) { destroyableComponent->SetHealth(static_cast(destroyableComponent->GetMaxHealth())); destroyableComponent->SetArmor(static_cast(destroyableComponent->GetMaxArmor())); destroyableComponent->SetImagination(static_cast(destroyableComponent->GetMaxImagination())); - EntityManager::Instance()->SerializeEntity(m_Parent); + EntityManager::Instance()->SerializeEntity(m_ParentEntity); } m_Dirty = true; @@ -1088,8 +1036,8 @@ void InventoryComponent::PopEquippedItems() { bool InventoryComponent::IsEquipped(const LOT lot) const { - for (const auto& pair : m_Equipped) { - if (pair.second.lot == lot) { + for (const auto& [equipLocation, equippedItem] : m_Equipped) { + if (equippedItem.lot == lot) { return true; } } @@ -1099,15 +1047,11 @@ bool InventoryComponent::IsEquipped(const LOT lot) const { void InventoryComponent::CheckItemSet(const LOT lot) { // Check if the lot is in the item set cache - if (std::find(m_ItemSetsChecked.begin(), m_ItemSetsChecked.end(), lot) != m_ItemSetsChecked.end()) { - return; - } - - const std::string lot_query = "%" + std::to_string(lot) + "%"; + if (std::find(m_ItemSetsChecked.begin(), m_ItemSetsChecked.end(), lot) != m_ItemSetsChecked.end()) return; auto query = CDClientDatabase::CreatePreppedStmt( - "SELECT setID FROM ItemSets WHERE itemIDs LIKE ?;"); - query.bind(1, lot_query.c_str()); + "SELECT setID FROM ItemSets WHERE itemIDs LIKE %?%;"); + query.bind(1, lot); auto result = query.execQuery(); @@ -1134,42 +1078,28 @@ void InventoryComponent::CheckItemSet(const LOT lot) { } m_ItemSetsChecked.push_back(lot); - - result.finalize(); -} - -void InventoryComponent::SetConsumable(LOT lot) { - m_Consumable = lot; -} - -LOT InventoryComponent::GetConsumable() const { - return m_Consumable; } void InventoryComponent::AddItemSkills(const LOT lot) { const auto info = Inventory::FindItemComponent(lot); - const auto slot = FindBehaviorSlot(static_cast(info.itemType)); + const auto slot = FindBehaviorSlot(info.itemType); - if (slot == BehaviorSlot::Invalid) { - return; - } + if (slot == BehaviorSlot::Invalid) return; const auto index = m_Skills.find(slot); const auto skill = FindSkill(lot); - if (skill == 0) { - return; - } + if (skill == 0) return; if (index != m_Skills.end()) { const auto old = index->second; - GameMessages::SendRemoveSkill(m_Parent, old); + GameMessages::SendRemoveSkill(m_ParentEntity, old); } - GameMessages::SendAddSkill(m_Parent, skill, static_cast(slot)); + GameMessages::SendAddSkill(m_ParentEntity, skill, static_cast(slot)); m_Skills.insert_or_assign(slot, skill); } @@ -1177,32 +1107,27 @@ void InventoryComponent::AddItemSkills(const LOT lot) { void InventoryComponent::RemoveItemSkills(const LOT lot) { const auto info = Inventory::FindItemComponent(lot); - const auto slot = FindBehaviorSlot(static_cast(info.itemType)); + const auto slot = FindBehaviorSlot(info.itemType); - if (slot == BehaviorSlot::Invalid) { - return; - } + if (slot == BehaviorSlot::Invalid) return; const auto index = m_Skills.find(slot); - if (index == m_Skills.end()) { - return; - } + if (index == m_Skills.end()) return; - const auto old = index->second; + const auto skillId = index->second; - GameMessages::SendRemoveSkill(m_Parent, old); + GameMessages::SendRemoveSkill(m_ParentEntity, skillId); m_Skills.erase(slot); - if (slot == BehaviorSlot::Primary) { - m_Skills.insert_or_assign(BehaviorSlot::Primary, 1); + if (slot != BehaviorSlot::Primary) return; - GameMessages::SendAddSkill(m_Parent, 1, static_cast(BehaviorSlot::Primary)); - } + m_Skills.insert_or_assign(BehaviorSlot::Primary, 1); + GameMessages::SendAddSkill(m_ParentEntity, 1, static_cast(BehaviorSlot::Primary)); } -void InventoryComponent::TriggerPassiveAbility(PassiveAbilityTrigger trigger, Entity* target) { +void InventoryComponent::TriggerPassiveAbility(PassiveAbilityTrigger trigger, Entity* target) const { for (auto* set : m_Itemsets) { set->TriggerPassiveAbility(trigger, target); } @@ -1224,17 +1149,15 @@ bool InventoryComponent::HasAnyPassive(const std::vectorGetObjectID()); - - if (current != nullptr) { - current->Deactivate(); - } + auto current = PetComponent::GetActivePet(m_ParentEntity->GetObjectID()); + if (!current) return; + current->Deactivate(); } void InventoryComponent::SpawnPet(Item* item) { - auto* current = PetComponent::GetActivePet(m_Parent->GetObjectID()); + auto current = PetComponent::GetActivePet(m_ParentEntity->GetObjectID()); - if (current != nullptr) { + if (current) { current->Deactivate(); if (current->GetDatabaseId() == item->GetSubKey()) { @@ -1243,26 +1166,24 @@ void InventoryComponent::SpawnPet(Item* item) { } // First check if we can summon the pet. You need 1 imagination to do so. - auto destroyableComponent = m_Parent->GetComponent(); + auto* destroyableComponent = m_ParentEntity->GetComponent(); if (Game::config->GetValue("pets_take_imagination") == "1" && destroyableComponent && destroyableComponent->GetImagination() <= 0) { - GameMessages::SendUseItemRequirementsResponse(m_Parent->GetObjectID(), m_Parent->GetSystemAddress(), eUseItemResponse::NoImaginationForPet); + GameMessages::SendUseItemRequirementsResponse(m_ParentEntity->GetObjectID(), m_ParentEntity->GetSystemAddress(), eUseItemResponse::NoImaginationForPet); return; } EntityInfo info{}; info.lot = item->GetLot(); - info.pos = m_Parent->GetPosition(); + info.pos = m_ParentEntity->GetPosition(); info.rot = NiQuaternion::IDENTITY; - info.spawnerID = m_Parent->GetObjectID(); + info.spawnerID = m_ParentEntity->GetObjectID(); auto* pet = EntityManager::Instance()->CreateEntity(info); auto* petComponent = pet->GetComponent(); - if (petComponent != nullptr) { - petComponent->Activate(item); - } + if (petComponent) petComponent->Activate(item); EntityManager::Instance()->ConstructEntity(pet); } @@ -1274,19 +1195,7 @@ void InventoryComponent::SetDatabasePet(LWOOBJID id, const DatabasePet& data) { const DatabasePet& InventoryComponent::GetDatabasePet(LWOOBJID id) const { const auto& pair = m_Pets.find(id); - if (pair == m_Pets.end()) return DATABASE_PET_INVALID; - - return pair->second; -} - -bool InventoryComponent::IsPet(LWOOBJID id) const { - const auto& pair = m_Pets.find(id); - - return pair != m_Pets.end(); -} - -void InventoryComponent::RemoveDatabasePet(LWOOBJID id) { - m_Pets.erase(id); + return pair == m_Pets.end() ? DATABASE_PET_INVALID : pair->second; } BehaviorSlot InventoryComponent::FindBehaviorSlot(const eItemType type) { @@ -1313,8 +1222,8 @@ bool InventoryComponent::IsTransferInventory(eInventoryType type) { uint32_t InventoryComponent::FindSkill(const LOT lot) { auto* table = CDClientManager::Instance().GetTable(); - const auto results = table->Query([=](const CDObjectSkills& entry) { - return entry.objectTemplate == static_cast(lot); + const auto results = table->Query([&lot](const CDObjectSkills& entry) { + return entry.objectTemplate == static_cast(lot); }); for (const auto& result : results) { @@ -1328,33 +1237,33 @@ uint32_t InventoryComponent::FindSkill(const LOT lot) { std::vector InventoryComponent::FindBuffs(Item* item, bool castOnEquip) const { std::vector buffs; - if (item == nullptr) return buffs; + if (!item) return buffs; auto* table = CDClientManager::Instance().GetTable(); auto* behaviors = CDClientManager::Instance().GetTable(); - const auto results = table->Query([=](const CDObjectSkills& entry) { + const auto results = table->Query([item](const CDObjectSkills& entry) { return entry.objectTemplate == static_cast(item->GetLot()); }); - auto* missions = static_cast(m_Parent->GetComponent(eReplicaComponentType::MISSION)); + auto* missions = m_ParentEntity->GetComponent(); for (const auto& result : results) { - if (result.castOnType == 1) { - const auto entry = behaviors->GetSkillByID(result.skillID); + if (result.castOnType != 1) continue; - if (entry.skillID == 0) { - Game::logger->Log("InventoryComponent", "Failed to find buff behavior for skill (%i)!", result.skillID); + const auto entry = behaviors->GetSkillByID(result.skillID); - continue; - } + if (entry.skillID == 0) { + Game::logger->Log("InventoryComponent", "Failed to find buff behavior for skill (%i)!", result.skillID); - if (missions != nullptr && castOnEquip) { - missions->Progress(eMissionTaskType::USE_SKILL, result.skillID); - } + continue; + } - // If item is not a proxy, add its buff to the added buffs. - if (item->GetParent() == LWOOBJID_EMPTY) buffs.push_back(static_cast(entry.behaviorID)); + if (missions && castOnEquip) { + missions->Progress(eMissionTaskType::USE_SKILL, result.skillID); } + + // If item is not a proxy, add its buff to the added buffs. + if (item->GetParentEntity() == LWOOBJID_EMPTY) buffs.push_back(static_cast(entry.behaviorID)); } return buffs; @@ -1370,22 +1279,20 @@ void InventoryComponent::SetNPCItems(const std::vector& items) { const auto& info = Inventory::FindItemComponent(item); - UpdateSlot(info.equipLocation, { id, static_cast(item), 1, slot++ }, true); + UpdateSlot(info.equipLocation, EquippedItem(id, static_cast(item), 1, slot++), true); } - EntityManager::Instance()->SerializeEntity(m_Parent); + EntityManager::Instance()->SerializeEntity(m_ParentEntity); } InventoryComponent::~InventoryComponent() { - for (const auto& inventory : m_Inventories) { - delete inventory.second; + for (const auto& [inventoryId, inventory] : m_Inventories) { + delete inventory; } m_Inventories.clear(); - for (auto* set : m_Itemsets) { - delete set; - } + std::for_each(m_Itemsets.begin(), m_Itemsets.end(), [](ItemSet* set) { delete set; }); m_Itemsets.clear(); m_Pets.clear(); @@ -1396,28 +1303,21 @@ std::vector InventoryComponent::GenerateProxies(Item* parent) { auto subItems = parent->GetInfo().subItems; - if (subItems.empty()) { - return proxies; - } + if (subItems.empty()) return proxies; subItems.erase(std::remove_if(subItems.begin(), subItems.end(), ::isspace), subItems.end()); - std::stringstream stream(subItems); - std::string segment; - std::vector lots; + auto itemsAsStr = GeneralUtils::SplitString(subItems, ','); - while (std::getline(stream, segment, ',')) { - try { - lots.push_back(std::stoi(segment)); - } catch (std::invalid_argument& exception) { - Game::logger->Log("InventoryComponent", "Failed to parse proxy (%s): (%s)!", segment.c_str(), exception.what()); - } - } + std::vector lots; + std::for_each(itemsAsStr.begin(), itemsAsStr.end(), [&lots](const std::string& str) { + int32_t lot; + if (GeneralUtils::TryParse(str, lot)) lots.push_back(lot); + else Game::logger->Log("InventoryComponent", "failed to parse %s to a lot.", str.c_str()); + }); - for (const auto lot : lots) { - if (!Inventory::IsValidItem(lot)) { - continue; - } + std::for_each(lots.begin(), lots.end(), [&proxies, parent, this](const int lot) { + if (!Inventory::IsValidItem(lot)) return; auto* inventory = GetInventory(ITEM_SETS); @@ -1426,7 +1326,7 @@ std::vector InventoryComponent::GenerateProxies(Item* parent) { EquipItem(proxy); proxies.push_back(proxy); - } + }); return proxies; } @@ -1435,28 +1335,18 @@ std::vector InventoryComponent::FindProxies(const LWOOBJID parent) { auto* inventory = GetInventory(ITEM_SETS); std::vector proxies; - - for (const auto& pair : inventory->GetItems()) { - auto* item = pair.second; - - if (item->GetParent() == parent) { - proxies.push_back(item); - } + for (const auto& [itemObjId, item] : inventory->GetItems()) { + if (item && item->GetParentEntity() != parent) proxies.push_back(item); } return proxies; } bool InventoryComponent::IsValidProxy(const LWOOBJID parent) { - for (const auto& pair : m_Inventories) { - const auto items = pair.second->GetItems(); - - for (const auto& candidate : items) { - auto* item = candidate.second; - - if (item->GetId() == parent) { - return true; - } + for (const auto& [inventoryType, inventory] : m_Inventories) { + if (!inventory) continue; + for (const auto& [itemObjId, item] : inventory->GetItems()) { + if (item->GetId() == parent) return true; } } @@ -1464,21 +1354,14 @@ bool InventoryComponent::IsValidProxy(const LWOOBJID parent) { } bool InventoryComponent::IsParentValid(Item* root) { - if (root->GetInfo().subItems.empty()) { - return true; - } + if (root->GetInfo().subItems.empty()) return true; const auto id = root->GetId(); - for (const auto& pair : m_Inventories) { - const auto items = pair.second->GetItems(); - - for (const auto& candidate : items) { - auto* item = candidate.second; - - if (item->GetParent() == id) { - return true; - } + for (const auto& [inventoryType, inventory] : m_Inventories) { + if (!inventory) continue; + for (const auto& [itemObjId, item] : inventory->GetItems()) { + if (item->GetParentEntity() == id) return true; } } @@ -1488,76 +1371,25 @@ bool InventoryComponent::IsParentValid(Item* root) { void InventoryComponent::CheckProxyIntegrity() { std::vector dead; - for (const auto& pair : m_Inventories) { - const auto& items = pair.second->GetItems(); - - for (const auto& candidate : items) { - auto* item = candidate.second; - - const auto parent = item->GetParent(); - - if (parent == LWOOBJID_EMPTY) { - continue; - } - - if (IsValidProxy(parent)) { - continue; - } - - dead.push_back(item); - } - } - - for (auto* item : dead) { - item->RemoveFromInventory(); - } - - dead.clear(); - - /* - for (const auto& pair : inventories) - { - const auto& items = pair.second->GetItems(); - - for (const auto& candidate : items) - { - auto* item = candidate.second; - - const auto parent = item->GetParent(); + for (const auto& [inventoryType, inventory] : m_Inventories) { + if (!inventory) continue; + for (const auto& [itemObjId, item] : inventory->GetItems()) { + const auto parent = item->GetParentEntity(); - if (parent != LWOOBJID_EMPTY) - { - continue; - } - - if (!item->IsEquipped()) - { - continue; - } - - if (IsParentValid(item)) - { - continue; - } - - dead.push_back(item); + if (parent != LWOOBJID_EMPTY && !IsValidProxy(parent)) dead.push_back(item); } } - for (auto* item : dead) - { - item->RemoveFromInventory(); - } - */ + std::for_each(dead.begin(), dead.end(), [](Item* item) { if (item) item->RemoveFromInventory(); }); } void InventoryComponent::PurgeProxies(Item* item) { - const auto root = item->GetParent(); + const auto root = item->GetParentEntity(); if (root != LWOOBJID_EMPTY) { item = FindItemById(root); - if (item != nullptr) { + if (item) { UnEquipItem(item); } @@ -1566,17 +1398,17 @@ void InventoryComponent::PurgeProxies(Item* item) { auto proxies = FindProxies(item->GetId()); - for (auto* proxy : proxies) { + std::for_each(proxies.begin(), proxies.end(), [](Item* proxy) { + if (!proxy) return; proxy->UnEquip(); - proxy->RemoveFromInventory(); - } + }); } void InventoryComponent::LoadPetXml(tinyxml2::XMLDocument* document) { auto* petInventoryElement = document->FirstChildElement("obj")->FirstChildElement("pet"); - if (petInventoryElement == nullptr) { + if (!petInventoryElement) { m_Pets.clear(); return; @@ -1584,7 +1416,7 @@ void InventoryComponent::LoadPetXml(tinyxml2::XMLDocument* document) { auto* petElement = petInventoryElement->FirstChildElement(); - while (petElement != nullptr) { + while (petElement) { LWOOBJID id; LOT lot; int32_t moderationStatus; @@ -1616,13 +1448,13 @@ void InventoryComponent::UpdatePetXml(tinyxml2::XMLDocument* document) { petInventoryElement->DeleteChildren(); - for (const auto& pet : m_Pets) { + for (const auto& [petObjId, pet] : m_Pets) { auto* petElement = document->NewElement("p"); - petElement->SetAttribute("id", pet.first); - petElement->SetAttribute("l", pet.second.lot); - petElement->SetAttribute("m", pet.second.moderationState); - petElement->SetAttribute("n", pet.second.name.c_str()); + petElement->SetAttribute("id", petObjId); + petElement->SetAttribute("l", pet.lot); + petElement->SetAttribute("m", pet.moderationState); + petElement->SetAttribute("n", pet.name.c_str()); petElement->SetAttribute("t", 0); petInventoryElement->LinkEndChild(petElement); diff --git a/dGame/dComponents/InventoryComponent.h b/dGame/dComponents/InventoryComponent.h index 801f9f51e..864f07a46 100644 --- a/dGame/dComponents/InventoryComponent.h +++ b/dGame/dComponents/InventoryComponent.h @@ -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 @@ class InventoryComponent : public Component * Returns all the inventories this entity has, indexed by type * @return all the inventories this entity has, indexed by type */ - const std::map& GetInventories() const; + const std::map& 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 @@ class InventoryComponent : public Component * 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 @@ class InventoryComponent : public Component * @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 @@ class InventoryComponent : public Component * 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 @@ class InventoryComponent : public Component * 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 @@ class InventoryComponent : public Component * @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 @@ class InventoryComponent : public Component * * @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; diff --git a/dGame/dComponents/ItemComponent.cpp b/dGame/dComponents/ItemComponent.cpp new file mode 100644 index 000000000..fb7133dc1 --- /dev/null +++ b/dGame/dComponents/ItemComponent.cpp @@ -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(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(m_UgDescription.length()); + outBitStream->Write(reinterpret_cast(m_UgDescription.c_str()), m_UgDescription.length() * sizeof(uint16_t)); + } + m_DirtyItemInfo = false; + } + +} diff --git a/dGame/dComponents/ItemComponent.h b/dGame/dComponents/ItemComponent.h new file mode 100644 index 000000000..75c36897b --- /dev/null +++ b/dGame/dComponents/ItemComponent.h @@ -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__ diff --git a/dGame/dComponents/LUPExhibitComponent.cpp b/dGame/dComponents/LUPExhibitComponent.cpp index 7b8c85ba7..9034d7aa2 100644 --- a/dGame/dComponents/LUPExhibitComponent.cpp +++ b/dGame/dComponents/LUPExhibitComponent.cpp @@ -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; + } } diff --git a/dGame/dComponents/LUPExhibitComponent.h b/dGame/dComponents/LUPExhibitComponent.h index 587d1b2f9..bfa86b48a 100644 --- a/dGame/dComponents/LUPExhibitComponent.h +++ b/dGame/dComponents/LUPExhibitComponent.h @@ -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 @@ class LUPExhibitComponent : public Component /** * The list of possible exhibits to show */ - std::vector m_Exhibits; + const std::vector 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; }; diff --git a/dGame/dComponents/LevelProgressionComponent.cpp b/dGame/dComponents/LevelProgressionComponent.cpp index 8163e7363..4c08fe203 100644 --- a/dGame/dComponents/LevelProgressionComponent.cpp +++ b/dGame/dComponents/LevelProgressionComponent.cpp @@ -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(characterVersion); + auto* controllablePhysicsComponent = m_ParentEntity->GetComponent(); + + 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(); const auto& rewards = rewardsTable->GetByLevelID(m_Level); - bool rewardingItem = rewards.size() > 0; + if (rewards.empty()) return; - auto* inventoryComponent = m_Parent->GetComponent(); - auto* controllablePhysicsComponent = m_Parent->GetComponent(); + auto* inventoryComponent = m_ParentEntity->GetComponent(); + auto* controllablePhysicsComponent = m_ParentEntity->GetComponent(); 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(reward->value) ); + SetSpeedBase(static_cast(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(); + auto* controllablePhysicsComponent = m_ParentEntity->GetComponent(); if (controllablePhysicsComponent) controllablePhysicsComponent->SetSpeedMultiplier(m_SpeedBase / 500.0f); } diff --git a/dGame/dComponents/LevelProgressionComponent.h b/dGame/dComponents/LevelProgressionComponent.h index 06908a810..5be9756a2 100644 --- a/dGame/dComponents/LevelProgressionComponent.h +++ b/dGame/dComponents/LevelProgressionComponent.h @@ -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 @@ class LevelProgressionComponent : public Component { * 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 @@ class LevelProgressionComponent : public Component { float m_SpeedBase; /** - * The Character format version + * The Character format version. Certain bug fixes increment this version number. */ eCharacterVersion m_CharacterVersion; diff --git a/dGame/dComponents/MinigameControlComponent.cpp b/dGame/dComponents/MinigameControlComponent.cpp new file mode 100644 index 000000000..a0088d6bf --- /dev/null +++ b/dGame/dComponents/MinigameControlComponent.cpp @@ -0,0 +1,7 @@ + +#include "Entity.h" +#include "MinigameControlComponent.h" + +MinigameControlComponent::MinigameControlComponent(Entity* parent, int32_t componentId) : ActivityComponent(parent, componentId) { + +} diff --git a/dGame/dComponents/MinigameControlComponent.h b/dGame/dComponents/MinigameControlComponent.h new file mode 100644 index 000000000..cfeef3f3d --- /dev/null +++ b/dGame/dComponents/MinigameControlComponent.h @@ -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__ diff --git a/dGame/dComponents/MissionComponent.cpp b/dGame/dComponents/MissionComponent.cpp index 8f61c1aaf..315d9f72c 100644 --- a/dGame/dComponents/MissionComponent.cpp +++ b/dGame/dComponents/MissionComponent.cpp @@ -1,9 +1,8 @@ /* * Darkflame Universe - * Copyright 2019 + * Copyright 2023 */ -#include #include #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> 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& 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(taskType)) continue; + std::for_each(mission->GetTasks().begin(), mission->GetTasks().end(), [value, taskType](MissionTask* element) { + if (element->GetType() != static_cast(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(); const auto missions = missionsTable->Query([=](const CDMissions& entry) { - return entry.id == static_cast(missionId); + return entry.id == static_cast(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); - } - } - - return any; -#else - auto* missionTasksTable = CDClientManager::Instance().GetTable(); - auto* missionsTable = CDClientManager::Instance().GetTable(); - - auto tasks = missionTasksTable->Query([=](const CDMissionTasks& entry) { - return entry.taskType == static_cast(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(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); - } + if (!progress) continue; + // Progress mission to bring it up to speed + instance->Progress(type, value, associate, targets, count); } return any; -#endif } -const std::vector& MissionComponent::QueryAchievements(eMissionTaskType type, int32_t value, const std::string targets) { +const std::vector& 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& 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; - - while (std::getline(stream, token, ',')) { - try { - if (std::stoi(token) == value) { - result.push_back(mission.id); - - continue; - } - } catch (std::invalid_argument& exception) { - // Ignored + for (const auto& possibleMissionStr : GeneralUtils::SplitString(task.targetGroup, ',')) { + uint32_t possibleMission; + if (GeneralUtils::TryParse(possibleMissionStr, possibleMission) && possibleMission == value) { + result.push_back(cdMission.id); + + 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(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; - - if (mission) { - const auto complete = mission->IsComplete(); + for (const auto& [missionId, mission] : m_Missions) { + if (!mission) continue; + const auto complete = mission->IsComplete(); - auto* m = doc->NewElement("m"); + auto* missionElement = doc->NewElement("m"); - if (complete) { - mission->UpdateXml(m); + if (!complete && mission->IsMission()) missionElement->SetAttribute("o", mission->GetUniqueMissionOrderID()); - done->LinkEndChild(m); + mission->UpdateXml(missionElement); - 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; } diff --git a/dGame/dComponents/MissionComponent.h b/dGame/dComponents/MissionComponent.h index eeaaa7260..1576c0863 100644 --- a/dGame/dComponents/MissionComponent.h +++ b/dGame/dComponents/MissionComponent.h @@ -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 @@ class MissionComponent : public Component * 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& GetMissions() const; + const std::unordered_map& 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 @@ class MissionComponent : public Component * @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 @@ class MissionComponent : public Component * @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 @@ class MissionComponent : public Component * @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 @@ class MissionComponent : public Component * @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 @@ class MissionComponent : public Component * @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 @@ class MissionComponent : public Component * @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 @@ class MissionComponent : public Component * @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 @@ class MissionComponent : public Component * @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& QueryAchievements(eMissionTaskType type, int32_t value, const std::string targets); + static const std::vector& 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 diff --git a/dGame/dComponents/MissionOfferComponent.cpp b/dGame/dComponents/MissionOfferComponent.cpp index e4c94ebd9..7dbaecdcd 100644 --- a/dGame/dComponents/MissionOfferComponent.cpp +++ b/dGame/dComponents/MissionOfferComponent.cpp @@ -1,6 +1,6 @@ /* * Darkflame Universe - * Copyright 2019 + * Copyright 2023 */ #include @@ -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(); -uint32_t OfferedMission::GetMissionId() const { - return this->missionId; -} - -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(); - - 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(); + auto missions = missionNpcComponentTable->Query([=](const CDMissionNPCComponent& entry) { + return entry.id == static_cast(m_ComponentId); + }); - auto missions = missionNpcComponentTable->Query([=](const CDMissionNPCComponent& entry) { - return entry.id == static_cast(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(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(entity->GetComponent(eReplicaComponentType::MISSION)); + auto* missionComponent = entity->GetComponent(); 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 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 (!Mission::IsValidMission(missionId, info)) { - continue; - } - - const auto& randomPool = info.randomPool; - const auto isRandom = info.isRandom; + if (!canAccept || !Mission::IsValidMission(missionId, missionInfo)) continue; - 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; + // This means the mission is part of a random pool of missions. + if (missionInfo.isRandom && missionInfo.randomPool.empty()) continue; - std::vector randomMissionPool; + if (missionInfo.isRandom && !missionInfo.randomPool.empty()) { + auto randomMissionPoolStr = GeneralUtils::SplitString(missionInfo.randomPool, ','); - 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 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 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(0, canAcceptPool.size() - 1)]; + const auto selected = canAcceptPool[GeneralUtils::GenerateRandomNumber(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()); } } } diff --git a/dGame/dComponents/MissionOfferComponent.h b/dGame/dComponents/MissionOfferComponent.h index 6e22ca059..32a519672 100644 --- a/dGame/dComponents/MissionOfferComponent.h +++ b/dGame/dComponents/MissionOfferComponent.h @@ -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 "dCommonVars.h" -#include "Component.h" +#include #include #include + +#include "dCommonVars.h" +#include "Component.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 @@ struct OfferedMission { /** * 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, const int32_t componentId = -1); - MissionOfferComponent(Entity* parent, LOT parentLot); - ~MissionOfferComponent() override; + 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 @@ class MissionOfferComponent : public Component { /** * The missions this entity has to offer */ - std::vector offeredMissions; + std::vector> offeredMissions; + + int32_t m_ComponentId; }; -#endif // MISSIONOFFERCOMPONENT_H +#endif // __MISSIONOFFERCOMPONENT_H__ diff --git a/dGame/dComponents/ModelBehaviorComponent.cpp b/dGame/dComponents/ModelBehaviorComponent.cpp new file mode 100644 index 000000000..634724c13 --- /dev/null +++ b/dGame/dComponents/ModelBehaviorComponent.cpp @@ -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; + } +} + diff --git a/dGame/dComponents/ModelComponent.h b/dGame/dComponents/ModelBehaviorComponent.h similarity index 60% rename from dGame/dComponents/ModelComponent.h rename to dGame/dComponents/ModelBehaviorComponent.h index b52248697..989bdab96 100644 --- a/dGame/dComponents/ModelComponent.h +++ b/dGame/dComponents/ModelBehaviorComponent.h @@ -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 @@ class ModelComponent : public Component { * 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 @@ class ModelComponent : public Component { * 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 @@ class ModelComponent : public Component { */ NiQuaternion m_OriginalRotation; - /** - * The ID of the user that made the model - */ - LWOOBJID m_userModelID; }; diff --git a/dGame/dComponents/ModelComponent.cpp b/dGame/dComponents/ModelComponent.cpp deleted file mode 100644 index 74f614d10..000000000 --- a/dGame/dComponents/ModelComponent.cpp +++ /dev/null @@ -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(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(m_userModelID != LWOOBJID_EMPTY ? m_userModelID : m_Parent->GetObjectID()); - outBitStream->Write(0); - outBitStream->Write0(); - } - - //actual model component: - outBitStream->Write1(); // Yes we are writing model info - outBitStream->Write0(); // Is pickable - outBitStream->Write(2); // Physics type - outBitStream->Write(m_OriginalPosition); // Original position - outBitStream->Write(m_OriginalRotation); // Original rotation - - outBitStream->Write1(); // We are writing behavior info - outBitStream->Write(0); // Number of behaviors - outBitStream->Write1(); // Is this model paused - if (bIsInitialUpdate) outBitStream->Write0(); // We are not writing model editing info -} diff --git a/dGame/dComponents/ModuleAssemblyComponent.cpp b/dGame/dComponents/ModuleAssemblyComponent.cpp index 8e197f0c2..f00bc8808 100644 --- a/dGame/dComponents/ModuleAssemblyComponent.cpp +++ b/dGame/dComponents/ModuleAssemblyComponent.cpp @@ -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(m_AssemblyPartsLOTs.size())); - for (char16_t character : m_AssemblyPartsLOTs) { - outBitStream->Write(character); - } + outBitStream->Write(m_AssemblyPartsLOTs.size()); + for (const char16_t character : m_AssemblyPartsLOTs) { + outBitStream->Write(character); } } - -void ModuleAssemblyComponent::Update(float deltaTime) { - -} - diff --git a/dGame/dComponents/ModuleAssemblyComponent.h b/dGame/dComponents/ModuleAssemblyComponent.h index 39670c9a2..1e94eab7d 100644 --- a/dGame/dComponents/ModuleAssemblyComponent.h +++ b/dGame/dComponents/ModuleAssemblyComponent.h @@ -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 @@ class ModuleAssemblyComponent : public Component { * 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 @@ class ModuleAssemblyComponent : public Component { */ std::u16string m_AssemblyPartsLOTs; }; + +#endif //!__MODULEASSEMBLYCOMPONENT__H__ diff --git a/dGame/dComponents/MovementAIComponent.cpp b/dGame/dComponents/MovementAIComponent.cpp index 7acec5f75..7c97c85ab 100644 --- a/dGame/dComponents/MovementAIComponent.cpp +++ b/dGame/dComponents/MovementAIComponent.cpp @@ -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 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(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(); + m_NextWaypoint = GetCurrentPosition(); +} + +void MovementAIComponent::LoadConfigData() { + bool useWanderDB = m_ParentEntity->GetVar(u"usewanderdb"); + + if (useWanderDB) return; + const auto wanderOverride = m_ParentEntity->GetVarAs(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(); + 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 @@ void MovementAIComponent::Update(const float deltaTime) { 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 path) { - std::reverse(path.begin(), path.end()); - - for (const auto& point : path) { - m_Queue.push(point); +void MovementAIComponent::SetPath(const std::vector& 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 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(); - CDPhysicsComponentTable* physicsComponentTable = CDClientManager::Instance().GetTable(); + auto* componentRegistryTable = CDClientManager::Instance().GetTable(); + auto* physicsComponentTable = CDClientManager::Instance().GetTable(); 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 @@ float MovementAIComponent::GetBaseSpeed(LOT lot) { return speed; } -void MovementAIComponent::SetPosition(const NiPoint3& value) { - auto* controllablePhysicsComponent = m_Parent->GetComponent(); - - if (controllablePhysicsComponent != nullptr) { - controllablePhysicsComponent->SetPosition(value); - - return; - } - - auto* simplePhysicsComponent = m_Parent->GetComponent(); - - 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(); - - if (controllablePhysicsComponent != nullptr) { - controllablePhysicsComponent->SetRotation(value); - - return; - } - - auto* simplePhysicsComponent = m_Parent->GetComponent(); - - 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(); +void MovementAIComponent::SetVelocity(const NiPoint3& value) const { + auto* controllablePhysicsComponent = m_ParentEntity->GetComponent(); - if (controllablePhysicsComponent != nullptr) { + if (controllablePhysicsComponent) { controllablePhysicsComponent->SetVelocity(value); return; } - auto* simplePhysicsComponent = m_Parent->GetComponent(); + auto* simplePhysicsComponent = m_ParentEntity->GetComponent(); - 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 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(); } diff --git a/dGame/dComponents/MovementAIComponent.h b/dGame/dComponents/MovementAIComponent.h index 3c9044aae..69b4d060f 100644 --- a/dGame/dComponents/MovementAIComponent.h +++ b/dGame/dComponents/MovementAIComponent.h @@ -3,18 +3,16 @@ * Copyright 2018 */ -#ifndef MOVEMENTAICOMPONENT_H -#define MOVEMENTAICOMPONENT_H - -#include "BitStream.h" -#include "Entity.h" -#include "GameMessages.h" -#include "EntityManager.h" -#include "Game.h" -#include "dLogger.h" +#ifndef __MOVEMENTAICOMPONENT_H__ +#define __MOVEMENTAICOMPONENT_H__ +#pragma once + +#include +#include +#include + #include "Component.h" #include "eReplicaComponentType.h" -#include 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 @@ class MovementAIComponent : public Component { * 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 @@ class MovementAIComponent : public Component { * 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 @@ class MovementAIComponent : public Component { * 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 @@ class MovementAIComponent : public Component { * 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 @@ class MovementAIComponent : public Component { * Sets a path to follow for the AI * @param path the path to follow */ - void SetPath(std::vector path); + void SetPath(const std::vector& 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 @@ class MovementAIComponent : public Component { * Cache of all lots and their respective speeds */ static std::map m_PhysicsSpeedCache; + + int32_t m_ComponentId; }; -#endif // MOVEMENTAICOMPONENT_H +#endif // __MOVEMENTAICOMPONENT_H__ diff --git a/dGame/dComponents/MovingPlatformComponent.cpp b/dGame/dComponents/MovingPlatformComponent.cpp index 2666c60ce..6872f65a6 100644 --- a/dGame/dComponents/MovingPlatformComponent.cpp +++ b/dGame/dComponents/MovingPlatformComponent.cpp @@ -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(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(false); - outBitStream->Write(false); + outBitStream->Write0(); + outBitStream->Write0(); return; } - outBitStream->Write(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(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(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 { diff --git a/dGame/dComponents/MovingPlatformComponent.h b/dGame/dComponents/MovingPlatformComponent.h index 9e4c1ecfe..232505df6 100644 --- a/dGame/dComponents/MovingPlatformComponent.h +++ b/dGame/dComponents/MovingPlatformComponent.h @@ -6,12 +6,10 @@ #ifndef MOVINGPLATFORMCOMPONENT_H #define MOVINGPLATFORMCOMPONENT_H -#include "RakNetTypes.h" #include "NiPoint3.h" #include #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 @@ class MoverSubComponent { */ 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; diff --git a/dGame/dComponents/MultiZoneEntranceComponent.cpp b/dGame/dComponents/MultiZoneEntranceComponent.cpp new file mode 100644 index 000000000..9f823282f --- /dev/null +++ b/dGame/dComponents/MultiZoneEntranceComponent.cpp @@ -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(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(); + 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(); + if (!rocketLaunchpadControlComponent || index >= m_LUPWorlds.size()) return; + + rocketLaunchpadControlComponent->Launch(originator, m_LUPWorlds[index], 0); +} diff --git a/dGame/dComponents/RocketLaunchLupComponent.h b/dGame/dComponents/MultiZoneEntranceComponent.h similarity index 69% rename from dGame/dComponents/RocketLaunchLupComponent.h rename to dGame/dComponents/MultiZoneEntranceComponent.h index 226fa1b23..2b0013a57 100644 --- a/dGame/dComponents/RocketLaunchLupComponent.h +++ b/dGame/dComponents/MultiZoneEntranceComponent.h @@ -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 @@ class RocketLaunchLupComponent : public Component { * @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 m_LUPWorlds{}; + std::vector m_LUPWorlds; }; diff --git a/dGame/dComponents/MutableModelBehaviorComponent.cpp b/dGame/dComponents/MutableModelBehaviorComponent.cpp new file mode 100644 index 000000000..f4a52338d --- /dev/null +++ b/dGame/dComponents/MutableModelBehaviorComponent.cpp @@ -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; + } +} + diff --git a/dGame/dComponents/MutableModelBehaviorComponent.h b/dGame/dComponents/MutableModelBehaviorComponent.h new file mode 100644 index 000000000..b869c1f4b --- /dev/null +++ b/dGame/dComponents/MutableModelBehaviorComponent.h @@ -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; +}; diff --git a/dGame/dComponents/PetComponent.cpp b/dGame/dComponents/PetComponent.cpp index c37ce6a0b..941c91312 100644 --- a/dGame/dComponents/PetComponent.cpp +++ b/dGame/dComponents/PetComponent.cpp @@ -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 PetComponent::buildCache{}; -std::unordered_map PetComponent::currentActivities{}; -std::unordered_map PetComponent::activePets{}; +std::unordered_map PetComponent::buildCache; +std::unordered_map PetComponent::currentActivities; +std::unordered_map 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 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 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(u"CheckPrecondition")); + auto checkPreconditions = GeneralUtils::UTF16ToWTF8(parent->GetVar(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(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(static_cast(m_Status)); - outBitStream->Write(static_cast(tamed ? m_Ability : PetAbilityType::Invalid)); // Something with the overhead icon? + outBitStream->Write(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 (tamed) outBitStream->Write(m_Owner); - if (bIsInitialUpdate) { - outBitStream->Write(tamed); - if (tamed) { - outBitStream->Write(m_ModerationStatus); + if (!bIsInitialUpdate) return; - const auto nameData = GeneralUtils::UTF8ToUTF16(m_Name); - const auto ownerNameData = GeneralUtils::UTF8ToUTF16(m_OwnerName); + outBitStream->Write(tamed); + if (tamed) { + outBitStream->Write(m_ModerationStatus); - outBitStream->Write(static_cast(nameData.size())); - for (const auto c : nameData) { - outBitStream->Write(c); - } + outBitStream->Write(m_Name.size()); + for (const auto c : m_Name) { + outBitStream->Write(c); + } - outBitStream->Write(static_cast(ownerNameData.size())); - for (const auto c : ownerNameData) { - outBitStream->Write(c); - } + outBitStream->Write(m_OwnerName.size()); + for (const auto c : m_OwnerName) { + outBitStream->Write(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(); - if (inventoryComponent == nullptr) { - return; - } + if (!inventoryComponent) return; - if (m_Preconditions != nullptr && !m_Preconditions->Check(originator, true)) { + if (m_Preconditions && !m_Preconditions->Check(originator, true)) { return; } - auto* movementAIComponent = m_Parent->GetComponent(); + auto* movementAIComponent = m_ParentEntity->GetComponent(); - 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(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(); - 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(u"interaction_distance"); + float interactionDistance = m_ParentEntity->GetVar(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(); - - if (m_MovementAI == nullptr) { - return; + if (!m_MovementAI) { + m_MovementAI = m_ParentEntity->GetComponent(); + 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 @@ void PetComponent::Update(float deltaTime) { 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(); - 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(); - 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(); - 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(); - 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(); - 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()); - - if (cached == buildCache.end()) { - return; - } + const auto& cached = buildCache.find(m_ParentEntity->GetLOT()); - 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(); + if (!m_MovementAI) m_MovementAI = m_ParentEntity->GetComponent(); - 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(info.wanderDelayMax); - m_Timer = (div == 0 ? 0 : GeneralUtils::GenerateRandomNumber(0, div)) + info.wanderDelayMin; //set a random timer to stay put. + const auto div = static_cast(info.wanderDelayMax); + m_Timer = (div == 0 ? 0 : GeneralUtils::GenerateRandomNumber(0, div)) + info.wanderDelayMin; //set a random timer to stay put. - const float radius = info.wanderRadius * sqrt(static_cast(GeneralUtils::GenerateRandomNumber(0, 1))); //our wander radius + a bit of random range - const float theta = ((static_cast(GeneralUtils::GenerateRandomNumber(0, 1)) * 2 * PI)); + const float radius = info.wanderRadius * sqrt(GeneralUtils::GenerateRandomNumber(0.0f, 1.0f)); //our wander radius + a bit of random range + const float theta = GeneralUtils::GenerateRandomNumber(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(); + auto* playerDestroyableComponent = playerEntity->GetComponent(); 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(); - 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::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(); } -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 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 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 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 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); } diff --git a/dGame/dComponents/PetComponent.h b/dGame/dComponents/PetComponent.h index b3d089a93..1c1d6cfcb 100644 --- a/dGame/dComponents/PetComponent.h +++ b/dGame/dComponents/PetComponent.h @@ -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 @@ class PetComponent : public Component * 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 @@ class PetComponent : public Component * 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 @@ class PetComponent : public Component */ 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 @@ class PetComponent : public Component * @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 @@ class PetComponent : public Component /** * Flags that indicate that a player has tamed a pet, indexed by the LOT of the pet */ - static std::map petFlags; + static std::map petFlags; /** * The ID of the component in the pet component table @@ -359,5 +354,5 @@ class PetComponent : public Component /** * The rate at which imagination is drained from the user for having the pet out. */ - float imaginationDrainRate; + float m_ImaginationDrainRate; }; diff --git a/dGame/dComponents/PhantomPhysicsComponent.cpp b/dGame/dComponents/PhantomPhysicsComponent.cpp index e6272aa4a..5d1f2d122 100644 --- a/dGame/dComponents/PhantomPhysicsComponent.cpp +++ b/dGame/dComponents/PhantomPhysicsComponent.cpp @@ -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 - - if (m_Parent->GetVar(u"create_physics")) { - CreatePhysics(); - } - - if (m_Parent->GetVar(u"respawnVol")) { - m_IsRespawnVolume = true; - } - - if (m_IsRespawnVolume) { - { - auto respawnString = std::stringstream(m_Parent->GetVarAsString(u"rspPos")); - - std::string segment; - std::vector 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 seglist; - - while (std::getline(respawnString, segment, '\x1f')) { - seglist.push_back(segment); - } + m_Direction = NiPoint3::ZERO; +} - 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); - } - } - - if (data->GetKey() == u"respawnVol") { - if (bool(std::stoi(data->GetValueAsString()))) { - m_IsRespawnVolume = true; - } - } - - if (m_IsRespawnVolume) { - if (data->GetKey() == u"rspPos") { - //Joy, we get to split strings! - std::stringstream test(data->GetValueAsString()); - std::string segment; - std::vector seglist; - - while (std::getline(test, segment, '\x1f')) { - seglist.push_back(segment); - } - - 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 seglist; - - while (std::getline(test, segment, '\x1f')) { - seglist.push_back(segment); - } + auto* compRegistryTable = CDClientManager::Instance().GetTable(); + auto componentID = compRegistryTable->GetByIDAndType(m_ParentEntity->GetLOT(), eReplicaComponentType::PHANTOM_PHYSICS); + + auto* physCompTable = CDClientManager::Instance().GetTable(); + + if (!physCompTable) return; + + auto* info = physCompTable->GetByID(componentID); + if (!info || info->physicsAsset.empty() || info->physicsAsset == "NO_PHYSICS") return; + + 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); - 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; - } - } +void PhantomPhysicsComponent::LoadConfigData() { + if (m_ParentEntity->GetVar(u"create_physics")) { + CreatePhysics(); } - */ - - if (!m_HasCreatedPhysics) { - CDComponentsRegistryTable* compRegistryTable = CDClientManager::Instance().GetTable(); - auto componentID = compRegistryTable->GetByIDAndType(m_Parent->GetLOT(), eReplicaComponentType::PHANTOM_PHYSICS); - - CDPhysicsComponentTable* physComp = CDClientManager::Instance().GetTable(); - - 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); - } + if (m_ParentEntity->GetVar(u"respawnVol")) { + m_IsRespawnVolume = true; } -} -PhantomPhysicsComponent::~PhantomPhysicsComponent() { - if (m_dpEntity) { - dpWorld::Instance().RemoveEntity(m_dpEntity); + 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(u"primitiveModelType"); - x = m_Parent->GetVar(u"primitiveModelValueX"); - y = m_Parent->GetVar(u"primitiveModelValueY"); - z = m_Parent->GetVar(u"primitiveModelValueZ"); + if (m_ParentEntity->HasVar(u"primitiveModelType")) { + type = m_ParentEntity->GetVar(u"primitiveModelType"); + pos.x = m_ParentEntity->GetVar(u"primitiveModelValueX"); + pos.y = m_ParentEntity->GetVar(u"primitiveModelValueY"); + pos.z = m_ParentEntity->GetVar(u"primitiveModelValueZ"); } else { - CDComponentsRegistryTable* compRegistryTable = CDClientManager::Instance().GetTable(); - auto componentID = compRegistryTable->GetByIDAndType(m_Parent->GetLOT(), eReplicaComponentType::PHANTOM_PHYSICS); + auto* compRegistryTable = CDClientManager::Instance().GetTable(); + auto componentID = compRegistryTable->GetByIDAndType(m_ParentEntity->GetLOT(), eReplicaComponentType::PHANTOM_PHYSICS); - CDPhysicsComponentTable* physComp = CDClientManager::Instance().GetTable(); + auto* physCompTable = CDClientManager::Instance().GetTable(); - 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(m_dpEntity->GetShape()); + Game::logger->Log("PhantomPhysicsComponent", "objectId is %llu", m_ParentEntity->GetObjectID()); + auto box = dynamic_cast(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); } diff --git a/dGame/dComponents/PhantomPhysicsComponent.h b/dGame/dComponents/PhantomPhysicsComponent.h index cc0d18447..cbba07a0b 100644 --- a/dGame/dComponents/PhantomPhysicsComponent.h +++ b/dGame/dComponents/PhantomPhysicsComponent.h @@ -1,22 +1,18 @@ /* * Darkflame Universe - * Copyright 2018 + * Copyright 2023 */ #pragma once -#include "NiPoint3.h" -#include "NiQuaternion.h" -#include "BitStream.h" -#include -#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 @@ class PhantomPhysicsComponent : public Component { * 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 @@ class PhantomPhysicsComponent : public Component { /** * 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: diff --git a/dGame/dComponents/PlayerForcedMovementComponent.cpp b/dGame/dComponents/PlayerForcedMovementComponent.cpp index 76993507f..d92af485d 100644 --- a/dGame/dComponents/PlayerForcedMovementComponent.cpp +++ b/dGame/dComponents/PlayerForcedMovementComponent.cpp @@ -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; } diff --git a/dGame/dComponents/PlayerForcedMovementComponent.h b/dGame/dComponents/PlayerForcedMovementComponent.h index 90708c9aa..b67c3f2db 100644 --- a/dGame/dComponents/PlayerForcedMovementComponent.h +++ b/dGame/dComponents/PlayerForcedMovementComponent.h @@ -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 @@ class PlayerForcedMovementComponent : public Component { * * @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; } diff --git a/dGame/dComponents/PossessableComponent.cpp b/dGame/dComponents/PossessableComponent.cpp index 5c45a6c19..326515345 100644 --- a/dGame/dComponents/PossessableComponent.cpp +++ b/dGame/dComponents/PossessableComponent.cpp @@ -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(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(componentId)); + query.bind(1, static_cast(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(); + auto* possessor = originator->GetComponent(); if (possessor) { - possessor->Mount(m_Parent); + possessor->Mount(m_ParentEntity); } } diff --git a/dGame/dComponents/PossessableComponent.h b/dGame/dComponents/PossessableComponent.h index 2026c11e8..39e12f450 100644 --- a/dGame/dComponents/PossessableComponent.h +++ b/dGame/dComponents/PossessableComponent.h @@ -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 @@ class PossessableComponent : public Component { * 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 @@ class PossessableComponent : public Component { * 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 @@ class PossessableComponent : public Component { /** * 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 @@ class PossessableComponent : public Component { * */ bool m_ItemSpawned = false; + + int32_t m_ComponentId = -1; }; diff --git a/dGame/dComponents/PossessionComponent.cpp b/dGame/dComponents/PossessionComponent.cpp new file mode 100644 index 000000000..1a4098435 --- /dev/null +++ b/dGame/dComponents/PossessionComponent.cpp @@ -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(); + 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(); + if (possessableComponent) { + possessableComponent->SetPossessor(m_ParentEntity->GetObjectID()); + SetPossessable(mount->GetObjectID()); + SetPossessableType(possessableComponent->GetPossessionType()); + } + + auto* characterComponent = m_ParentEntity->GetComponent(); + 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(); + if (possessableComponent) { + possessableComponent->SetPossessor(LWOOBJID_EMPTY); + if (forceDismount) possessableComponent->ForceDepossess(); + } + EntityManager::Instance()->SerializeEntity(m_ParentEntity); + EntityManager::Instance()->SerializeEntity(mount); + + auto* characterComponent = m_ParentEntity->GetComponent(); + if (characterComponent) characterComponent->SetIsRacing(false); +} diff --git a/dGame/dComponents/PossessorComponent.h b/dGame/dComponents/PossessionComponent.h similarity index 80% rename from dGame/dComponents/PossessorComponent.h rename to dGame/dComponents/PossessionComponent.h index 4456af273..cbfb6fb5d 100644 --- a/dGame/dComponents/PossessorComponent.h +++ b/dGame/dComponents/PossessionComponent.h @@ -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 @@ class PossessorComponent : public Component { * 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 @@ class PossessorComponent : public Component { * 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 @@ class PossessorComponent : public Component { 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; diff --git a/dGame/dComponents/PossessorComponent.cpp b/dGame/dComponents/PossessorComponent.cpp deleted file mode 100644 index 387b34791..000000000 --- a/dGame/dComponents/PossessorComponent.cpp +++ /dev/null @@ -1,84 +0,0 @@ -#include "PossessorComponent.h" -#include "PossessableComponent.h" -#include "CharacterComponent.h" -#include "EntityManager.h" -#include "GameMessages.h" -#include "eUnequippableActiveType.h" -#include "eControlScheme.h" -#include "eStateChangeType.h" - -PossessorComponent::PossessorComponent(Entity* parent) : Component(parent) { - m_Possessable = LWOOBJID_EMPTY; -} - -PossessorComponent::~PossessorComponent() { - if (m_Possessable != LWOOBJID_EMPTY) { - auto* mount = EntityManager::Instance()->GetEntity(m_Possessable); - if (mount) { - auto* possessable = mount->GetComponent(); - if (possessable) { - if (possessable->GetIsItemSpawned()) { - GameMessages::SendMarkInventoryItemAsActive(m_Parent->GetObjectID(), false, eUnequippableActiveType::MOUNT, GetMountItemID(), m_Parent->GetSystemAddress()); - } - possessable->Dismount(); - } - } - } -} - -void PossessorComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags) { - outBitStream->Write(m_DirtyPossesor || bIsInitialUpdate); - if (m_DirtyPossesor || bIsInitialUpdate) { - m_DirtyPossesor = false; - outBitStream->Write(m_Possessable != LWOOBJID_EMPTY); - if (m_Possessable != LWOOBJID_EMPTY) { - outBitStream->Write(m_Possessable); - } - outBitStream->Write(m_PossessableType); - } -} - -void PossessorComponent::Mount(Entity* mount) { - // Don't do anything if we are busy dismounting - if (GetIsDismounting() || !mount) return; - - GameMessages::SendSetMountInventoryID(m_Parent, mount->GetObjectID(), UNASSIGNED_SYSTEM_ADDRESS); - auto* possessableComponent = mount->GetComponent(); - if (possessableComponent) { - possessableComponent->SetPossessor(m_Parent->GetObjectID()); - SetPossessable(mount->GetObjectID()); - SetPossessableType(possessableComponent->GetPossessionType()); - } - - auto characterComponent = m_Parent->GetComponent(); - if (characterComponent) characterComponent->SetIsRacing(true); - - // GM's to send - GameMessages::SendSetJetPackMode(m_Parent, false); - GameMessages::SendVehicleUnlockInput(mount->GetObjectID(), false, m_Parent->GetSystemAddress()); - GameMessages::SendSetStunned(m_Parent->GetObjectID(), eStateChangeType::PUSH, m_Parent->GetSystemAddress(), LWOOBJID_EMPTY, true, false, true, false, false, false, false, true, true, true, true, true, true, true, true, true); - - EntityManager::Instance()->SerializeEntity(m_Parent); - EntityManager::Instance()->SerializeEntity(mount); -} - -void PossessorComponent::Dismount(Entity* mount, bool forceDismount) { - // Don't do anything if we are busy dismounting - if (GetIsDismounting() || !mount) return; - SetIsDismounting(true); - - if (mount) { - auto* possessableComponent = mount->GetComponent(); - if (possessableComponent) { - possessableComponent->SetPossessor(LWOOBJID_EMPTY); - if (forceDismount) possessableComponent->ForceDepossess(); - } - EntityManager::Instance()->SerializeEntity(m_Parent); - EntityManager::Instance()->SerializeEntity(mount); - - auto characterComponent = m_Parent->GetComponent(); - if (characterComponent) characterComponent->SetIsRacing(false); - } - // Make sure we don't have wacky controls - GameMessages::SendSetPlayerControlScheme(m_Parent, eControlScheme::SCHEME_A); -} diff --git a/dGame/dComponents/PropertyComponent.cpp b/dGame/dComponents/PropertyComponent.cpp index 4f8df40cc..81decc828 100644 --- a/dGame/dComponents/PropertyComponent.cpp +++ b/dGame/dComponents/PropertyComponent.cpp @@ -2,10 +2,4 @@ #include "GameMessages.h" #include "dZoneManager.h" -PropertyComponent::PropertyComponent(Entity* parent) : Component(parent) { - m_PropertyName = parent->GetVar(u"propertyName"); - m_PropertyState = new PropertyState(); -} - -PropertyComponent::~PropertyComponent() = default; - +PropertyComponent::PropertyComponent(Entity* parent) : Component(parent) { } diff --git a/dGame/dComponents/PropertyComponent.h b/dGame/dComponents/PropertyComponent.h index 41f93677a..6b648e094 100644 --- a/dGame/dComponents/PropertyComponent.h +++ b/dGame/dComponents/PropertyComponent.h @@ -3,32 +3,16 @@ * Copyright 2018 */ -#ifndef PROPERTYCOMPONENT_H -#define PROPERTYCOMPONENT_H +#ifndef __PROPERTYCOMPONENT_H__ +#define __PROPERTYCOMPONENT_H__ -#include "BitStream.h" -#include "Entity.h" #include "Component.h" #include "eReplicaComponentType.h" -struct PropertyState { - LWOOBJID ownerID; - LWOOBJID propertyID; - bool rented; -}; - -/** - * This component is unused and has no functionality - */ class PropertyComponent : public Component { public: - static const eReplicaComponentType ComponentType = eReplicaComponentType::PROPERTY; - explicit PropertyComponent(Entity* parentEntity); - ~PropertyComponent() override; - [[nodiscard]] PropertyState* GetPropertyState() const { return m_PropertyState; }; -private: - PropertyState* m_PropertyState; - std::string m_PropertyName; + inline static const eReplicaComponentType ComponentType = eReplicaComponentType::PROPERTY; + PropertyComponent(Entity* parentEntity); }; -#endif // PROPERTYCOMPONENT_H +#endif // __PROPERTYCOMPONENT_H__ diff --git a/dGame/dComponents/PropertyEntranceComponent.cpp b/dGame/dComponents/PropertyEntranceComponent.cpp index bff917d8a..df24daeb0 100644 --- a/dGame/dComponents/PropertyEntranceComponent.cpp +++ b/dGame/dComponents/PropertyEntranceComponent.cpp @@ -1,6 +1,6 @@ #include "PropertyEntranceComponent.h" -#include +#include "CDPropertyEntranceComponentTable.h" #include "Character.h" #include "Database.h" @@ -14,15 +14,19 @@ #include "Amf3.h" #include "eObjectBits.h" #include "eGameMasterLevel.h" +#include "DluAssert.h" -PropertyEntranceComponent::PropertyEntranceComponent(uint32_t componentID, Entity* parent) : Component(parent) { - this->propertyQueries = {}; +PropertyEntranceComponent::PropertyEntranceComponent(Entity* parent, int32_t componentID) : Component(parent) { + this->propertyQueries.clear(); + m_ComponentId = componentID; +} +void PropertyEntranceComponent::LoadTemplateData() { auto table = CDClientManager::Instance().GetTable(); - const auto& entry = table->GetByID(componentID); + const auto& entry = table->GetByID(m_ComponentId); - this->m_MapID = entry.mapID; - this->m_PropertyName = entry.propertyName; + m_MapID = entry.mapID; + m_PropertyName = entry.propertyName; } void PropertyEntranceComponent::OnUse(Entity* entity) { @@ -32,7 +36,7 @@ void PropertyEntranceComponent::OnUse(Entity* entity) { auto* rocket = entity->GetComponent()->RocketEquip(entity); if (!rocket) return; - GameMessages::SendPropertyEntranceBegin(m_Parent->GetObjectID(), entity->GetSystemAddress()); + GameMessages::SendPropertyEntranceBegin(m_ParentEntity->GetObjectID(), entity->GetSystemAddress()); AMFArrayValue args; @@ -42,293 +46,247 @@ void PropertyEntranceComponent::OnUse(Entity* entity) { } void PropertyEntranceComponent::OnEnterProperty(Entity* entity, uint32_t index, bool returnToZone, const SystemAddress& sysAddr) { + if (!entity->GetCharacter()) { + Game::logger->Log("PropertyEntranceComponent", "Entity %llu attempted to enter a property with no character attached.", entity->GetObjectID()); + } LWOCLONEID cloneId = 0; if (index == -1 && !returnToZone) { cloneId = entity->GetCharacter()->GetPropertyCloneID(); - } else if (index == -1 && returnToZone) { - cloneId = 0; } else if (index >= 0) { // Increment index once here because the first index of other player properties is 2 in the propertyQueries cache. index++; - const auto& pair = propertyQueries.find(entity->GetObjectID()); + const auto& cachedQuery = propertyQueries.find(entity->GetObjectID()); - if (pair == propertyQueries.end()) return; + if (cachedQuery == propertyQueries.end()) { + Game::logger->Log("PropertyEntranceComponent", "Player %llu:%s did not have a property query open", entity->GetObjectID(), entity->GetCharacter()->GetName().c_str()); + return; + } - const auto& query = pair->second; + const auto& query = cachedQuery->second; if (index >= query.size()) return; cloneId = query[index].CloneId; } - auto* launcher = m_Parent->GetComponent(); + auto* launcher = m_ParentEntity->GetComponent(); - if (launcher == nullptr) { - return; - } + if (!launcher) return; launcher->SetSelectedCloneId(entity->GetObjectID(), cloneId); launcher->Launch(entity, launcher->GetTargetZone(), cloneId); } -PropertySelectQueryProperty PropertyEntranceComponent::SetPropertyValues(PropertySelectQueryProperty property, LWOCLONEID cloneId, std::string ownerName, std::string propertyName, std::string propertyDescription, float reputation, bool isBFF, bool isFriend, bool isModeratorApproved, bool isAlt, bool isOwned, uint32_t privacyOption, uint32_t timeLastUpdated, float performanceCost) { - property.CloneId = cloneId; - property.OwnerName = ownerName; - property.Name = propertyName; - property.Description = propertyDescription; - property.Reputation = reputation; - property.IsBestFriend = isBFF; - property.IsFriend = isFriend; - property.IsModeratorApproved = isModeratorApproved; - property.IsAlt = isAlt; - property.IsOwned = isOwned; - property.AccessType = privacyOption; - property.DateLastPublished = timeLastUpdated; - property.PerformanceCost = performanceCost; - - return property; -} - -std::string PropertyEntranceComponent::BuildQuery(Entity* entity, int32_t sortMethod, Character* character, std::string customQuery, bool wantLimits) { - std::string base; - if (customQuery == "") { - base = baseQueryForProperties; - } else { - base = customQuery; - } - std::string orderBy = ""; - if (sortMethod == SORT_TYPE_FEATURED || sortMethod == SORT_TYPE_FRIENDS) { - std::string friendsList = " AND p.owner_id IN ("; - - auto friendsListQuery = Database::CreatePreppedStmt("SELECT * FROM (SELECT CASE WHEN player_id = ? THEN friend_id WHEN friend_id = ? THEN player_id END AS requested_player FROM friends ) AS fr WHERE requested_player IS NOT NULL ORDER BY requested_player DESC;"); +std::string PropertyEntranceComponent::BuildQuery(const ePropertySortType sortMethod, Character* character, const std::string& customQuery, const bool wantLimits) { + const std::string baseQueryForProperties = + R"QUERY( + SELECT p.* + FROM properties as p + JOIN charinfo as ci + ON ci.prop_clone_id = p.clone_id + WHERE p.zone_id = ? + AND ( + p.description LIKE ? + OR p.name LIKE ? + OR ci.name LIKE ? + ) + AND p.privacy_option >= ? + )QUERY"; + DluAssert(character != nullptr); + const std::string base = customQuery.empty() ? baseQueryForProperties : customQuery; + std::string orderBy = "ORDER BY p.last_updated DESC"; + if (sortMethod == ePropertySortType::SORT_TYPE_FEATURED || sortMethod == ePropertySortType::SORT_TYPE_FRIENDS) { + std::stringstream friendsStream; + friendsStream << " AND p.owner_id IN ("; + + std::unique_ptr friendsListQuery(Database::CreatePreppedStmt( + "SELECT * FROM (SELECT CASE WHEN player_id = ? THEN friend_id WHEN friend_id = ? THEN player_id END AS requested_player FROM friends ) AS fr WHERE requested_player IS NOT NULL ORDER BY requested_player DESC;" + )); friendsListQuery->setUInt(1, character->GetID()); friendsListQuery->setUInt(2, character->GetID()); - auto friendsListQueryResult = friendsListQuery->executeQuery(); + std::unique_ptr friendsListQueryResult(friendsListQuery->executeQuery()); - while (friendsListQueryResult->next()) { - auto playerIDToConvert = friendsListQueryResult->getInt(1); - friendsList = friendsList + std::to_string(playerIDToConvert) + ","; + if (friendsListQueryResult->next()) { + friendsStream << friendsListQueryResult->getInt("requested_player"); + while (friendsListQueryResult->next()) { + friendsStream << ',' << friendsListQueryResult->getInt("requested_player"); + } } - // Replace trailing comma with the closing parenthesis. - if (friendsList.at(friendsList.size() - 1) == ',') friendsList.erase(friendsList.size() - 1, 1); - friendsList += ") "; // If we have no friends then use a -1 for the query. - if (friendsList.find("()") != std::string::npos) friendsList = " AND p.owner_id IN (-1) "; - - orderBy += friendsList + "ORDER BY ci.name ASC "; - - delete friendsListQueryResult; - friendsListQueryResult = nullptr; - - delete friendsListQuery; - friendsListQuery = nullptr; - } else if (sortMethod == SORT_TYPE_RECENT) { - orderBy = "ORDER BY p.last_updated DESC "; - } else if (sortMethod == SORT_TYPE_REPUTATION) { - orderBy = "ORDER BY p.reputation DESC, p.last_updated DESC "; - } else { - orderBy = "ORDER BY p.last_updated DESC "; + if (friendsListQueryResult->rowsCount() == 0) friendsStream << -1; + friendsStream << ')'; + + orderBy += friendsStream.str() + " ORDER BY ci.name ASC"; + } else if (sortMethod == ePropertySortType::SORT_TYPE_RECENT) { + orderBy = "ORDER BY p.last_updated DESC"; + } else if (sortMethod == ePropertySortType::SORT_TYPE_REPUTATION) { + orderBy = "ORDER BY p.reputation DESC, p.last_updated DESC"; } - return base + orderBy + (wantLimits ? "LIMIT ? OFFSET ?;" : ";"); + return base + orderBy + (wantLimits ? " LIMIT ? OFFSET ?;" : " ;"); } -void PropertyEntranceComponent::OnPropertyEntranceSync(Entity* entity, bool includeNullAddress, bool includeNullDescription, bool playerOwn, bool updateUi, int32_t numResults, int32_t lReputationTime, int32_t sortMethod, int32_t startIndex, std::string filterText, const SystemAddress& sysAddr) { - - std::vector entries{}; - PropertySelectQueryProperty playerEntry{}; +void PropertyEntranceComponent::OnPropertyEntranceSync(Entity* entity, bool includeNullAddress, bool includeNullDescription, bool playerOwn, bool updateUi, int32_t numResults, int32_t lReputationTime, ePropertySortType sortMethod, int32_t startIndex, std::string filterText, const SystemAddress& sysAddr) { + std::vector propertyPageEntries; + PropertySelectQueryProperty requestorEntry; auto character = entity->GetCharacter(); if (!character) return; - // Player property goes in index 1 of the vector. This is how the client expects it. - auto playerPropertyLookup = Database::CreatePreppedStmt("SELECT * FROM properties WHERE owner_id = ? AND zone_id = ?"); + // The first index of every player requested query must be the players' own property since it is visible on every page. + std::unique_ptr playerPropertyLookup( + Database::CreatePreppedStmt( + "SELECT * FROM properties WHERE owner_id = ? AND zone_id = ?")); playerPropertyLookup->setInt(1, character->GetID()); - playerPropertyLookup->setInt(2, this->m_MapID); + playerPropertyLookup->setInt(2, m_MapID); - auto playerPropertyLookupResults = playerPropertyLookup->executeQuery(); + std::unique_ptr playerPropertyLookupResults(playerPropertyLookup->executeQuery()); // If the player has a property this query will have a single result. + requestorEntry.CloneId = character->GetPropertyCloneID(); + requestorEntry.OwnerName = character->GetName(); if (playerPropertyLookupResults->next()) { - const auto cloneId = playerPropertyLookupResults->getUInt64(4); - const auto propertyName = std::string(playerPropertyLookupResults->getString(5).c_str()); - const auto propertyDescription = std::string(playerPropertyLookupResults->getString(6).c_str()); - const auto privacyOption = playerPropertyLookupResults->getInt(9); - const auto modApproved = playerPropertyLookupResults->getBoolean(10); - const auto dateLastUpdated = playerPropertyLookupResults->getInt64(11); - const auto reputation = playerPropertyLookupResults->getUInt(14); - const auto performanceCost = (float)playerPropertyLookupResults->getDouble(16); - - playerEntry = SetPropertyValues(playerEntry, cloneId, character->GetName(), propertyName, propertyDescription, reputation, true, true, modApproved, true, true, privacyOption, dateLastUpdated, performanceCost); - } else { - playerEntry = SetPropertyValues(playerEntry, character->GetPropertyCloneID(), character->GetName(), "", "", 0, true, true); + requestorEntry.Name = std::string(playerPropertyLookupResults->getString("name").c_str()); + requestorEntry.Description = std::string(playerPropertyLookupResults->getString("description").c_str()); + requestorEntry.Reputation = playerPropertyLookupResults->getUInt("reputation"); + requestorEntry.IsBestFriend = true; + requestorEntry.IsFriend = true; + requestorEntry.IsModeratorApproved = playerPropertyLookupResults->getBoolean("mod_approved"); + requestorEntry.IsAlt = true; + requestorEntry.IsOwned = true; + requestorEntry.AccessType = playerPropertyLookupResults->getInt("privacy_option"); + requestorEntry.DateLastPublished = playerPropertyLookupResults->getInt64("last_updated"); + requestorEntry.PerformanceCost = playerPropertyLookupResults->getDouble("performance_cost"); } - delete playerPropertyLookupResults; - playerPropertyLookupResults = nullptr; - - delete playerPropertyLookup; - playerPropertyLookup = nullptr; + propertyPageEntries.push_back(requestorEntry); - entries.push_back(playerEntry); + const auto query = BuildQuery(sortMethod, character); - const auto query = BuildQuery(entity, sortMethod, character); - - auto propertyLookup = Database::CreatePreppedStmt(query); + std::unique_ptr propertyLookup(Database::CreatePreppedStmt(query)); const auto searchString = "%" + filterText + "%"; + PropertyPrivacyOption friendsLookup = + sortMethod == ePropertySortType::SORT_TYPE_FEATURED || sortMethod == ePropertySortType::SORT_TYPE_FRIENDS ? + PropertyPrivacyOption::Friends : + PropertyPrivacyOption::Public; + propertyLookup->setUInt(1, this->m_MapID); propertyLookup->setString(2, searchString.c_str()); propertyLookup->setString(3, searchString.c_str()); propertyLookup->setString(4, searchString.c_str()); - propertyLookup->setInt(5, sortMethod == SORT_TYPE_FEATURED || sortMethod == SORT_TYPE_FRIENDS ? (uint32_t)PropertyPrivacyOption::Friends : (uint32_t)PropertyPrivacyOption::Public); + propertyLookup->setInt(5, static_cast(friendsLookup)); propertyLookup->setInt(6, numResults); propertyLookup->setInt(7, startIndex); - auto propertyEntry = propertyLookup->executeQuery(); + std::unique_ptr propertyEntry(propertyLookup->executeQuery()); while (propertyEntry->next()) { - const auto propertyId = propertyEntry->getUInt64(1); - const auto owner = propertyEntry->getInt(2); - const auto cloneId = propertyEntry->getUInt64(4); - const auto propertyNameFromDb = std::string(propertyEntry->getString(5).c_str()); - const auto propertyDescriptionFromDb = std::string(propertyEntry->getString(6).c_str()); - const auto privacyOption = propertyEntry->getInt(9); - const auto modApproved = propertyEntry->getBoolean(10); - const auto dateLastUpdated = propertyEntry->getInt(11); - const float reputation = propertyEntry->getInt(14); - const auto performanceCost = (float)propertyEntry->getDouble(16); - - PropertySelectQueryProperty entry{}; - - std::string ownerName = ""; - bool isOwned = true; - auto nameLookup = Database::CreatePreppedStmt("SELECT name FROM charinfo WHERE prop_clone_id = ?;"); - - nameLookup->setUInt64(1, cloneId); - - auto nameResult = nameLookup->executeQuery(); + PropertySelectQueryProperty playerPropertyEntry; + const auto owner = propertyEntry->getInt("owner_id"); + playerPropertyEntry.CloneId = propertyEntry->getUInt64("clone_id"); + playerPropertyEntry.Name = std::string(propertyEntry->getString("name").c_str()); + playerPropertyEntry.Description = std::string(propertyEntry->getString("description").c_str()); + playerPropertyEntry.AccessType = propertyEntry->getInt("privacy_option"); + playerPropertyEntry.IsModeratorApproved = propertyEntry->getBoolean("mod_approved"); + playerPropertyEntry.DateLastPublished = propertyEntry->getInt("last_updated"); + playerPropertyEntry.Reputation = static_cast(propertyEntry->getInt("reputation")); + playerPropertyEntry.PerformanceCost = static_cast(propertyEntry->getDouble("performance_cost")); + playerPropertyEntry.OwnerName = ""; + playerPropertyEntry.IsBestFriend = false; + playerPropertyEntry.IsFriend = false; + playerPropertyEntry.IsAlt = false; + playerPropertyEntry.IsOwned = true; + std::unique_ptr nameLookup( + Database::CreatePreppedStmt( + "SELECT name FROM charinfo WHERE prop_clone_id = ?;")); + + nameLookup->setUInt64(1, playerPropertyEntry.CloneId); + + std::unique_ptr nameResult(nameLookup->executeQuery()); if (!nameResult->next()) { - delete nameLookup; - nameLookup = nullptr; - - Game::logger->Log("PropertyEntranceComponent", "Failed to find property owner name for %llu!", cloneId); - + Game::logger->Log("PropertyEntranceComponent", "Failed to find property owner name for %llu!", playerPropertyEntry.CloneId); continue; - } else { - isOwned = cloneId == character->GetPropertyCloneID(); - ownerName = std::string(nameResult->getString(1).c_str()); } - - delete nameResult; - nameResult = nullptr; - - delete nameLookup; - nameLookup = nullptr; - - std::string propertyName = propertyNameFromDb; - std::string propertyDescription = propertyDescriptionFromDb; - - bool isBestFriend = false; - bool isFriend = false; - - // Convert owner char id to LWOOBJID - LWOOBJID ownerObjId = owner; - GeneralUtils::SetBit(ownerObjId, eObjectBits::CHARACTER); - GeneralUtils::SetBit(ownerObjId, eObjectBits::PERSISTENT); + playerPropertyEntry.IsOwned = playerPropertyEntry.CloneId == character->GetPropertyCloneID(); + playerPropertyEntry.OwnerName = std::string(nameResult->getString("name").c_str()); // Query to get friend and best friend fields - auto friendCheck = Database::CreatePreppedStmt("SELECT best_friend FROM friends WHERE (player_id = ? AND friend_id = ?) OR (player_id = ? AND friend_id = ?)"); + std::unique_ptr friendCheck( + Database::CreatePreppedStmt( + "SELECT best_friend FROM friends WHERE (player_id = ? AND friend_id = ?) OR (player_id = ? AND friend_id = ?)")); friendCheck->setUInt(1, character->GetID()); - friendCheck->setUInt(2, ownerObjId); - friendCheck->setUInt(3, ownerObjId); + friendCheck->setUInt(2, owner); + friendCheck->setUInt(3, owner); friendCheck->setUInt(4, character->GetID()); - auto friendResult = friendCheck->executeQuery(); + std::unique_ptr friendResult(friendCheck->executeQuery()); // If we got a result than the two players are friends. - if (friendResult->next()) { - isFriend = true; - if (friendResult->getInt(1) == 3) { - isBestFriend = true; - } - } - - delete friendCheck; - friendCheck = nullptr; - - delete friendResult; - friendResult = nullptr; + playerPropertyEntry.IsFriend = friendResult->next(); + playerPropertyEntry.IsBestFriend = playerPropertyEntry.IsFriend && friendResult->getInt("best_friend") == 3; - bool isModeratorApproved = propertyEntry->getBoolean(10); + playerPropertyEntry.IsModeratorApproved = propertyEntry->getBoolean("mod_approved"); - if (!isModeratorApproved && entity->GetGMLevel() >= eGameMasterLevel::LEAD_MODERATOR) { - propertyName = "[AWAITING APPROVAL]"; - propertyDescription = "[AWAITING APPROVAL]"; - isModeratorApproved = true; + // So lead moderators can visit the property but also see that it is still awaiting approval. + if (!playerPropertyEntry.IsModeratorApproved && entity->GetGMLevel() >= eGameMasterLevel::LEAD_MODERATOR) { + playerPropertyEntry.Name = "[AWAITING APPROVAL]"; + playerPropertyEntry.Description = "[AWAITING APPROVAL]"; + playerPropertyEntry.IsModeratorApproved = true; } - bool isAlt = false; // Query to determine whether this property is an alt character of the entity. - auto isAltQuery = Database::CreatePreppedStmt("SELECT id FROM charinfo where account_id in (SELECT account_id from charinfo WHERE id = ?) AND id = ?;"); + std::unique_ptr isAltQuery( + Database::CreatePreppedStmt( + "SELECT id FROM charinfo where account_id in (SELECT account_id from charinfo WHERE id = ?) AND id = ?;")); isAltQuery->setInt(1, character->GetID()); isAltQuery->setInt(2, owner); - auto isAltQueryResults = isAltQuery->executeQuery(); + std::unique_ptr isAltQueryResults(isAltQuery->executeQuery()); - if (isAltQueryResults->next()) { - isAlt = true; - } - - delete isAltQueryResults; - isAltQueryResults = nullptr; - - delete isAltQuery; - isAltQuery = nullptr; + if (isAltQueryResults->next()) playerPropertyEntry.IsAlt = true; - entry = SetPropertyValues(entry, cloneId, ownerName, propertyName, propertyDescription, reputation, isBestFriend, isFriend, isModeratorApproved, isAlt, isOwned, privacyOption, dateLastUpdated, performanceCost); - - entries.push_back(entry); + propertyPageEntries.push_back(playerPropertyEntry); } - - delete propertyEntry; - propertyEntry = nullptr; - - delete propertyLookup; - propertyLookup = nullptr; - - propertyQueries[entity->GetObjectID()] = entries; + propertyQueries[entity->GetObjectID()] = propertyPageEntries; // Query here is to figure out whether or not to display the button to go to the next page or not. - int32_t numberOfProperties = 0; - - auto buttonQuery = BuildQuery(entity, sortMethod, character, "SELECT COUNT(*) FROM properties as p JOIN charinfo as ci ON ci.prop_clone_id = p.clone_id where p.zone_id = ? AND (p.description LIKE ? OR p.name LIKE ? OR ci.name LIKE ?) AND p.privacy_option >= ? ", false); - auto propertiesLeft = Database::CreatePreppedStmt(buttonQuery); + const std::string baseButtonQuery = + R"QUERY( + SELECT COUNT(*) as numProperties + FROM properties as p + JOIN charinfo as ci + ON ci.prop_clone_id = p.clone_id + WHERE p.zone_id = ? + AND ( + p.description LIKE ? + OR p.name LIKE ? + OR ci.name LIKE ? + ) + AND p.privacy_option >= ? + )QUERY"; + + auto buttonQuery = BuildQuery(sortMethod, character, baseButtonQuery, false); + std::unique_ptr propertiesLeft(Database::CreatePreppedStmt(buttonQuery)); propertiesLeft->setUInt(1, this->m_MapID); propertiesLeft->setString(2, searchString.c_str()); propertiesLeft->setString(3, searchString.c_str()); propertiesLeft->setString(4, searchString.c_str()); - propertiesLeft->setInt(5, sortMethod == SORT_TYPE_FEATURED || sortMethod == SORT_TYPE_FRIENDS ? 1 : 2); + propertiesLeft->setInt(5, static_cast(friendsLookup)); - auto result = propertiesLeft->executeQuery(); + std::unique_ptr result(propertiesLeft->executeQuery()); result->next(); - numberOfProperties = result->getInt(1); - - delete result; - result = nullptr; - - delete propertiesLeft; - propertiesLeft = nullptr; + int32_t numberOfProperties = result->getInt("numProperties"); - GameMessages::SendPropertySelectQuery(m_Parent->GetObjectID(), startIndex, numberOfProperties - (startIndex + numResults) > 0, character->GetPropertyCloneID(), false, true, entries, sysAddr); + GameMessages::SendPropertySelectQuery(m_ParentEntity->GetObjectID(), startIndex, numberOfProperties - (startIndex + numResults) > 0, character->GetPropertyCloneID(), false, true, propertyPageEntries, sysAddr); } diff --git a/dGame/dComponents/PropertyEntranceComponent.h b/dGame/dComponents/PropertyEntranceComponent.h index e37d1daa4..488b8af5d 100644 --- a/dGame/dComponents/PropertyEntranceComponent.h +++ b/dGame/dComponents/PropertyEntranceComponent.h @@ -3,18 +3,25 @@ #include #include "Component.h" -#include "Entity.h" -#include "EntityManager.h" -#include "GameMessages.h" #include "eReplicaComponentType.h" +#include "PropertySelectQueryProperty.h" + +enum class ePropertySortType : int32_t { + SORT_TYPE_FRIENDS = 0, + SORT_TYPE_REPUTATION = 1, + SORT_TYPE_RECENT = 3, + SORT_TYPE_FEATURED = 5 +}; /** * Represents the launch pad that's used to select and browse properties */ -class PropertyEntranceComponent : public Component { +class PropertyEntranceComponent final : public Component { public: - static const eReplicaComponentType ComponentType = eReplicaComponentType::PROPERTY_ENTRANCE; - explicit PropertyEntranceComponent(uint32_t componentID, Entity* parent); + inline static const eReplicaComponentType ComponentType = eReplicaComponentType::PROPERTY_ENTRANCE; + explicit PropertyEntranceComponent(Entity* parent, int32_t componentID); + + void LoadTemplateData() override; /** * Handles an OnUse request for some other entity, rendering the property browse menu @@ -45,7 +52,7 @@ class PropertyEntranceComponent : public Component { * @param filterText property names to search for * @param sysAddr the address to send gamemessage responses to */ - void OnPropertyEntranceSync(Entity* entity, bool includeNullAddress, bool includeNullDescription, bool playerOwn, bool updateUi, int32_t numResults, int32_t lReputationTime, int32_t sortMethod, int32_t startIndex, std::string filterText, const SystemAddress& sysAddr); + void OnPropertyEntranceSync(Entity* entity, bool includeNullAddress, bool includeNullDescription, bool playerOwn, bool updateUi, int32_t numResults, int32_t lReputationTime, ePropertySortType sortMethod, int32_t startIndex, std::string filterText, const SystemAddress& sysAddr); /** * Returns the name of this property @@ -59,9 +66,7 @@ class PropertyEntranceComponent : public Component { */ [[nodiscard]] LWOMAPID GetMapID() const { return m_MapID; }; - PropertySelectQueryProperty SetPropertyValues(PropertySelectQueryProperty property, LWOCLONEID cloneId = LWOCLONEID_INVALID, std::string ownerName = "", std::string propertyName = "", std::string propertyDescription = "", float reputation = 0, bool isBFF = false, bool isFriend = false, bool isModeratorApproved = false, bool isAlt = false, bool isOwned = false, uint32_t privacyOption = 0, uint32_t timeLastUpdated = 0, float performanceCost = 0.0f); - - std::string BuildQuery(Entity* entity, int32_t sortMethod, Character* character, std::string customQuery = "", bool wantLimits = true); + [[nodiscard]] static std::string BuildQuery(const ePropertySortType sortMethod, Character* character, const std::string& customQuery = "", const bool wantLimits = true); private: /** @@ -79,12 +84,5 @@ class PropertyEntranceComponent : public Component { */ LWOMAPID m_MapID; - enum ePropertySortType : int32_t { - SORT_TYPE_FRIENDS = 0, - SORT_TYPE_REPUTATION = 1, - SORT_TYPE_RECENT = 3, - SORT_TYPE_FEATURED = 5 - }; - - std::string baseQueryForProperties = "SELECT p.* FROM properties as p JOIN charinfo as ci ON ci.prop_clone_id = p.clone_id where p.zone_id = ? AND (p.description LIKE ? OR p.name LIKE ? OR ci.name LIKE ?) AND p.privacy_option >= ? "; + int32_t m_ComponentId = -1; }; diff --git a/dGame/dComponents/PropertyManagementComponent.cpp b/dGame/dComponents/PropertyManagementComponent.cpp index c87d0744f..a8981451c 100644 --- a/dGame/dComponents/PropertyManagementComponent.cpp +++ b/dGame/dComponents/PropertyManagementComponent.cpp @@ -206,7 +206,7 @@ bool PropertyManagementComponent::Claim(const LWOOBJID playerId) { std::string name = zone->GetZoneName(); std::string description = ""; - auto prop_path = zone->GetPath(m_Parent->GetVarAsString(u"propertyName")); + auto prop_path = zone->GetPath(m_ParentEntity->GetVarAsString(u"propertyName")); if (prop_path){ if (!prop_path->property.displayName.empty()) name = prop_path->property.displayName; @@ -241,9 +241,7 @@ bool PropertyManagementComponent::Claim(const LWOOBJID playerId) { } auto* zoneControlObject = dZoneManager::Instance()->GetZoneControlObject(); - for (CppScripts::Script* script : CppScripts::GetEntityScripts(zoneControlObject)) { - script->OnZonePropertyRented(zoneControlObject, entity); - } + zoneControlObject->GetScript()->OnZonePropertyRented(zoneControlObject, entity); return true; } @@ -275,7 +273,7 @@ void PropertyManagementComponent::OnStartBuilding() { player->SendToZone(zoneId); } - auto inventoryComponent = ownerEntity->GetComponent(); + auto* inventoryComponent = ownerEntity->GetComponent(); // Push equipped items if (inventoryComponent) inventoryComponent->PushEquippedItems(); @@ -395,7 +393,7 @@ void PropertyManagementComponent::UpdateModelPosition(const LWOOBJID id, const N models.insert_or_assign(model->GetObjectID(), spawnerId); - GameMessages::SendPlaceModelResponse(entity->GetObjectID(), entity->GetSystemAddress(), position, m_Parent->GetObjectID(), 14, originalRotation); + GameMessages::SendPlaceModelResponse(entity->GetObjectID(), entity->GetSystemAddress(), position, m_ParentEntity->GetObjectID(), 14, originalRotation); GameMessages::SendUGCEquipPreCreateBasedOnEditMode(entity->GetObjectID(), entity->GetSystemAddress(), 0, spawnerId); @@ -404,7 +402,7 @@ void PropertyManagementComponent::UpdateModelPosition(const LWOOBJID id, const N EntityManager::Instance()->GetZoneControlEntity()->OnZonePropertyModelPlaced(entity); }); // Progress place model missions - auto missionComponent = entity->GetComponent(); + auto* missionComponent = entity->GetComponent(); if (missionComponent != nullptr) missionComponent->Progress(eMissionTaskType::PLACE_MODEL, 0); } @@ -783,7 +781,7 @@ PropertyManagementComponent* PropertyManagementComponent::Instance() { void PropertyManagementComponent::OnQueryPropertyData(Entity* originator, const SystemAddress& sysAddr, LWOOBJID author) { if (author == LWOOBJID_EMPTY) { - author = m_Parent->GetObjectID(); + author = m_ParentEntity->GetObjectID(); } const auto& worldId = dZoneManager::Instance()->GetZone()->GetZoneID(); @@ -861,7 +859,7 @@ void PropertyManagementComponent::OnQueryPropertyData(Entity* originator, const void PropertyManagementComponent::OnUse(Entity* originator) { OnQueryPropertyData(originator, UNASSIGNED_SYSTEM_ADDRESS); - GameMessages::SendOpenPropertyManagment(m_Parent->GetObjectID(), originator->GetSystemAddress()); + GameMessages::SendOpenPropertyManagment(m_ParentEntity->GetObjectID(), originator->GetSystemAddress()); } void PropertyManagementComponent::SetOwnerId(const LWOOBJID value) { diff --git a/dGame/dComponents/PropertyManagementComponent.h b/dGame/dComponents/PropertyManagementComponent.h index 2ee010a8c..080cc358e 100644 --- a/dGame/dComponents/PropertyManagementComponent.h +++ b/dGame/dComponents/PropertyManagementComponent.h @@ -8,7 +8,7 @@ /** * Information regarding which players may visit this property */ -enum class PropertyPrivacyOption +enum class PropertyPrivacyOption : uint32_t { /** * Default, only you can visit your property @@ -18,12 +18,12 @@ enum class PropertyPrivacyOption /** * Your friends can visit your property */ - Friends = 1, + Friends = 1, - /** - * Requires Mythran approval, everyone can visit your property - */ - Public = 2 + /** + * Requires Mythran approval, everyone can visit your property + */ + Public = 2 }; /** @@ -32,7 +32,7 @@ enum class PropertyPrivacyOption class PropertyManagementComponent : public Component { public: - static const eReplicaComponentType ComponentType = eReplicaComponentType::PROPERTY_MANAGEMENT; + inline static const eReplicaComponentType ComponentType = eReplicaComponentType::PROPERTY_MANAGEMENT; PropertyManagementComponent(Entity* parent); static PropertyManagementComponent* Instance(); diff --git a/dGame/dComponents/PropertyVendorComponent.cpp b/dGame/dComponents/PropertyVendorComponent.cpp index ed89bfc7e..16e7c6ee5 100644 --- a/dGame/dComponents/PropertyVendorComponent.cpp +++ b/dGame/dComponents/PropertyVendorComponent.cpp @@ -10,47 +10,43 @@ #include "PropertyManagementComponent.h" #include "UserManager.h" -PropertyVendorComponent::PropertyVendorComponent(Entity* parent) : Component(parent) { -} +PropertyVendorComponent::PropertyVendorComponent(Entity* parent) : Component(parent) { } void PropertyVendorComponent::OnUse(Entity* originator) { - if (PropertyManagementComponent::Instance() == nullptr) return; + if (!PropertyManagementComponent::Instance()) return; OnQueryPropertyData(originator, originator->GetSystemAddress()); - if (PropertyManagementComponent::Instance()->GetOwnerId() == LWOOBJID_EMPTY) { - Game::logger->Log("PropertyVendorComponent", "Property vendor opening!"); - - GameMessages::SendOpenPropertyVendor(m_Parent->GetObjectID(), originator->GetSystemAddress()); + if (PropertyManagementComponent::Instance()->GetOwnerId() != LWOOBJID_EMPTY) return; + Game::logger->Log("PropertyVendorComponent", "Property vendor opening!"); - return; - } + GameMessages::SendOpenPropertyVendor(m_ParentEntity->GetObjectID(), originator->GetSystemAddress()); } void PropertyVendorComponent::OnQueryPropertyData(Entity* originator, const SystemAddress& sysAddr) { - if (PropertyManagementComponent::Instance() == nullptr) return; + if (!PropertyManagementComponent::Instance()) return; - PropertyManagementComponent::Instance()->OnQueryPropertyData(originator, sysAddr, m_Parent->GetObjectID()); + PropertyManagementComponent::Instance()->OnQueryPropertyData(originator, sysAddr, m_ParentEntity->GetObjectID()); } void PropertyVendorComponent::OnBuyFromVendor(Entity* originator, const bool confirmed, const LOT lot, const uint32_t count) { - if (PropertyManagementComponent::Instance() == nullptr) return; + if (!PropertyManagementComponent::Instance()) return; - if (PropertyManagementComponent::Instance()->Claim(originator->GetObjectID()) == false) { - Game::logger->Log("PropertyVendorComponent", "FAILED TO CLAIM PROPERTY. PLAYER ID IS %llu", originator->GetObjectID()); + if (!PropertyManagementComponent::Instance()->Claim(originator->GetObjectID())) { + Game::logger->Log("PropertyVendorComponent", "Player %llu attempted to claim a property that did not belong to them.", originator->GetObjectID()); return; } - GameMessages::SendPropertyRentalResponse(m_Parent->GetObjectID(), 0, 0, 0, 0, originator->GetSystemAddress()); + GameMessages::SendPropertyRentalResponse(m_ParentEntity->GetObjectID(), 0, 0, 0, 0, originator->GetSystemAddress()); auto* controller = dZoneManager::Instance()->GetZoneControlObject(); - controller->OnFireEventServerSide(m_Parent, "propertyRented"); + controller->OnFireEventServerSide(m_ParentEntity, "propertyRented"); PropertyManagementComponent::Instance()->SetOwner(originator); PropertyManagementComponent::Instance()->OnQueryPropertyData(originator, originator->GetSystemAddress()); - Game::logger->Log("PropertyVendorComponent", "Fired event; (%d) (%i) (%i)", confirmed, lot, count); + Game::logger->Log("PropertyVendorComponent", "Fired event, (%d) (%i) (%i)", confirmed, lot, count); } diff --git a/dGame/dComponents/PropertyVendorComponent.h b/dGame/dComponents/PropertyVendorComponent.h index 5055b445a..045440272 100644 --- a/dGame/dComponents/PropertyVendorComponent.h +++ b/dGame/dComponents/PropertyVendorComponent.h @@ -7,10 +7,10 @@ /** * The property guard that stands on a property before it's claimed, allows entities to attempt claiming this property. */ -class PropertyVendorComponent : public Component +class PropertyVendorComponent final : public Component { public: - static const eReplicaComponentType ComponentType = eReplicaComponentType::PROPERTY_VENDOR; + inline static const eReplicaComponentType ComponentType = eReplicaComponentType::PROPERTY_VENDOR; explicit PropertyVendorComponent(Entity* parent); /** diff --git a/dGame/dComponents/ProximityMonitorComponent.cpp b/dGame/dComponents/ProximityMonitorComponent.cpp index acc93fdea..913b20370 100644 --- a/dGame/dComponents/ProximityMonitorComponent.cpp +++ b/dGame/dComponents/ProximityMonitorComponent.cpp @@ -4,14 +4,13 @@ #include "ControllablePhysicsComponent.h" #include "EntityManager.h" #include "SimplePhysicsComponent.h" +#include "CDClientManager.h" +#include "CDProximityMonitorComponentTable.h" const std::map ProximityMonitorComponent::m_EmptyObjectMap = {}; -ProximityMonitorComponent::ProximityMonitorComponent(Entity* parent, int radiusSmall, int radiusLarge) : Component(parent) { - if (radiusSmall != -1 && radiusLarge != -1) { - SetProximityRadius(radiusSmall, "rocketSmall"); - SetProximityRadius(radiusLarge, "rocketLarge"); - } +ProximityMonitorComponent::ProximityMonitorComponent(Entity* parent, int32_t componentId) : Component(parent) { + m_ComponentId = componentId; } ProximityMonitorComponent::~ProximityMonitorComponent() { @@ -20,32 +19,52 @@ ProximityMonitorComponent::~ProximityMonitorComponent() { dpWorld::Instance().RemoveEntity(en.second); } +} - m_ProximitiesData.clear(); +void ProximityMonitorComponent::LoadTemplateData() { + if (m_ComponentId == -1) return; + auto* proxCompTable = CDClientManager::Instance().GetTable(); + auto proxCompData = proxCompTable->Query([this](CDProximityMonitorComponent entry) { return (entry.id == this->m_ComponentId); }); + + if (!proxCompData.empty()) { + float radiusSmall = -1.0f; + float radiusLarge = -1.0f; + auto proximitySplit = GeneralUtils::SplitString(proxCompData[0].Proximities, ','); + if (proximitySplit.size() < 2) return; + GeneralUtils::TryParse(proximitySplit.at(0), radiusSmall); + GeneralUtils::TryParse(proximitySplit.at(1), radiusLarge); + if (radiusSmall != -1.0f && radiusLarge != -1.0f) { + AddProximityRadius(radiusSmall, "rocketSmall"); + AddProximityRadius(radiusLarge, "rocketLarge"); + } + } } -void ProximityMonitorComponent::SetProximityRadius(float proxRadius, const std::string& name) { - dpEntity* en = new dpEntity(m_Parent->GetObjectID(), proxRadius); - en->SetPosition(m_Parent->GetPosition()); +void ProximityMonitorComponent::AddProximityRadius(float proxRadius, const std::string& name) { + dpEntity* entity = new dpEntity(m_ParentEntity->GetObjectID(), proxRadius); + AddProximityRadius(entity, name); +} - dpWorld::Instance().AddEntity(en); - m_ProximitiesData.insert(std::make_pair(name, en)); +void ProximityMonitorComponent::AddProximityRadius(const BoxDimensions& dimensions, const std::string& name) { + dpEntity* entity = new dpEntity(m_ParentEntity->GetObjectID(), dimensions); + AddProximityRadius(entity, name); } -void ProximityMonitorComponent::SetProximityRadius(dpEntity* entity, const std::string& name) { +void ProximityMonitorComponent::AddProximityRadius(dpEntity* entity, const std::string& name) { + entity->SetPosition(m_ParentEntity->GetPosition()); + dpWorld::Instance().AddEntity(entity); - entity->SetPosition(m_Parent->GetPosition()); - m_ProximitiesData.insert(std::make_pair(name, entity)); + auto existing = m_ProximitiesData.find(name); + if (existing != m_ProximitiesData.end()) { + dpWorld::Instance().RemoveEntity(existing->second); + } + m_ProximitiesData.insert_or_assign(name, entity); } const std::map& ProximityMonitorComponent::GetProximityObjects(const std::string& name) { const auto& iter = m_ProximitiesData.find(name); - if (iter == m_ProximitiesData.end()) { - return m_EmptyObjectMap; - } - - return iter->second->GetCurrentlyCollidingObjects(); + return iter == m_ProximitiesData.end() ? m_EmptyObjectMap : iter->second->GetCurrentlyCollidingObjects(); } bool ProximityMonitorComponent::IsInProximity(const std::string& name, LWOOBJID objectID) { @@ -55,24 +74,23 @@ bool ProximityMonitorComponent::IsInProximity(const std::string& name, LWOOBJID return false; } - const auto& collitions = iter->second->GetCurrentlyCollidingObjects(); + const auto& collisions = iter->second->GetCurrentlyCollidingObjects(); - return collitions.find(objectID) != collitions.end(); + return collisions.find(objectID) != collisions.end(); } void ProximityMonitorComponent::Update(float deltaTime) { - for (const auto& prox : m_ProximitiesData) { - if (!prox.second) continue; + for (const auto& [proximityName, proximityEntity] : m_ProximitiesData) { + if (!proximityEntity) continue; //Process enter events - for (auto* en : prox.second->GetNewObjects()) { - m_Parent->OnCollisionProximity(en->GetObjectID(), prox.first, "ENTER"); + for (auto* en : proximityEntity->GetNewObjects()) { + m_ParentEntity->OnCollisionProximity(en->GetObjectID(), proximityName, "ENTER"); } //Process exit events - for (auto* en : prox.second->GetRemovedObjects()) { - m_Parent->OnCollisionProximity(en->GetObjectID(), prox.first, "LEAVE"); + for (auto* en : proximityEntity->GetRemovedObjects()) { + m_ParentEntity->OnCollisionProximity(en->GetObjectID(), proximityName, "LEAVE"); } } } - diff --git a/dGame/dComponents/ProximityMonitorComponent.h b/dGame/dComponents/ProximityMonitorComponent.h index 2f51917d0..2ce0c0421 100644 --- a/dGame/dComponents/ProximityMonitorComponent.h +++ b/dGame/dComponents/ProximityMonitorComponent.h @@ -19,10 +19,12 @@ */ class ProximityMonitorComponent : public Component { public: - static const eReplicaComponentType ComponentType = eReplicaComponentType::PROXIMITY_MONITOR; + inline static const eReplicaComponentType ComponentType = eReplicaComponentType::PROXIMITY_MONITOR; - ProximityMonitorComponent(Entity* parentEntity, int smallRadius = -1, int largeRadius = -1); + ProximityMonitorComponent(Entity* parentEntity, int32_t componentId = -1); ~ProximityMonitorComponent() override; + + void LoadTemplateData() override; void Update(float deltaTime) override; /** @@ -30,14 +32,14 @@ class ProximityMonitorComponent : public Component { * @param proxRadius the radius to use for the physics entity we use to detect proximity * @param name the name of this check */ - void SetProximityRadius(float proxRadius, const std::string& name); + void AddProximityRadius(float proxRadius, const std::string& name); /** * Creates an entry to check proximity for, given a name * @param entity the physics entity to add to our proximity sensors * @param name the name of this check */ - void SetProximityRadius(dpEntity* entity, const std::string& name); + void AddProximityRadius(const BoxDimensions& entity, const std::string& name); /** * Returns the last of entities that are used to check proximity, given a name @@ -62,15 +64,19 @@ class ProximityMonitorComponent : public Component { private: + void AddProximityRadius(dpEntity* entity, const std::string& name); + /** * All the proximity sensors for this component, indexed by name */ - std::map m_ProximitiesData = {}; + std::map m_ProximitiesData; /** * Default value for the proximity data */ static const std::map m_EmptyObjectMap; + + int32_t m_ComponentId = -1; }; #endif // PROXIMITYMONITORCOMPONENT_H diff --git a/dGame/dComponents/RebuildComponent.cpp b/dGame/dComponents/QuickBuildComponent.cpp similarity index 56% rename from dGame/dComponents/RebuildComponent.cpp rename to dGame/dComponents/QuickBuildComponent.cpp index 39c8fe8d6..c6e888a4d 100644 --- a/dGame/dComponents/RebuildComponent.cpp +++ b/dGame/dComponents/QuickBuildComponent.cpp @@ -1,4 +1,4 @@ -#include "RebuildComponent.h" +#include "QuickBuildComponent.h" #include "Entity.h" #include "DestroyableComponent.h" #include "GameMessages.h" @@ -21,10 +21,12 @@ #include "Loot.h" #include "TeamManager.h" #include "RenderComponent.h" +#include "CDRebuildComponentTable.h" #include "CppScripts.h" -RebuildComponent::RebuildComponent(Entity* entity) : Component(entity) { +QuickBuildComponent::QuickBuildComponent(Entity* entity, int32_t componentId) : ActivityComponent(entity, componentId) { + m_ComponentId = componentId; std::u16string checkPreconditions = entity->GetVar(u"CheckPrecondition"); if (!checkPreconditions.empty()) { @@ -33,20 +35,20 @@ RebuildComponent::RebuildComponent(Entity* entity) : Component(entity) { // Should a setting that has the build activator position exist, fetch that setting here and parse it for position. // It is assumed that the user who sets this setting uses the correct character delimiter (character 31 or in hex 0x1F) - auto positionAsVector = GeneralUtils::SplitString(m_Parent->GetVarAsString(u"rebuild_activators"), 0x1F); + auto positionAsVector = GeneralUtils::SplitString(m_ParentEntity->GetVarAsString(u"rebuild_activators"), 0x1F); if (positionAsVector.size() == 3 && GeneralUtils::TryParse(positionAsVector[0], m_ActivatorPosition.x) && GeneralUtils::TryParse(positionAsVector[1], m_ActivatorPosition.y) && GeneralUtils::TryParse(positionAsVector[2], m_ActivatorPosition.z)) { } else { - Game::logger->Log("RebuildComponent", "Failed to find activator position for lot %i. Defaulting to parents position.", m_Parent->GetLOT()); - m_ActivatorPosition = m_Parent->GetPosition(); + Game::logger->Log("QuickBuildComponent", "Failed to find activator position for lot %i. Defaulting to parents position.", m_ParentEntity->GetLOT()); + m_ActivatorPosition = m_ParentEntity->GetPosition(); } SpawnActivator(); } -RebuildComponent::~RebuildComponent() { +QuickBuildComponent::~QuickBuildComponent() { delete m_Precondition; Entity* builder = GetBuilder(); @@ -57,8 +59,41 @@ RebuildComponent::~RebuildComponent() { DespawnActivator(); } -void RebuildComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags) { - if (m_Parent->GetComponent(eReplicaComponentType::DESTROYABLE) == nullptr) { +void QuickBuildComponent::LoadConfigData() { + const auto rebuildResetTime = m_ParentEntity->GetVar(u"rebuild_reset_time"); + + if (rebuildResetTime != 0.0f) { + SetResetTime(rebuildResetTime); + if (m_ParentEntity->GetLOT() == 9483) // TODO: Fix the moving platform in Forbidden valley spawning at the end. + { + SetResetTime(GetResetTime() + 25); + } + } + + const auto activityID = m_ParentEntity->GetVar(u"activityID"); + if (activityID > 0) SetActivityId(activityID); + + const auto compTime = m_ParentEntity->GetVar(u"compTime"); + if (compTime > 0) SetCompleteTime(compTime); +} + +void QuickBuildComponent::LoadTemplateData() { + auto* rebCompTable = CDClientManager::Instance().GetTable(); + std::vector rebCompData = rebCompTable->Query([this](CDRebuildComponent entry) { return (entry.id == this->m_ComponentId); }); + if (rebCompData.empty()) return; + const auto& quickbuildData = rebCompData.at(0); + SetResetTime(quickbuildData.reset_time); + SetCompleteTime(quickbuildData.complete_time); + SetTakeImagination(quickbuildData.take_imagination); + SetInterruptible(quickbuildData.interruptible); + SetSelfActivator(quickbuildData.self_activator); + SetActivityId(quickbuildData.activityID); + SetPostImaginationCost(quickbuildData.post_imagination_cost); + SetTimeBeforeSmash(quickbuildData.time_before_smash); +} + +void QuickBuildComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags) { + if (!m_ParentEntity->GetComponent()) { if (bIsInitialUpdate) { outBitStream->Write(false); } @@ -110,7 +145,7 @@ void RebuildComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitia m_StateDirty = false; } -void RebuildComponent::Update(float deltaTime) { +void QuickBuildComponent::Update(float deltaTime) { m_Activator = GetActivator(); // Serialize the quickbuild every so often, fixes the odd bug where the quickbuild is not buildable @@ -120,7 +155,7 @@ void RebuildComponent::Update(float deltaTime) { else { m_SoftTimer = 5.0f; - EntityManager::Instance()->SerializeEntity(m_Parent); + EntityManager::Instance()->SerializeEntity(m_ParentEntity); }*/ switch (m_State) { @@ -128,7 +163,7 @@ void RebuildComponent::Update(float deltaTime) { SpawnActivator(); m_TimeBeforeDrain = 0; - auto* spawner = m_Parent->GetSpawner(); + auto* spawner = m_ParentEntity->GetSpawner(); const bool isSmashGroup = spawner != nullptr ? spawner->GetIsSpawnSmashGroup() : false; if (isSmashGroup) { @@ -139,13 +174,13 @@ void RebuildComponent::Update(float deltaTime) { if (m_TimerIncomplete >= m_TimeBeforeSmash - 4.0f) { m_ShowResetEffect = true; - EntityManager::Instance()->SerializeEntity(m_Parent); + EntityManager::Instance()->SerializeEntity(m_ParentEntity); } if (m_TimerIncomplete >= m_TimeBeforeSmash) { m_Builder = LWOOBJID_EMPTY; - GameMessages::SendDieNoImplCode(m_Parent, LWOOBJID_EMPTY, LWOOBJID_EMPTY, eKillType::VIOLENT, u"", 0.0f, 0.0f, 0.0f, false, true); + GameMessages::SendDieNoImplCode(m_ParentEntity, LWOOBJID_EMPTY, LWOOBJID_EMPTY, eKillType::VIOLENT, u"", 0.0f, 0.0f, 0.0f, false, true); ResetRebuild(false); } @@ -163,13 +198,13 @@ void RebuildComponent::Update(float deltaTime) { if (!m_ShowResetEffect) { m_ShowResetEffect = true; - EntityManager::Instance()->SerializeEntity(m_Parent); + EntityManager::Instance()->SerializeEntity(m_ParentEntity); } } if (m_Timer >= m_ResetTime) { - GameMessages::SendDieNoImplCode(m_Parent, LWOOBJID_EMPTY, LWOOBJID_EMPTY, eKillType::VIOLENT, u"", 0.0f, 0.0f, 0.0f, false, true); + GameMessages::SendDieNoImplCode(m_ParentEntity, LWOOBJID_EMPTY, LWOOBJID_EMPTY, eKillType::VIOLENT, u"", 0.0f, 0.0f, 0.0f, false, true); ResetRebuild(false); } @@ -194,7 +229,7 @@ void RebuildComponent::Update(float deltaTime) { if (m_TimeBeforeDrain <= 0.0f) { m_TimeBeforeDrain = m_CompleteTime / static_cast(m_TakeImagination); - DestroyableComponent* destComp = builder->GetComponent(); + auto* destComp = builder->GetComponent(); if (!destComp) break; int newImagination = destComp->GetImagination(); @@ -225,13 +260,13 @@ void RebuildComponent::Update(float deltaTime) { if (m_TimerIncomplete >= m_TimeBeforeSmash - 4.0f) { m_ShowResetEffect = true; - EntityManager::Instance()->SerializeEntity(m_Parent); + EntityManager::Instance()->SerializeEntity(m_ParentEntity); } if (m_TimerIncomplete >= m_TimeBeforeSmash) { m_Builder = LWOOBJID_EMPTY; - GameMessages::SendDieNoImplCode(m_Parent, LWOOBJID_EMPTY, LWOOBJID_EMPTY, eKillType::VIOLENT, u"", 0.0f, 0.0f, 0.0f, false, true); + GameMessages::SendDieNoImplCode(m_ParentEntity, LWOOBJID_EMPTY, LWOOBJID_EMPTY, eKillType::VIOLENT, u"", 0.0f, 0.0f, 0.0f, false, true); ResetRebuild(false); } @@ -242,7 +277,7 @@ void RebuildComponent::Update(float deltaTime) { } } -void RebuildComponent::OnUse(Entity* originator) { +void QuickBuildComponent::OnUse(Entity* originator) { if (GetBuilder() != nullptr || m_State == eRebuildState::COMPLETED) { return; } @@ -254,27 +289,27 @@ void RebuildComponent::OnUse(Entity* originator) { StartRebuild(originator); } -void RebuildComponent::SpawnActivator() { +void QuickBuildComponent::SpawnActivator() { if (!m_SelfActivator || m_ActivatorPosition != NiPoint3::ZERO) { if (!m_Activator) { EntityInfo info; info.lot = 6604; - info.spawnerID = m_Parent->GetObjectID(); - info.pos = m_ActivatorPosition == NiPoint3::ZERO ? m_Parent->GetPosition() : m_ActivatorPosition; + info.spawnerID = m_ParentEntity->GetObjectID(); + info.pos = m_ActivatorPosition == NiPoint3::ZERO ? m_ParentEntity->GetPosition() : m_ActivatorPosition; - m_Activator = EntityManager::Instance()->CreateEntity(info, nullptr, m_Parent); + m_Activator = EntityManager::Instance()->CreateEntity(info, nullptr, m_ParentEntity); if (m_Activator) { m_ActivatorId = m_Activator->GetObjectID(); EntityManager::Instance()->ConstructEntity(m_Activator); } - EntityManager::Instance()->SerializeEntity(m_Parent); + EntityManager::Instance()->SerializeEntity(m_ParentEntity); } } } -void RebuildComponent::DespawnActivator() { +void QuickBuildComponent::DespawnActivator() { if (m_Activator) { EntityManager::Instance()->DestructEntity(m_Activator); @@ -286,73 +321,73 @@ void RebuildComponent::DespawnActivator() { } } -Entity* RebuildComponent::GetActivator() { +Entity* QuickBuildComponent::GetActivator() { return EntityManager::Instance()->GetEntity(m_ActivatorId); } -NiPoint3 RebuildComponent::GetActivatorPosition() { +NiPoint3 QuickBuildComponent::GetActivatorPosition() { return m_ActivatorPosition; } -float RebuildComponent::GetResetTime() { +float QuickBuildComponent::GetResetTime() { return m_ResetTime; } -float RebuildComponent::GetCompleteTime() { +float QuickBuildComponent::GetCompleteTime() { return m_CompleteTime; } -int RebuildComponent::GetTakeImagination() { +int QuickBuildComponent::GetTakeImagination() { return m_TakeImagination; } -bool RebuildComponent::GetInterruptible() { +bool QuickBuildComponent::GetInterruptible() { return m_Interruptible; } -bool RebuildComponent::GetSelfActivator() { +bool QuickBuildComponent::GetSelfActivator() { return m_SelfActivator; } -std::vector RebuildComponent::GetCustomModules() { +std::vector QuickBuildComponent::GetCustomModules() { return m_CustomModules; } -int RebuildComponent::GetActivityId() { +int QuickBuildComponent::GetActivityId() { return m_ActivityId; } -int RebuildComponent::GetPostImaginationCost() { +int QuickBuildComponent::GetPostImaginationCost() { return m_PostImaginationCost; } -float RebuildComponent::GetTimeBeforeSmash() { +float QuickBuildComponent::GetTimeBeforeSmash() { return m_TimeBeforeSmash; } -eRebuildState RebuildComponent::GetState() { +eRebuildState QuickBuildComponent::GetState() { return m_State; } -Entity* RebuildComponent::GetBuilder() const { +Entity* QuickBuildComponent::GetBuilder() const { auto* builder = EntityManager::Instance()->GetEntity(m_Builder); return builder; } -bool RebuildComponent::GetRepositionPlayer() const { +bool QuickBuildComponent::GetRepositionPlayer() const { return m_RepositionPlayer; } -void RebuildComponent::SetActivatorPosition(NiPoint3 value) { +void QuickBuildComponent::SetActivatorPosition(NiPoint3 value) { m_ActivatorPosition = value; } -void RebuildComponent::SetResetTime(float value) { +void QuickBuildComponent::SetResetTime(float value) { m_ResetTime = value; } -void RebuildComponent::SetCompleteTime(float value) { +void QuickBuildComponent::SetCompleteTime(float value) { if (value < 0) { m_CompleteTime = 4.5f; } else { @@ -360,31 +395,31 @@ void RebuildComponent::SetCompleteTime(float value) { } } -void RebuildComponent::SetTakeImagination(int value) { +void QuickBuildComponent::SetTakeImagination(int value) { m_TakeImagination = value; } -void RebuildComponent::SetInterruptible(bool value) { +void QuickBuildComponent::SetInterruptible(bool value) { m_Interruptible = value; } -void RebuildComponent::SetSelfActivator(bool value) { +void QuickBuildComponent::SetSelfActivator(bool value) { m_SelfActivator = value; } -void RebuildComponent::SetCustomModules(std::vector value) { +void QuickBuildComponent::SetCustomModules(std::vector value) { m_CustomModules = value; } -void RebuildComponent::SetActivityId(int value) { +void QuickBuildComponent::SetActivityId(int value) { m_ActivityId = value; } -void RebuildComponent::SetPostImaginationCost(int value) { +void QuickBuildComponent::SetPostImaginationCost(int value) { m_PostImaginationCost = value; } -void RebuildComponent::SetTimeBeforeSmash(float value) { +void QuickBuildComponent::SetTimeBeforeSmash(float value) { if (value < 0) { m_TimeBeforeSmash = 10.0f; } else { @@ -392,11 +427,11 @@ void RebuildComponent::SetTimeBeforeSmash(float value) { } } -void RebuildComponent::SetRepositionPlayer(bool value) { +void QuickBuildComponent::SetRepositionPlayer(bool value) { m_RepositionPlayer = value; } -void RebuildComponent::StartRebuild(Entity* user) { +void QuickBuildComponent::StartRebuild(Entity* user) { if (m_State == eRebuildState::OPEN || m_State == eRebuildState::COMPLETED || m_State == eRebuildState::INCOMPLETE) { m_Builder = user->GetObjectID(); @@ -405,31 +440,27 @@ void RebuildComponent::StartRebuild(Entity* user) { EntityManager::Instance()->SerializeEntity(user); - GameMessages::SendRebuildNotifyState(m_Parent, m_State, eRebuildState::BUILDING, user->GetObjectID()); - GameMessages::SendEnableRebuild(m_Parent, true, false, false, eQuickBuildFailReason::NOT_GIVEN, 0.0f, user->GetObjectID()); + GameMessages::SendRebuildNotifyState(m_ParentEntity, m_State, eRebuildState::BUILDING, user->GetObjectID()); + GameMessages::SendEnableRebuild(m_ParentEntity, true, false, false, eQuickBuildFailReason::NOT_GIVEN, 0.0f, user->GetObjectID()); m_State = eRebuildState::BUILDING; m_StateDirty = true; - EntityManager::Instance()->SerializeEntity(m_Parent); + EntityManager::Instance()->SerializeEntity(m_ParentEntity); - auto* movingPlatform = m_Parent->GetComponent(); + auto* movingPlatform = m_ParentEntity->GetComponent(); if (movingPlatform != nullptr) { movingPlatform->OnRebuildInitilized(); } - for (auto* script : CppScripts::GetEntityScripts(m_Parent)) { - script->OnRebuildStart(m_Parent, user); - } + m_ParentEntity->GetScript()->OnRebuildStart(m_ParentEntity, user); // Notify scripts and possible subscribers - for (auto* script : CppScripts::GetEntityScripts(m_Parent)) - script->OnRebuildNotifyState(m_Parent, m_State); - for (const auto& cb : m_RebuildStateCallbacks) - cb(m_State); + m_ParentEntity->GetScript()->OnRebuildNotifyState(m_ParentEntity, m_State); + for (const auto& cb : m_RebuildStateCallbacks) cb(m_State); } } -void RebuildComponent::CompleteRebuild(Entity* user) { +void QuickBuildComponent::CompleteRebuild(Entity* user) { if (user == nullptr) { return; } @@ -439,16 +470,16 @@ void RebuildComponent::CompleteRebuild(Entity* user) { characterComponent->SetCurrentActivity(eGameActivity::NONE); characterComponent->TrackRebuildComplete(); } else { - Game::logger->Log("RebuildComponent", "Some user tried to finish the rebuild but they didn't have a character somehow."); + Game::logger->Log("QuickBuildComponent", "Some user tried to finish the rebuild but they didn't have a character somehow."); return; } EntityManager::Instance()->SerializeEntity(user); - GameMessages::SendRebuildNotifyState(m_Parent, m_State, eRebuildState::COMPLETED, user->GetObjectID()); - GameMessages::SendPlayFXEffect(m_Parent, 507, u"create", "BrickFadeUpVisCompleteEffect", LWOOBJID_EMPTY, 0.4f, 1.0f, true); - GameMessages::SendEnableRebuild(m_Parent, false, false, true, eQuickBuildFailReason::NOT_GIVEN, m_ResetTime, user->GetObjectID()); - GameMessages::SendTerminateInteraction(user->GetObjectID(), eTerminateType::FROM_INTERACTION, m_Parent->GetObjectID()); + GameMessages::SendRebuildNotifyState(m_ParentEntity, m_State, eRebuildState::COMPLETED, user->GetObjectID()); + GameMessages::SendPlayFXEffect(m_ParentEntity, 507, u"create", "BrickFadeUpVisCompleteEffect", LWOOBJID_EMPTY, 0.4f, 1.0f, true); + GameMessages::SendEnableRebuild(m_ParentEntity, false, false, true, eQuickBuildFailReason::NOT_GIVEN, m_ResetTime, user->GetObjectID()); + GameMessages::SendTerminateInteraction(user->GetObjectID(), eTerminateType::FROM_INTERACTION, m_ParentEntity->GetObjectID()); m_State = eRebuildState::COMPLETED; @@ -456,7 +487,7 @@ void RebuildComponent::CompleteRebuild(Entity* user) { m_Timer = 0.0f; m_DrainedImagination = 0; - EntityManager::Instance()->SerializeEntity(m_Parent); + EntityManager::Instance()->SerializeEntity(m_ParentEntity); // Removes extra item requirements, isn't live accurate. // In live, all items were removed at the start of the quickbuild, then returned if it was cancelled. @@ -468,7 +499,7 @@ void RebuildComponent::CompleteRebuild(Entity* user) { DespawnActivator(); // Set owner override so that entities smashed by this quickbuild will result in the builder getting rewards. - m_Parent->SetOwnerOverride(user->GetObjectID()); + m_ParentEntity->SetOwnerOverride(user->GetObjectID()); auto* builder = GetBuilder(); @@ -482,18 +513,16 @@ void RebuildComponent::CompleteRebuild(Entity* user) { if (missionComponent) missionComponent->Progress(eMissionTaskType::ACTIVITY, m_ActivityId); } } - } else{ + } else { auto* missionComponent = builder->GetComponent(); if (missionComponent) missionComponent->Progress(eMissionTaskType::ACTIVITY, m_ActivityId); } - LootGenerator::Instance().DropActivityLoot(builder, m_Parent, m_ActivityId, 1); + LootGenerator::Instance().DropActivityLoot(builder, m_ParentEntity, m_ActivityId, 1); } // Notify scripts - for (auto* script : CppScripts::GetEntityScripts(m_Parent)) { - script->OnRebuildComplete(m_Parent, user); - script->OnRebuildNotifyState(m_Parent, m_State); - } + m_ParentEntity->GetScript()->OnRebuildComplete(m_ParentEntity, user); + m_ParentEntity->GetScript()->OnRebuildNotifyState(m_ParentEntity, m_State); // Notify subscribers for (const auto& callback : m_RebuildStateCallbacks) @@ -501,9 +530,9 @@ void RebuildComponent::CompleteRebuild(Entity* user) { for (const auto& callback : m_RebuildCompleteCallbacks) callback(user); - m_Parent->TriggerEvent(eTriggerEventType::REBUILD_COMPLETE, user); + m_ParentEntity->TriggerEvent(eTriggerEventType::REBUILD_COMPLETE, user); - auto* movingPlatform = m_Parent->GetComponent(); + auto* movingPlatform = m_ParentEntity->GetComponent(); if (movingPlatform != nullptr) { movingPlatform->OnCompleteRebuild(); } @@ -512,7 +541,7 @@ void RebuildComponent::CompleteRebuild(Entity* user) { auto* character = user->GetCharacter(); if (character != nullptr) { - const auto flagNumber = m_Parent->GetVar(u"quickbuild_single_build_player_flag"); + const auto flagNumber = m_ParentEntity->GetVar(u"quickbuild_single_build_player_flag"); if (flagNumber != 0) { character->SetPlayerFlag(flagNumber, true); @@ -521,18 +550,18 @@ void RebuildComponent::CompleteRebuild(Entity* user) { RenderComponent::PlayAnimation(user, u"rebuild-celebrate", 1.09f); } -void RebuildComponent::ResetRebuild(bool failed) { +void QuickBuildComponent::ResetRebuild(bool failed) { Entity* builder = GetBuilder(); if (m_State == eRebuildState::BUILDING && builder) { - GameMessages::SendEnableRebuild(m_Parent, false, false, failed, eQuickBuildFailReason::NOT_GIVEN, m_ResetTime, builder->GetObjectID()); + GameMessages::SendEnableRebuild(m_ParentEntity, false, false, failed, eQuickBuildFailReason::NOT_GIVEN, m_ResetTime, builder->GetObjectID()); if (failed) { RenderComponent::PlayAnimation(builder, u"rebuild-fail"); } } - GameMessages::SendRebuildNotifyState(m_Parent, m_State, eRebuildState::RESETTING, LWOOBJID_EMPTY); + GameMessages::SendRebuildNotifyState(m_ParentEntity, m_State, eRebuildState::RESETTING, LWOOBJID_EMPTY); m_State = eRebuildState::RESETTING; m_StateDirty = true; @@ -541,22 +570,20 @@ void RebuildComponent::ResetRebuild(bool failed) { m_ShowResetEffect = false; m_DrainedImagination = 0; - EntityManager::Instance()->SerializeEntity(m_Parent); + EntityManager::Instance()->SerializeEntity(m_ParentEntity); // Notify scripts and possible subscribers - for (auto* script : CppScripts::GetEntityScripts(m_Parent)) - script->OnRebuildNotifyState(m_Parent, m_State); - for (const auto& cb : m_RebuildStateCallbacks) - cb(m_State); + m_ParentEntity->GetScript()->OnRebuildNotifyState(m_ParentEntity, m_State); + for (const auto& cb : m_RebuildStateCallbacks) cb(m_State); - m_Parent->ScheduleKillAfterUpdate(); + m_ParentEntity->ScheduleKillAfterUpdate(); if (m_Activator) { m_Activator->ScheduleKillAfterUpdate(); } } -void RebuildComponent::CancelRebuild(Entity* entity, eQuickBuildFailReason failReason, bool skipChecks) { +void QuickBuildComponent::CancelRebuild(Entity* entity, eQuickBuildFailReason failReason, bool skipChecks) { if (m_State != eRebuildState::COMPLETED || skipChecks) { m_Builder = LWOOBJID_EMPTY; @@ -564,41 +591,39 @@ void RebuildComponent::CancelRebuild(Entity* entity, eQuickBuildFailReason failR const auto entityID = entity != nullptr ? entity->GetObjectID() : LWOOBJID_EMPTY; // Notify the client that a state has changed - GameMessages::SendRebuildNotifyState(m_Parent, m_State, eRebuildState::INCOMPLETE, entityID); - GameMessages::SendEnableRebuild(m_Parent, false, true, false, failReason, m_Timer, entityID); + GameMessages::SendRebuildNotifyState(m_ParentEntity, m_State, eRebuildState::INCOMPLETE, entityID); + GameMessages::SendEnableRebuild(m_ParentEntity, false, true, false, failReason, m_Timer, entityID); // Now terminate any interaction with the rebuild - GameMessages::SendTerminateInteraction(entityID, eTerminateType::FROM_INTERACTION, m_Parent->GetObjectID()); - GameMessages::SendTerminateInteraction(m_Parent->GetObjectID(), eTerminateType::FROM_INTERACTION, m_Parent->GetObjectID()); + GameMessages::SendTerminateInteraction(entityID, eTerminateType::FROM_INTERACTION, m_ParentEntity->GetObjectID()); + GameMessages::SendTerminateInteraction(m_ParentEntity->GetObjectID(), eTerminateType::FROM_INTERACTION, m_ParentEntity->GetObjectID()); // Now update the component itself m_State = eRebuildState::INCOMPLETE; m_StateDirty = true; // Notify scripts and possible subscribers - for (auto* script : CppScripts::GetEntityScripts(m_Parent)) - script->OnRebuildNotifyState(m_Parent, m_State); - for (const auto& cb : m_RebuildStateCallbacks) - cb(m_State); + m_ParentEntity->GetScript()->OnRebuildNotifyState(m_ParentEntity, m_State); + for (const auto& cb : m_RebuildStateCallbacks) cb(m_State); - EntityManager::Instance()->SerializeEntity(m_Parent); + EntityManager::Instance()->SerializeEntity(m_ParentEntity); } if (entity == nullptr) { return; } - CharacterComponent* characterComponent = entity->GetComponent(); + auto* characterComponent = entity->GetComponent(); if (characterComponent) { characterComponent->SetCurrentActivity(eGameActivity::NONE); EntityManager::Instance()->SerializeEntity(entity); } } -void RebuildComponent::AddRebuildCompleteCallback(const std::function& callback) { +void QuickBuildComponent::AddRebuildCompleteCallback(const std::function& callback) { m_RebuildCompleteCallbacks.push_back(callback); } -void RebuildComponent::AddRebuildStateCallback(const std::function& callback) { +void QuickBuildComponent::AddRebuildStateCallback(const std::function& callback) { m_RebuildStateCallbacks.push_back(callback); } diff --git a/dGame/dComponents/RebuildComponent.h b/dGame/dComponents/QuickBuildComponent.h similarity index 94% rename from dGame/dComponents/RebuildComponent.h rename to dGame/dComponents/QuickBuildComponent.h index 09dd0465c..23d92dbdc 100644 --- a/dGame/dComponents/RebuildComponent.h +++ b/dGame/dComponents/QuickBuildComponent.h @@ -1,5 +1,5 @@ -#ifndef REBUILDCOMPONENT_H -#define REBUILDCOMPONENT_H +#ifndef __QUICKBUILDCOMPONENT__H__ +#define __QUICKBUILDCOMPONENT__H__ #include #include @@ -8,7 +8,7 @@ #include "NiPoint3.h" #include "ScriptedActivityComponent.h" #include "Preconditions.h" -#include "Component.h" +#include "ActivityComponent.h" #include "eReplicaComponentType.h" #include "eRebuildState.h" @@ -20,15 +20,17 @@ enum class eQuickBuildFailReason : uint32_t; * consists of an activator that shows a popup and then the actual entity that the bricks are built into. Note * that quick builds are also scripted activities so this shared some logic with the ScriptedActivityComponent. */ -class RebuildComponent : public Component { +class QuickBuildComponent : public ActivityComponent { public: - static const eReplicaComponentType ComponentType = eReplicaComponentType::QUICK_BUILD; + inline static const eReplicaComponentType ComponentType = eReplicaComponentType::QUICK_BUILD; - RebuildComponent(Entity* entity); - ~RebuildComponent() override; + QuickBuildComponent(Entity* entity, int32_t componentId = 0); + ~QuickBuildComponent() override; void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags); void Update(float deltaTime) override; + void LoadTemplateData() override; + void LoadConfigData() override; /** * Handles a OnUse event from some entity, initiating the quick build @@ -349,6 +351,8 @@ class RebuildComponent : public Component { */ PreconditionExpression* m_Precondition = nullptr; + int32_t m_ComponentId = 0; + /** * Starts the rebuild for a certain entity * @param user the entity to start the rebuild @@ -362,4 +366,4 @@ class RebuildComponent : public Component { void CompleteRebuild(Entity* user); }; -#endif // REBUILDCOMPONENT_H +#endif //!__QUICKBUILDCOMPONENT__H__ diff --git a/dGame/dComponents/RacingComponent.cpp b/dGame/dComponents/RacingComponent.cpp new file mode 100644 index 000000000..b203f02dc --- /dev/null +++ b/dGame/dComponents/RacingComponent.cpp @@ -0,0 +1,5 @@ +#include "RacingComponent.h" + +RacingComponent::RacingComponent(Entity* parent, int32_t componentId) : RacingControlComponent(parent, componentId) { + +} diff --git a/dGame/dComponents/RacingComponent.h b/dGame/dComponents/RacingComponent.h new file mode 100644 index 000000000..dcd87bb11 --- /dev/null +++ b/dGame/dComponents/RacingComponent.h @@ -0,0 +1,16 @@ +#ifndef __RACINGCOMPONENT__H__ +#define __RACINGCOMPONENT__H__ + +#include "RacingControlComponent.h" +#include "eReplicaComponentType.h" + +class Entity; + +class RacingComponent : public RacingControlComponent { +public: + inline static const eReplicaComponentType ComponentType = eReplicaComponentType::RACING_CONTROL; + RacingComponent(Entity* parent, int32_t componentId); +}; + + +#endif //!__RACINGCOMPONENT__H__ diff --git a/dGame/dComponents/RacingControlComponent.cpp b/dGame/dComponents/RacingControlComponent.cpp index c16e99fb1..0f31df041 100644 --- a/dGame/dComponents/RacingControlComponent.cpp +++ b/dGame/dComponents/RacingControlComponent.cpp @@ -14,10 +14,9 @@ #include "ModuleAssemblyComponent.h" #include "Player.h" #include "PossessableComponent.h" -#include "PossessorComponent.h" +#include "PossessionComponent.h" #include "eRacingTaskParam.h" #include "Spawner.h" -#include "VehiclePhysicsComponent.h" #include "dServer.h" #include "dZoneManager.h" #include "dConfig.h" @@ -30,8 +29,7 @@ #define M_PI 3.14159265358979323846264338327950288 #endif -RacingControlComponent::RacingControlComponent(Entity* parent) - : Component(parent) { +RacingControlComponent::RacingControlComponent(Entity* parent, int32_t componentId) : ScriptedActivityComponent(parent, componentId) { m_PathName = u"MainPath"; m_RemainingLaps = 3; m_LeadingPlayer = LWOOBJID_EMPTY; @@ -132,16 +130,15 @@ void RacingControlComponent::LoadPlayerVehicle(Entity* player, info.lot = 8092; info.pos = startPosition; info.rot = startRotation; - info.spawnerID = m_Parent->GetObjectID(); + info.spawnerID = m_ParentEntity->GetObjectID(); auto* carEntity = - EntityManager::Instance()->CreateEntity(info, nullptr, m_Parent); + EntityManager::Instance()->CreateEntity(info, nullptr, m_ParentEntity); // Make the vehicle a child of the racing controller. - m_Parent->AddChild(carEntity); + m_ParentEntity->AddChild(carEntity); - auto* destroyableComponent = - carEntity->GetComponent(); + auto* destroyableComponent = carEntity->GetComponent(); // Setup the vehicle stats. if (destroyableComponent != nullptr) { @@ -150,16 +147,14 @@ void RacingControlComponent::LoadPlayerVehicle(Entity* player, } // Setup the vehicle as being possessed by the player. - auto* possessableComponent = - carEntity->GetComponent(); + auto* possessableComponent = carEntity->GetComponent(); if (possessableComponent != nullptr) { possessableComponent->SetPossessor(player->GetObjectID()); } // Load the vehicle's assemblyPartLOTs for display. - auto* moduleAssemblyComponent = - carEntity->GetComponent(); + auto* moduleAssemblyComponent = carEntity->GetComponent(); if (moduleAssemblyComponent) { moduleAssemblyComponent->SetSubKey(item->GetSubKey()); @@ -174,11 +169,11 @@ void RacingControlComponent::LoadPlayerVehicle(Entity* player, } // Setup the player as possessing the vehicle. - auto* possessorComponent = player->GetComponent(); + auto* possessionComponent = player->GetComponent(); - if (possessorComponent != nullptr) { - possessorComponent->SetPossessable(carEntity->GetObjectID()); - possessorComponent->SetPossessableType(ePossessionType::ATTACHED_VISIBLE); // for racing it's always Attached_Visible + if (possessionComponent != nullptr) { + possessionComponent->SetPossessable(carEntity->GetObjectID()); + possessionComponent->SetPossessableType(ePossessionType::ATTACHED_VISIBLE); // for racing it's always Attached_Visible } // Set the player's current activity as racing. @@ -208,17 +203,17 @@ void RacingControlComponent::LoadPlayerVehicle(Entity* player, EntityManager::Instance()->ConstructEntity(carEntity); EntityManager::Instance()->SerializeEntity(player); - EntityManager::Instance()->SerializeEntity(m_Parent); + EntityManager::Instance()->SerializeEntity(m_ParentEntity); GameMessages::SendRacingSetPlayerResetInfo( - m_Parent->GetObjectID(), 0, 0, player->GetObjectID(), startPosition, 1, + m_ParentEntity->GetObjectID(), 0, 0, player->GetObjectID(), startPosition, 1, UNASSIGNED_SYSTEM_ADDRESS); const auto playerID = player->GetObjectID(); // Reset the player to the start position during downtime, in case something // went wrong. - m_Parent->AddCallbackTimer(1, [this, playerID]() { + m_ParentEntity->AddCallbackTimer(1, [this, playerID]() { auto* player = EntityManager::Instance()->GetEntity(playerID); if (player == nullptr) { @@ -226,14 +221,14 @@ void RacingControlComponent::LoadPlayerVehicle(Entity* player, } GameMessages::SendRacingResetPlayerToLastReset( - m_Parent->GetObjectID(), playerID, UNASSIGNED_SYSTEM_ADDRESS); + m_ParentEntity->GetObjectID(), playerID, UNASSIGNED_SYSTEM_ADDRESS); }); GameMessages::SendSetJetPackMode(player, false); // Set the vehicle's state. GameMessages::SendNotifyVehicleOfRacingObject(carEntity->GetObjectID(), - m_Parent->GetObjectID(), + m_ParentEntity->GetObjectID(), UNASSIGNED_SYSTEM_ADDRESS); GameMessages::SendVehicleSetWheelLockState(carEntity->GetObjectID(), false, @@ -254,7 +249,7 @@ void RacingControlComponent::OnRacingClientReady(Entity* player) { if (racingPlayer.playerID != player->GetObjectID()) { if (racingPlayer.playerLoaded) { GameMessages::SendRacingPlayerLoaded( - m_Parent->GetObjectID(), racingPlayer.playerID, + m_ParentEntity->GetObjectID(), racingPlayer.playerID, racingPlayer.vehicleID, UNASSIGNED_SYSTEM_ADDRESS); } @@ -264,11 +259,11 @@ void RacingControlComponent::OnRacingClientReady(Entity* player) { racingPlayer.playerLoaded = true; GameMessages::SendRacingPlayerLoaded( - m_Parent->GetObjectID(), racingPlayer.playerID, + m_ParentEntity->GetObjectID(), racingPlayer.playerID, racingPlayer.vehicleID, UNASSIGNED_SYSTEM_ADDRESS); } - EntityManager::Instance()->SerializeEntity(m_Parent); + EntityManager::Instance()->SerializeEntity(m_ParentEntity); } void RacingControlComponent::OnRequestDie(Entity* player) { @@ -301,15 +296,15 @@ void RacingControlComponent::OnRequestDie(Entity* player) { // Respawn the player in 2 seconds, as was done in live. Not sure if this value is in a setting somewhere else... vehicle->AddCallbackTimer(2.0f, [=]() { - if (!vehicle || !this->m_Parent) return; + if (!vehicle || !this->m_ParentEntity) return; GameMessages::SendRacingResetPlayerToLastReset( - m_Parent->GetObjectID(), racingPlayer.playerID, + m_ParentEntity->GetObjectID(), racingPlayer.playerID, UNASSIGNED_SYSTEM_ADDRESS); GameMessages::SendVehicleStopBoost(vehicle, player->GetSystemAddress(), true); GameMessages::SendRacingSetPlayerResetInfo( - m_Parent->GetObjectID(), racingPlayer.lap, + m_ParentEntity->GetObjectID(), racingPlayer.lap, racingPlayer.respawnIndex, player->GetObjectID(), racingPlayer.respawnPosition, racingPlayer.respawnIndex + 1, UNASSIGNED_SYSTEM_ADDRESS); @@ -327,12 +322,12 @@ void RacingControlComponent::OnRequestDie(Entity* player) { } } else { GameMessages::SendRacingSetPlayerResetInfo( - m_Parent->GetObjectID(), racingPlayer.lap, + m_ParentEntity->GetObjectID(), racingPlayer.lap, racingPlayer.respawnIndex, player->GetObjectID(), racingPlayer.respawnPosition, racingPlayer.respawnIndex + 1, UNASSIGNED_SYSTEM_ADDRESS); GameMessages::SendRacingResetPlayerToLastReset( - m_Parent->GetObjectID(), racingPlayer.playerID, + m_ParentEntity->GetObjectID(), racingPlayer.playerID, UNASSIGNED_SYSTEM_ADDRESS); } } @@ -376,11 +371,11 @@ void RacingControlComponent::HandleMessageBoxResponse(Entity* player, int32_t bu // Calculate the score, different loot depending on player count const auto score = m_LoadedPlayers * 10 + data->finished; - LootGenerator::Instance().GiveActivityLoot(player, m_Parent, m_ActivityID, score); + LootGenerator::Instance().GiveActivityLoot(player, m_ParentEntity, m_ActivityID, score); // Giving rewards GameMessages::SendNotifyRacingClient( - m_Parent->GetObjectID(), 2, 0, LWOOBJID_EMPTY, u"", + m_ParentEntity->GetObjectID(), 2, 0, LWOOBJID_EMPTY, u"", player->GetObjectID(), UNASSIGNED_SYSTEM_ADDRESS); auto* missionComponent = player->GetComponent(); @@ -410,7 +405,7 @@ void RacingControlComponent::HandleMessageBoxResponse(Entity* player, int32_t bu // Exiting race GameMessages::SendNotifyRacingClient( - m_Parent->GetObjectID(), 3, 0, LWOOBJID_EMPTY, u"", + m_ParentEntity->GetObjectID(), 3, 0, LWOOBJID_EMPTY, u"", player->GetObjectID(), UNASSIGNED_SYSTEM_ADDRESS); auto* playerInstance = dynamic_cast(player); @@ -618,7 +613,7 @@ void RacingControlComponent::Update(float deltaTime) { // Setup for racing if (m_StartTimer == 0) { GameMessages::SendNotifyRacingClient( - m_Parent->GetObjectID(), 1, 0, LWOOBJID_EMPTY, u"", + m_ParentEntity->GetObjectID(), 1, 0, LWOOBJID_EMPTY, u"", LWOOBJID_EMPTY, UNASSIGNED_SYSTEM_ADDRESS); for (const auto& player : m_RacingPlayers) { @@ -636,8 +631,7 @@ void RacingControlComponent::Update(float deltaTime) { vehicle->SetPosition(player.respawnPosition); vehicle->SetRotation(player.respawnRotation); - auto* destroyableComponent = - vehicle->GetComponent(); + auto* destroyableComponent = vehicle->GetComponent(); if (destroyableComponent != nullptr) { destroyableComponent->SetImagination(0); @@ -702,14 +696,14 @@ void RacingControlComponent::Update(float deltaTime) { } // Start the race - GameMessages::SendActivityStart(m_Parent->GetObjectID(), + GameMessages::SendActivityStart(m_ParentEntity->GetObjectID(), UNASSIGNED_SYSTEM_ADDRESS); m_Started = true; Game::logger->Log("RacingControlComponent", "Starting race"); - EntityManager::Instance()->SerializeEntity(m_Parent); + EntityManager::Instance()->SerializeEntity(m_ParentEntity); m_StartTime = std::time(nullptr); } @@ -740,7 +734,7 @@ void RacingControlComponent::Update(float deltaTime) { // If the player is this far below the map, safe to assume they should // be smashed by death plane if (vehiclePosition.y < -500) { - GameMessages::SendDie(vehicle, m_Parent->GetObjectID(), + GameMessages::SendDie(vehicle, m_ParentEntity->GetObjectID(), LWOOBJID_EMPTY, true, eKillType::VIOLENT, u"", 0, 0, 0, true, false, 0); @@ -814,8 +808,7 @@ void RacingControlComponent::Update(float deltaTime) { "Best lap time (%llu)", lapTime); } - auto* missionComponent = - playerEntity->GetComponent(); + auto* missionComponent = playerEntity->GetComponent(); if (missionComponent != nullptr) { diff --git a/dGame/dComponents/RacingControlComponent.h b/dGame/dComponents/RacingControlComponent.h index a81121e16..6007e2d56 100644 --- a/dGame/dComponents/RacingControlComponent.h +++ b/dGame/dComponents/RacingControlComponent.h @@ -6,7 +6,7 @@ #include "BitStream.h" #include "Entity.h" -#include "Component.h" +#include "ScriptedActivityComponent.h" #include "eReplicaComponentType.h" /** @@ -103,11 +103,11 @@ struct RacingPlayerInfo { /** * Component that's attached to a manager entity in each race zone that loads player vehicles, keep scores, etc. */ -class RacingControlComponent : public Component { +class RacingControlComponent : public ScriptedActivityComponent { public: - static const eReplicaComponentType ComponentType = eReplicaComponentType::RACING_CONTROL; + inline static const eReplicaComponentType ComponentType = eReplicaComponentType::RACING_CONTROL; - RacingControlComponent(Entity* parentEntity); + RacingControlComponent(Entity* parentEntity, int32_t componentId); ~RacingControlComponent(); void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags); diff --git a/dGame/dComponents/RacingSoundTriggerComponent.cpp b/dGame/dComponents/RacingSoundTriggerComponent.cpp new file mode 100644 index 000000000..ffff8dd3b --- /dev/null +++ b/dGame/dComponents/RacingSoundTriggerComponent.cpp @@ -0,0 +1,5 @@ +#include "RacingSoundTriggerComponent.h" + +RacingSoundTriggerComponent::RacingSoundTriggerComponent(Entity* parent) : SoundTriggerComponent(parent) { + +} diff --git a/dGame/dComponents/RacingSoundTriggerComponent.h b/dGame/dComponents/RacingSoundTriggerComponent.h new file mode 100644 index 000000000..c35e76ce2 --- /dev/null +++ b/dGame/dComponents/RacingSoundTriggerComponent.h @@ -0,0 +1,15 @@ +#ifndef __RACINGSOUNDTRIGGERCOMPONENT__H__ +#define __RACINGSOUNDTRIGGERCOMPONENT__H__ + +#include "SoundTriggerComponent.h" +#include "eReplicaComponentType.h" + +class Entity; + +class RacingSoundTriggerComponent : public SoundTriggerComponent { +public: + inline static const eReplicaComponentType ComponentType = eReplicaComponentType::RACING_SOUND_TRIGGER; + RacingSoundTriggerComponent(Entity* parent); +}; + +#endif //!__RACINGSOUNDTRIGGERCOMPONENT__H__ diff --git a/dGame/dComponents/RacingStatsComponent.cpp b/dGame/dComponents/RacingStatsComponent.cpp new file mode 100644 index 000000000..8c9209ab1 --- /dev/null +++ b/dGame/dComponents/RacingStatsComponent.cpp @@ -0,0 +1,7 @@ +#include "RacingStatsComponent.h" + +#include "Entity.h" + +RacingStatsComponent::RacingStatsComponent(Entity* parent) : Component(parent) { + +} diff --git a/dGame/dComponents/RacingStatsComponent.h b/dGame/dComponents/RacingStatsComponent.h new file mode 100644 index 000000000..308ae3e76 --- /dev/null +++ b/dGame/dComponents/RacingStatsComponent.h @@ -0,0 +1,11 @@ +#include "Component.h" + +#include "eReplicaComponentType.h" + +class Entity; + +class RacingStatsComponent : public Component { +public: + inline static const eReplicaComponentType ComponentType = eReplicaComponentType::RACING_STATS; + RacingStatsComponent(Entity* parent); +}; diff --git a/dGame/dComponents/RailActivatorComponent.cpp b/dGame/dComponents/RailActivatorComponent.cpp index 8e13c37f7..c3af0e3a0 100644 --- a/dGame/dComponents/RailActivatorComponent.cpp +++ b/dGame/dComponents/RailActivatorComponent.cpp @@ -4,7 +4,7 @@ #include "CDRailActivatorComponent.h" #include "Entity.h" #include "GameMessages.h" -#include "RebuildComponent.h" +#include "QuickBuildComponent.h" #include "Game.h" #include "dLogger.h" #include "RenderComponent.h" @@ -13,12 +13,21 @@ RailActivatorComponent::RailActivatorComponent(Entity* parent, int32_t componentID) : Component(parent) { m_ComponentID = componentID; - const auto tableData = CDClientManager::Instance().GetTable()->GetEntryByID(componentID);; +} - m_Path = parent->GetVar(u"rail_path"); - m_PathDirection = parent->GetVar(u"rail_path_direction"); - m_PathStart = parent->GetVar(u"rail_path_start"); +void RailActivatorComponent::LoadConfigData() { + m_Path = m_ParentEntity->GetVar(u"rail_path"); + m_PathDirection = m_ParentEntity->GetVar(u"rail_path_direction"); + m_PathStart = m_ParentEntity->GetVar(u"rail_path_start"); + m_DamageImmune = m_ParentEntity->GetVar(u"rail_activator_damage_immune"); + m_NoAggro = m_ParentEntity->GetVar(u"rail_no_aggro"); + m_NotifyArrived = m_ParentEntity->GetVar(u"rail_notify_activator_arrived"); + m_ShowNameBillboard = m_ParentEntity->GetVar(u"rail_show_name_billboard"); + m_UseDB = m_ParentEntity->GetVar(u"rail_use_db"); +} +void RailActivatorComponent::LoadTemplateData() { + const auto tableData = CDClientManager::Instance().GetTable()->GetEntryByID(m_ComponentID); m_StartSound = tableData.startSound; m_loopSound = tableData.loopSound; m_StopSound = tableData.stopSound; @@ -30,36 +39,26 @@ RailActivatorComponent::RailActivatorComponent(Entity* parent, int32_t component m_StartEffect = tableData.startEffectID; m_LoopEffect = tableData.loopEffectID; m_StopEffect = tableData.stopEffectID; - - m_DamageImmune = parent->GetVar(u"rail_activator_damage_immune"); - m_NoAggro = parent->GetVar(u"rail_no_aggro"); - m_NotifyArrived = parent->GetVar(u"rail_notify_activator_arrived"); - m_ShowNameBillboard = parent->GetVar(u"rail_show_name_billboard"); - m_UseDB = parent->GetVar(u"rail_use_db"); m_CameraLocked = tableData.cameraLocked; m_CollisionEnabled = tableData.playerCollision; } -RailActivatorComponent::~RailActivatorComponent() = default; - void RailActivatorComponent::OnUse(Entity* originator) { - auto* rebuildComponent = m_Parent->GetComponent(); - if (rebuildComponent != nullptr && rebuildComponent->GetState() != eRebuildState::COMPLETED) - return; + auto* quickBuildComponent = m_ParentEntity->GetComponent(); + if (quickBuildComponent && quickBuildComponent->GetState() != eRebuildState::COMPLETED) return; - if (rebuildComponent != nullptr) { - // Don't want it to be destroyed while a player is using it - rebuildComponent->SetResetTime(rebuildComponent->GetResetTime() + 10.0f); + // Don't want it to be destroyed while a player is using it + if (quickBuildComponent) { + quickBuildComponent->SetResetTime(quickBuildComponent->GetResetTime() + 10.0f); } m_EntitiesOnRail.push_back(originator->GetObjectID()); // Start the initial effects if (!m_StartEffect.second.empty()) { - GameMessages::SendPlayFXEffect(originator->GetObjectID(), m_StartEffect.first, m_StartEffect.second, - std::to_string(m_StartEffect.first)); + GameMessages::SendPlayFXEffect(originator->GetObjectID(), m_StartEffect.first, m_StartEffect.second, std::to_string(m_StartEffect.first)); } - + float animationLength = 0.5f; if (!m_StartAnimation.empty()) { animationLength = RenderComponent::PlayAnimation(originator, m_StartAnimation); @@ -67,18 +66,16 @@ void RailActivatorComponent::OnUse(Entity* originator) { const auto originatorID = originator->GetObjectID(); - m_Parent->AddCallbackTimer(animationLength, [originatorID, this]() { + m_ParentEntity->AddCallbackTimer(animationLength, [originatorID, this]() { auto* originator = EntityManager::Instance()->GetEntity(originatorID); - if (originator == nullptr) { - return; - } + if (!originator) return; GameMessages::SendStartRailMovement(originator->GetObjectID(), m_Path, m_StartSound, m_loopSound, m_StopSound, originator->GetSystemAddress(), m_PathStart, m_PathDirection, m_DamageImmune, m_NoAggro, m_NotifyArrived, m_ShowNameBillboard, m_CameraLocked, m_CollisionEnabled, m_UseDB, m_ComponentID, - m_Parent->GetObjectID()); + m_ParentEntity->GetObjectID()); }); } @@ -88,26 +85,26 @@ void RailActivatorComponent::OnRailMovementReady(Entity* originator) const { true, true, true, true, true, true, true ); - if (std::find(m_EntitiesOnRail.begin(), m_EntitiesOnRail.end(), originator->GetObjectID()) != m_EntitiesOnRail.end()) { - // Stop the initial effects - if (!m_StartEffect.second.empty()) { - GameMessages::SendStopFXEffect(originator, false, std::to_string(m_StartEffect.first)); - } - - // Start the looping effects - if (!m_LoopEffect.second.empty()) { - GameMessages::SendPlayFXEffect(originator->GetObjectID(), m_LoopEffect.first, m_LoopEffect.second, - std::to_string(m_LoopEffect.first)); - } - - if (!m_LoopAnimation.empty()) { - RenderComponent::PlayAnimation(originator, m_LoopAnimation); - } - - GameMessages::SendSetRailMovement(originator->GetObjectID(), m_PathDirection, m_Path, m_PathStart, - originator->GetSystemAddress(), m_ComponentID, - m_Parent->GetObjectID()); + if (std::find(m_EntitiesOnRail.begin(), m_EntitiesOnRail.end(), originator->GetObjectID()) == m_EntitiesOnRail.end()) return; + + // Stop the initial effects + if (!m_StartEffect.second.empty()) { + GameMessages::SendStopFXEffect(originator, false, std::to_string(m_StartEffect.first)); + } + + // Start the looping effects + if (!m_LoopEffect.second.empty()) { + GameMessages::SendPlayFXEffect(originator->GetObjectID(), m_LoopEffect.first, m_LoopEffect.second, + std::to_string(m_LoopEffect.first)); } + + if (!m_LoopAnimation.empty()) { + RenderComponent::PlayAnimation(originator, m_LoopAnimation); + } + + GameMessages::SendSetRailMovement(originator->GetObjectID(), m_PathDirection, m_Path, m_PathStart, + originator->GetSystemAddress(), m_ComponentID, + m_ParentEntity->GetObjectID()); } void RailActivatorComponent::OnCancelRailMovement(Entity* originator) { @@ -116,31 +113,30 @@ void RailActivatorComponent::OnCancelRailMovement(Entity* originator) { true, true, true, true, true, true, true ); - auto* rebuildComponent = m_Parent->GetComponent(); + auto* quickBuildComponent = m_ParentEntity->GetComponent(); + + // Set back reset time + if (quickBuildComponent) { + quickBuildComponent->SetResetTime(quickBuildComponent->GetResetTime() - 10.0f); + } + + if (std::find(m_EntitiesOnRail.begin(), m_EntitiesOnRail.end(), originator->GetObjectID()) == m_EntitiesOnRail.end()) return; + // Stop the looping effects + if (!m_LoopEffect.second.empty()) { + GameMessages::SendStopFXEffect(originator, false, std::to_string(m_LoopEffect.first)); + } - if (rebuildComponent != nullptr) { - // Set back reset time - rebuildComponent->SetResetTime(rebuildComponent->GetResetTime() - 10.0f); + // Start the end effects + if (!m_StopEffect.second.empty()) { + GameMessages::SendPlayFXEffect(originator->GetObjectID(), m_StopEffect.first, m_StopEffect.second, + std::to_string(m_StopEffect.first)); } - if (std::find(m_EntitiesOnRail.begin(), m_EntitiesOnRail.end(), originator->GetObjectID()) != m_EntitiesOnRail.end()) { - // Stop the looping effects - if (!m_LoopEffect.second.empty()) { - GameMessages::SendStopFXEffect(originator, false, std::to_string(m_LoopEffect.first)); - } - - // Start the end effects - if (!m_StopEffect.second.empty()) { - GameMessages::SendPlayFXEffect(originator->GetObjectID(), m_StopEffect.first, m_StopEffect.second, - std::to_string(m_StopEffect.first)); - } - - if (!m_StopAnimation.empty()) { - RenderComponent::PlayAnimation(originator, m_StopAnimation); - } - - // Remove the player after they've signalled they're done railing - m_EntitiesOnRail.erase(std::remove(m_EntitiesOnRail.begin(), m_EntitiesOnRail.end(), - originator->GetObjectID()), m_EntitiesOnRail.end()); + if (!m_StopAnimation.empty()) { + RenderComponent::PlayAnimation(originator, m_StopAnimation); } + + // Remove the player after they've signalled they're done railing + m_EntitiesOnRail.erase(std::remove(m_EntitiesOnRail.begin(), m_EntitiesOnRail.end(), + originator->GetObjectID()), m_EntitiesOnRail.end()); } diff --git a/dGame/dComponents/RailActivatorComponent.h b/dGame/dComponents/RailActivatorComponent.h index 5d625d2ad..90180d70b 100644 --- a/dGame/dComponents/RailActivatorComponent.h +++ b/dGame/dComponents/RailActivatorComponent.h @@ -12,10 +12,12 @@ */ class RailActivatorComponent final : public Component { public: + inline static const eReplicaComponentType ComponentType = eReplicaComponentType::RAIL_ACTIVATOR; + explicit RailActivatorComponent(Entity* parent, int32_t componentID); - ~RailActivatorComponent() override; - static const eReplicaComponentType ComponentType = eReplicaComponentType::RAIL_ACTIVATOR; + void LoadConfigData() override; + void LoadTemplateData() override; /** * Handles the OnUse event from some entity, initiates the rail movement diff --git a/dGame/dComponents/RenderComponent.cpp b/dGame/dComponents/RenderComponent.cpp index 94f5fb5d0..2c09c621a 100644 --- a/dGame/dComponents/RenderComponent.cpp +++ b/dGame/dComponents/RenderComponent.cpp @@ -15,7 +15,7 @@ std::unordered_map RenderComponent::m_DurationCache{}; -RenderComponent::RenderComponent(Entity* parent, int32_t componentId): Component(parent) { +RenderComponent::RenderComponent(Entity* parent, int32_t componentId) : Component(parent) { m_Effects = std::vector(); m_LastAnimationName = ""; if (componentId == -1) return; @@ -45,13 +45,10 @@ RenderComponent::RenderComponent(Entity* parent, int32_t componentId): Component RenderComponent::~RenderComponent() { for (Effect* eff : m_Effects) { - if (eff) { - delete eff; - eff = nullptr; - } + if (!eff) continue; + delete eff; + eff = nullptr; } - - m_Effects.clear(); } void RenderComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags) { @@ -59,9 +56,9 @@ void RenderComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitial outBitStream->Write(m_Effects.size()); - for (Effect* eff : m_Effects) { + for (auto* eff : m_Effects) { // Check that the effect is non-null - assert(eff); + DluAssert(eff); outBitStream->Write(eff->name.size()); for (const auto& value : eff->name) @@ -141,7 +138,7 @@ void RenderComponent::Update(const float deltaTime) { void RenderComponent::PlayEffect(const int32_t effectId, const std::u16string& effectType, const std::string& name, const LWOOBJID secondary, const float priority, const float scale, const bool serialize) { RemoveEffect(name); - GameMessages::SendPlayFXEffect(m_Parent, effectId, effectType, name, secondary, priority, scale, serialize); + GameMessages::SendPlayFXEffect(m_ParentEntity, effectId, effectType, name, secondary, priority, scale, serialize); auto* effect = AddEffect(effectId, name, effectType); @@ -180,7 +177,7 @@ void RenderComponent::PlayEffect(const int32_t effectId, const std::u16string& e } void RenderComponent::StopEffect(const std::string& name, const bool killImmediate) { - GameMessages::SendStopFXEffect(m_Parent, killImmediate, name); + GameMessages::SendStopFXEffect(m_ParentEntity, killImmediate, name); RemoveEffect(name); } @@ -189,7 +186,6 @@ std::vector& RenderComponent::GetEffects() { return m_Effects; } - float RenderComponent::PlayAnimation(Entity* self, const std::u16string& animation, float priority, float scale) { if (!self) return 0.0f; return RenderComponent::PlayAnimation(self, GeneralUtils::UTF16ToWTF8(animation), priority, scale); @@ -210,7 +206,6 @@ float RenderComponent::GetAnimationTime(Entity* self, const std::string& animati return RenderComponent::DoAnimation(self, animation, false); } - float RenderComponent::DoAnimation(Entity* self, const std::string& animation, bool sendAnimation, float priority, float scale) { float returnlength = 0.0f; if (!self) return returnlength; diff --git a/dGame/dComponents/RenderComponent.h b/dGame/dComponents/RenderComponent.h index cdf32160e..ba604df07 100644 --- a/dGame/dComponents/RenderComponent.h +++ b/dGame/dComponents/RenderComponent.h @@ -56,7 +56,7 @@ struct Effect { */ class RenderComponent : public Component { public: - static const eReplicaComponentType ComponentType = eReplicaComponentType::RENDER; + inline static const eReplicaComponentType ComponentType = eReplicaComponentType::RENDER; RenderComponent(Entity* entity, int32_t componentId = -1); ~RenderComponent() override; diff --git a/dGame/dComponents/RigidbodyPhantomPhysicsComponent.cpp b/dGame/dComponents/RigidbodyPhantomPhysicsComponent.cpp index babd19743..39ee78f4f 100644 --- a/dGame/dComponents/RigidbodyPhantomPhysicsComponent.cpp +++ b/dGame/dComponents/RigidbodyPhantomPhysicsComponent.cpp @@ -7,14 +7,11 @@ #include "Entity.h" RigidbodyPhantomPhysicsComponent::RigidbodyPhantomPhysicsComponent(Entity* parent) : Component(parent) { - m_Position = m_Parent->GetDefaultPosition(); - m_Rotation = m_Parent->GetDefaultRotation(); + m_Position = m_ParentEntity->GetDefaultPosition(); + m_Rotation = m_ParentEntity->GetDefaultRotation(); m_IsDirty = true; } -RigidbodyPhantomPhysicsComponent::~RigidbodyPhantomPhysicsComponent() { -} - void RigidbodyPhantomPhysicsComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags) { outBitStream->Write(m_IsDirty || bIsInitialUpdate); if (m_IsDirty || bIsInitialUpdate) { diff --git a/dGame/dComponents/RigidbodyPhantomPhysicsComponent.h b/dGame/dComponents/RigidbodyPhantomPhysicsComponent.h index 480f9b812..a1d020926 100644 --- a/dGame/dComponents/RigidbodyPhantomPhysicsComponent.h +++ b/dGame/dComponents/RigidbodyPhantomPhysicsComponent.h @@ -3,8 +3,8 @@ * Copyright 2019 */ -#ifndef RIGIDBODYPHANTOMPHYSICS_H -#define RIGIDBODYPHANTOMPHYSICS_H +#ifndef __RIGIDBODYPHANTOMPHYSICS_H__ +#define __RIGIDBODYPHANTOMPHYSICS_H__ #include "BitStream.h" #include "dCommonVars.h" @@ -19,10 +19,9 @@ */ class RigidbodyPhantomPhysicsComponent : public Component { public: - static const eReplicaComponentType ComponentType = eReplicaComponentType::PHANTOM_PHYSICS; + inline static const eReplicaComponentType ComponentType = eReplicaComponentType::PHANTOM_PHYSICS; RigidbodyPhantomPhysicsComponent(Entity* parent); - ~RigidbodyPhantomPhysicsComponent() override; void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags); @@ -68,4 +67,4 @@ class RigidbodyPhantomPhysicsComponent : public Component { bool m_IsDirty; }; -#endif // RIGIDBODYPHANTOMPHYSICS_H +#endif // __RIGIDBODYPHANTOMPHYSICS_H__ diff --git a/dGame/dComponents/RocketLaunchLupComponent.cpp b/dGame/dComponents/RocketLaunchLupComponent.cpp deleted file mode 100644 index 3c3265408..000000000 --- a/dGame/dComponents/RocketLaunchLupComponent.cpp +++ /dev/null @@ -1,32 +0,0 @@ -#include "RocketLaunchLupComponent.h" -#include "RocketLaunchpadControlComponent.h" -#include "InventoryComponent.h" -#include "CharacterComponent.h" - -RocketLaunchLupComponent::RocketLaunchLupComponent(Entity* parent) : Component(parent) { - m_Parent = parent; - std::string zoneString = GeneralUtils::UTF16ToWTF8(m_Parent->GetVar(u"MultiZoneIDs")); - std::stringstream ss(zoneString); - for (int i; ss >> i;) { - m_LUPWorlds.push_back(i); - if (ss.peek() == ';') - ss.ignore(); - } -} - -RocketLaunchLupComponent::~RocketLaunchLupComponent() {} - -void RocketLaunchLupComponent::OnUse(Entity* originator) { - auto* rocket = originator->GetComponent()->RocketEquip(originator); - if (!rocket) return; - - // the LUP world menu is just the property menu, the client knows how to handle it - GameMessages::SendPropertyEntranceBegin(m_Parent->GetObjectID(), m_Parent->GetSystemAddress()); -} - -void RocketLaunchLupComponent::OnSelectWorld(Entity* originator, uint32_t index) { - auto* rocketLaunchpadControlComponent = m_Parent->GetComponent(); - if (!rocketLaunchpadControlComponent) return; - - rocketLaunchpadControlComponent->Launch(originator, m_LUPWorlds[index], 0); -} diff --git a/dGame/dComponents/RocketLaunchpadControlComponent.cpp b/dGame/dComponents/RocketLaunchpadControlComponent.cpp index 3cac9e425..081bf08d6 100644 --- a/dGame/dComponents/RocketLaunchpadControlComponent.cpp +++ b/dGame/dComponents/RocketLaunchpadControlComponent.cpp @@ -13,7 +13,7 @@ #include "ChatPackets.h" #include "MissionComponent.h" #include "PropertyEntranceComponent.h" -#include "RocketLaunchLupComponent.h" +#include "MultiZoneEntranceComponent.h" #include "dServer.h" #include "PacketUtils.h" #include "eObjectWorldState.h" @@ -77,7 +77,7 @@ void RocketLaunchpadControlComponent::Launch(Entity* originator, LWOMAPID mapId, SetSelectedMapId(originator->GetObjectID(), zone); - GameMessages::SendFireEventClientSide(m_Parent->GetObjectID(), originator->GetSystemAddress(), u"RocketEquipped", rocket->GetId(), cloneId, -1, originator->GetObjectID()); + GameMessages::SendFireEventClientSide(m_ParentEntity->GetObjectID(), originator->GetSystemAddress(), u"RocketEquipped", rocket->GetId(), cloneId, -1, originator->GetObjectID()); GameMessages::SendChangeObjectWorldState(rocket->GetId(), eObjectWorldState::ATTACHED, UNASSIGNED_SYSTEM_ADDRESS); @@ -89,13 +89,13 @@ void RocketLaunchpadControlComponent::OnUse(Entity* originator) { // instead we let their OnUse handlers do their things // which components of an Object have their OnUse called when using them // so we don't need to call it here - auto* propertyEntrance = m_Parent->GetComponent(); + auto* propertyEntrance = m_ParentEntity->GetComponent(); if (propertyEntrance) { return; } - auto* rocketLaunchLUP = m_Parent->GetComponent(); - if (rocketLaunchLUP) { + auto* multiZoneEntranceComponent = m_ParentEntity->GetComponent(); + if (multiZoneEntranceComponent) { return; } diff --git a/dGame/dComponents/RocketLaunchpadControlComponent.h b/dGame/dComponents/RocketLaunchpadControlComponent.h index 84cff22dc..9d18089fd 100644 --- a/dGame/dComponents/RocketLaunchpadControlComponent.h +++ b/dGame/dComponents/RocketLaunchpadControlComponent.h @@ -18,7 +18,7 @@ class PreconditionExpression; */ class RocketLaunchpadControlComponent : public Component { public: - static const eReplicaComponentType ComponentType = eReplicaComponentType::ROCKET_LAUNCH; + inline static const eReplicaComponentType ComponentType = eReplicaComponentType::ROCKET_LAUNCHPAD_CONTROL; RocketLaunchpadControlComponent(Entity* parent, int rocketId); ~RocketLaunchpadControlComponent() override; diff --git a/dGame/dComponents/ScriptComponent.cpp b/dGame/dComponents/ScriptComponent.cpp new file mode 100644 index 000000000..d22215cf2 --- /dev/null +++ b/dGame/dComponents/ScriptComponent.cpp @@ -0,0 +1,72 @@ +/* + * Darkflame Universe + * Copyright 2018 + */ + +#include "Entity.h" +#include "ScriptComponent.h" +#include "CDClientManager.h" +#include "CDScriptComponentTable.h" +#include "CDZoneTableTable.h" +#include "dZoneManager.h" + +ScriptComponent::ScriptComponent(Entity* parent, const std::string& scriptName) : Component(parent) { + SetScript(scriptName); +} + +void ScriptComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags) { + if (bIsInitialUpdate) { + const auto& networkSettings = m_ParentEntity->GetNetworkSettings(); + auto hasNetworkSettings = !networkSettings.empty(); + outBitStream->Write(hasNetworkSettings); + + if (hasNetworkSettings) { + + // First write the most inner LDF data + RakNet::BitStream ldfData; + ldfData.Write(0); + ldfData.Write(networkSettings.size()); + + for (auto* networkSetting : networkSettings) { + networkSetting->WriteToPacket(&ldfData); + } + + // Finally write everything to the stream + outBitStream->Write(ldfData.GetNumberOfBytesUsed()); + outBitStream->Write(ldfData); + } + } +} + +CppScripts::Script* ScriptComponent::GetScript() { + return m_Script; +} + +void ScriptComponent::SetScript(const std::string& scriptName) { + m_Script = CppScripts::GetScript(m_ParentEntity, scriptName); +} + +const std::string ScriptComponent::GetScriptName(Entity* parentEntity, const uint32_t componentId) { + if (!parentEntity || componentId == 0) return ""; + // LDF key script overrides script component Id + const auto customScriptServer = parentEntity->GetVarAsString(u"custom_script_server"); + if (!customScriptServer.empty()) return customScriptServer; + + auto* scriptCompTable = CDClientManager::Instance().GetTable(); + CDScriptComponent scriptCompData = scriptCompTable->GetByID(componentId); + return scriptCompData.script_name; +} + +const std::string ScriptComponent::GetZoneScriptName(const uint32_t componentId) { + auto* zoneTable = CDClientManager::Instance().GetTable(); + const auto zoneID = dZoneManager::Instance()->GetZoneID(); + const auto* zoneData = zoneTable->Query(zoneID.GetMapID()); + + if (!zoneData) return ""; + + int zoneScriptID = zoneData->scriptID; + if (zoneScriptID == -1) return ""; + auto* scriptCompTable = CDClientManager::Instance().GetTable(); + const auto& zoneScriptData = scriptCompTable->GetByID(zoneScriptID); + return zoneScriptData.script_name; +} diff --git a/dScripts/ScriptComponent.h b/dGame/dComponents/ScriptComponent.h similarity index 64% rename from dScripts/ScriptComponent.h rename to dGame/dComponents/ScriptComponent.h index 77dff5bfb..ec5d1207d 100644 --- a/dScripts/ScriptComponent.h +++ b/dGame/dComponents/ScriptComponent.h @@ -19,10 +19,9 @@ class Entity; */ class ScriptComponent : public Component { public: - static const eReplicaComponentType ComponentType = eReplicaComponentType::SCRIPT; + inline static const eReplicaComponentType ComponentType = eReplicaComponentType::SCRIPT; - ScriptComponent(Entity* parent, std::string scriptName, bool serialized, bool client = false); - ~ScriptComponent() override; + ScriptComponent(Entity* parent, const std::string& scriptName); void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags); @@ -32,34 +31,23 @@ class ScriptComponent : public Component { */ CppScripts::Script* GetScript(); - /** - * Sets whether the entity should be serialized, unused - * @param var whether the entity should be serialized - */ - void SetSerialized(const bool var) { m_Serialized = var; } - /** * Sets the script using a path by looking through dScripts for a script that matches * @param scriptName the name of the script to find */ void SetScript(const std::string& scriptName); + // Get the script attached to the provided component id. + // LDF key custom_script_server overrides component id script. + static const std::string GetScriptName(Entity* parentEntity, const uint32_t componentId = 0); + + static const std::string GetZoneScriptName(const uint32_t componentId); private: /** * The script attached to this entity */ CppScripts::Script* m_Script; - - /** - * Whether or not the comp should be serialized, unused - */ - bool m_Serialized; - - /** - * Whether or not this script is a client script - */ - bool m_Client; }; #endif // SCRIPTCOMPONENT_H diff --git a/dGame/dComponents/ScriptedActivityComponent.cpp b/dGame/dComponents/ScriptedActivityComponent.cpp index 555332f4b..a90c08dad 100644 --- a/dGame/dComponents/ScriptedActivityComponent.cpp +++ b/dGame/dComponents/ScriptedActivityComponent.cpp @@ -1,621 +1,5 @@ #include "ScriptedActivityComponent.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 -#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" +ScriptedActivityComponent::ScriptedActivityComponent(Entity* parent, int32_t componentId) : ActivityComponent(parent, componentId) { -ScriptedActivityComponent::ScriptedActivityComponent(Entity* parent, int activityID) : Component(parent) { - m_ActivityID = activityID; - CDActivitiesTable* activitiesTable = CDClientManager::Instance().GetTable(); - std::vector activities = activitiesTable->Query([=](CDActivities entry) {return (entry.ActivityID == m_ActivityID); }); - - for (CDActivities activity : activities) { - m_ActivityInfo = activity; - if (static_cast(activity.leaderboardType) == LeaderboardType::Racing && Game::config->GetValue("solo_racing") == "1") { - m_ActivityInfo.minTeamSize = 1; - m_ActivityInfo.minTeams = 1; - } - - const auto& transferOverride = parent->GetVar(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_Parent->GetComponent(); - - if (destroyableComponent) { - // check for LMIs and set the loot LMIs - CDActivityRewardsTable* activityRewardsTable = CDClientManager::Instance().GetTable(); - std::vector 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 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 }); - } - } - } - } -} - -ScriptedActivityComponent::~ScriptedActivityComponent() -= default; - -void ScriptedActivityComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags) const { - outBitStream->Write(true); - outBitStream->Write(m_ActivityPlayers.size()); - - if (!m_ActivityPlayers.empty()) { - for (const auto& activityPlayer : m_ActivityPlayers) { - - outBitStream->Write(activityPlayer->playerID); - for (const auto& activityValue : activityPlayer->values) { - outBitStream->Write(activityValue); - } - } - } -} - -void ScriptedActivityComponent::ReloadConfig() { - CDActivitiesTable* activitiesTable = CDClientManager::Instance().GetTable(); - std::vector 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 ScriptedActivityComponent::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 ScriptedActivityComponent::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_Parent); -} - -void ScriptedActivityComponent::PlayerJoinLobby(Entity* player) { - if (!m_Parent->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 ScriptedActivityComponent::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 ScriptedActivityComponent::Update(float deltaTime) { - std::vector 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("ScriptedActivityComponent", "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 ScriptedActivityComponent::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 ScriptedActivityComponent::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 ScriptedActivityComponent::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_Parent->GetLOT() == 4860) { - auto* missionComponent = player->GetComponent(); - missionComponent->CompleteMission(229); - } - - ChatPackets::SendSystemMessage(player->GetSystemAddress(), u"Sorry, this activity is not ready."); - static_cast(player)->SendToZone(dZoneManager::Instance()->GetZone()->GetWorldID()); // Gets them out of this stuck state - - return false; - }*/ - - return true; -} - -bool ScriptedActivityComponent::PlayerIsInQueue(Entity* player) { - for (Lobby* lobby : m_Queue) { - for (LobbyPlayer* lobbyPlayer : lobby->players) { - if (player->GetObjectID() == lobbyPlayer->entityID) return true; - } - } - - return false; -} - -bool ScriptedActivityComponent::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 ScriptedActivityComponent::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 ScriptedActivityComponent::TakeCost(Entity* player) const { - if (m_ActivityInfo.optionalCostLOT <= 0 || m_ActivityInfo.optionalCostCount <= 0) - return true; - - auto* inventoryComponent = player->GetComponent(); - 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 ScriptedActivityComponent::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* ScriptedActivityComponent::NewInstance() { - auto* instance = new ActivityInstance(m_Parent, m_ActivityInfo); - m_Instances.push_back(instance); - return instance; -} - -void ScriptedActivityComponent::LoadPlayersIntoInstance(ActivityInstance* instance, const std::vector& lobby) const { - for (LobbyPlayer* player : lobby) { - auto* entity = player->GetEntity(); - if (entity == nullptr || !TakeCost(entity)) { - continue; - } - - instance->AddParticipant(entity); - } -} - -const std::vector& ScriptedActivityComponent::GetInstances() const { - return m_Instances; -} - -ActivityInstance* ScriptedActivityComponent::GetInstance(const LWOOBJID playerID) { - for (const auto* instance : GetInstances()) { - for (const auto* participant : instance->GetParticipants()) { - if (participant->GetObjectID() == playerID) - return const_cast(instance); - } - } - - return nullptr; -} - -void ScriptedActivityComponent::ClearInstances() { - for (ActivityInstance* instance : m_Instances) { - delete instance; - } - m_Instances.clear(); -} - -ActivityPlayer* ScriptedActivityComponent::GetActivityPlayerData(LWOOBJID playerID) { - for (auto* activityData : m_ActivityPlayers) { - if (activityData->playerID == playerID) { - return activityData; - } - } - - return nullptr; -} - -void ScriptedActivityComponent::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_Parent); - - return; - } - } -} - -ActivityPlayer* ScriptedActivityComponent::AddActivityPlayerData(LWOOBJID playerID) { - auto* data = GetActivityPlayerData(playerID); - if (data != nullptr) - return data; - - m_ActivityPlayers.push_back(new ActivityPlayer{ playerID, {} }); - EntityManager::Instance()->SerializeEntity(m_Parent); - - return GetActivityPlayerData(playerID); -} - -float_t ScriptedActivityComponent::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 ScriptedActivityComponent::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_Parent); -} - -void ScriptedActivityComponent::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(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(); - if (missionComponent) { - missionComponent->Progress(eMissionTaskType::ACTIVITY, m_ActivityInfo.ActivityID); - } - - // First, get the activity data - auto* activityRewardsTable = CDClientManager::Instance().GetTable(); - std::vector 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(); - std::vector 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_Parent, activityRewards[0].LootMatrixIndex, minCoins, maxCoins); - } -} - -std::vector ActivityInstance::GetParticipants() const { - std::vector 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); } diff --git a/dGame/dComponents/ScriptedActivityComponent.h b/dGame/dComponents/ScriptedActivityComponent.h index 1d49a62d1..dd9c86299 100644 --- a/dGame/dComponents/ScriptedActivityComponent.h +++ b/dGame/dComponents/ScriptedActivityComponent.h @@ -1,381 +1,18 @@ -/* - * Darkflame Universe - * Copyright 2018 - */ -#include "CDClientManager.h" +#ifndef __SCRIPTEDACTIVITYCOMPONENT__H__ +#define __SCRIPTEDACTIVITYCOMPONENT__H__ -#ifndef SCRIPTEDACTIVITYCOMPONENT_H -#define SCRIPTEDACTIVITYCOMPONENT_H - -#include "BitStream.h" -#include "Entity.h" -#include "Component.h" +#include "ActivityComponent.h" #include "eReplicaComponentType.h" -#include "CDActivitiesTable.h" +class Entity; - /** - * Represents an instance of an activity, having participants and score - */ -class ActivityInstance { +class ScriptedActivityComponent : public ActivityComponent { public: - ActivityInstance(Entity* parent, CDActivities activityInfo) { m_Parent = 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 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 ScriptedActivityComponent) - */ - Entity* m_Parent; - - /** - * All the participants of this activity - */ - std::vector 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 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]; + inline static const eReplicaComponentType ComponentType = eReplicaComponentType::SCRIPTED_ACTIVITY; + ScriptedActivityComponent(Entity* parent, int32_t componentId); }; -/** - * Welcome to the absolute behemoth that is the scripted 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 ScriptedActivityComponent : public Component { -public: - static const eReplicaComponentType ComponentType = eReplicaComponentType::SCRIPTED_ACTIVITY; - - ScriptedActivityComponent(Entity* parent, int activityID); - ~ScriptedActivityComponent() 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); +#endif //!__SCRIPTEDACTIVITYCOMPONENT__H__ - /** - * 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& 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& 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 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 m_Instances; - - /** - * The current lobbies for this activity - */ - std::vector m_Queue; - - /** - * All the activity score for the players in this activity - */ - std::vector m_ActivityPlayers; - - /** - * LMIs for team sizes - */ - std::unordered_map m_ActivityLootMatrices; - - /** - * The activity id - * - */ - int32_t m_ActivityID; -}; -#endif // SCRIPTEDACTIVITYCOMPONENT_H diff --git a/dGame/dComponents/ShootingGalleryComponent.cpp b/dGame/dComponents/ShootingGalleryComponent.cpp index d5e12b284..bf54861c4 100644 --- a/dGame/dComponents/ShootingGalleryComponent.cpp +++ b/dGame/dComponents/ShootingGalleryComponent.cpp @@ -2,7 +2,7 @@ #include "EntityManager.h" #include "ScriptedActivityComponent.h" -ShootingGalleryComponent::ShootingGalleryComponent(Entity* parent) : Component(parent) { +ShootingGalleryComponent::ShootingGalleryComponent(Entity* parent, int32_t componentId) : ActivityComponent(parent, componentId) { } ShootingGalleryComponent::~ShootingGalleryComponent() = default; @@ -14,7 +14,7 @@ void ShootingGalleryComponent::SetStaticParams(const StaticShootingGalleryParams void ShootingGalleryComponent::SetDynamicParams(const DynamicShootingGalleryParams& params) { m_DynamicParams = params; m_Dirty = true; - EntityManager::Instance()->SerializeEntity(m_Parent); + EntityManager::Instance()->SerializeEntity(m_ParentEntity); } void ShootingGalleryComponent::Serialize(RakNet::BitStream* outBitStream, bool isInitialUpdate, uint32_t& flags) const { diff --git a/dGame/dComponents/ShootingGalleryComponent.h b/dGame/dComponents/ShootingGalleryComponent.h index c31575f12..2f82b296a 100644 --- a/dGame/dComponents/ShootingGalleryComponent.h +++ b/dGame/dComponents/ShootingGalleryComponent.h @@ -2,7 +2,7 @@ #include "dCommonVars.h" #include "NiPoint3.h" #include "Entity.h" -#include "Component.h" +#include "ActivityComponent.h" #include "eReplicaComponentType.h" /** @@ -71,11 +71,11 @@ struct StaticShootingGalleryParams { * A very ancient component that was used to guide shooting galleries, it's still kind of used but a lot of logic is * also in the related scripts. */ -class ShootingGalleryComponent : public Component { +class ShootingGalleryComponent : public ActivityComponent { public: - static const eReplicaComponentType ComponentType = eReplicaComponentType::SHOOTING_GALLERY; + inline static const eReplicaComponentType ComponentType = eReplicaComponentType::SHOOTING_GALLERY; - explicit ShootingGalleryComponent(Entity* parent); + explicit ShootingGalleryComponent(Entity* parent, int32_t componentId); ~ShootingGalleryComponent(); void Serialize(RakNet::BitStream* outBitStream, bool isInitialUpdate, uint32_t& flags) const; diff --git a/dGame/dComponents/SimplePhysicsComponent.cpp b/dGame/dComponents/SimplePhysicsComponent.cpp index 54a2e6163..a33e135e8 100644 --- a/dGame/dComponents/SimplePhysicsComponent.cpp +++ b/dGame/dComponents/SimplePhysicsComponent.cpp @@ -13,12 +13,12 @@ #include "Entity.h" -SimplePhysicsComponent::SimplePhysicsComponent(uint32_t componentID, Entity* parent) : Component(parent) { - m_Position = m_Parent->GetDefaultPosition(); - m_Rotation = m_Parent->GetDefaultRotation(); +SimplePhysicsComponent::SimplePhysicsComponent(Entity* parent, uint32_t componentID) : Component(parent) { + m_Position = m_ParentEntity->GetDefaultPosition(); + m_Rotation = m_ParentEntity->GetDefaultRotation(); m_IsDirty = true; - const auto& climbable_type = m_Parent->GetVar(u"climbable"); + const auto& climbable_type = m_ParentEntity->GetVar(u"climbable"); if (climbable_type == u"wall") { SetClimbableType(eClimbableType::CLIMBABLE_TYPE_WALL); } else if (climbable_type == u"ladder") { diff --git a/dGame/dComponents/SimplePhysicsComponent.h b/dGame/dComponents/SimplePhysicsComponent.h index 513567105..9fc641a61 100644 --- a/dGame/dComponents/SimplePhysicsComponent.h +++ b/dGame/dComponents/SimplePhysicsComponent.h @@ -28,9 +28,9 @@ enum class eClimbableType : int32_t { */ class SimplePhysicsComponent : public Component { public: - static const eReplicaComponentType ComponentType = eReplicaComponentType::SIMPLE_PHYSICS; + inline static const eReplicaComponentType ComponentType = eReplicaComponentType::SIMPLE_PHYSICS; - SimplePhysicsComponent(uint32_t componentID, Entity* parent); + SimplePhysicsComponent(Entity* parent, uint32_t componentID); ~SimplePhysicsComponent() override; void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags); diff --git a/dGame/dComponents/SkillComponent.cpp b/dGame/dComponents/SkillComponent.cpp index c2f074255..5f06c7702 100644 --- a/dGame/dComponents/SkillComponent.cpp +++ b/dGame/dComponents/SkillComponent.cpp @@ -32,9 +32,9 @@ ProjectileSyncEntry::ProjectileSyncEntry() { std::unordered_map SkillComponent::m_skillBehaviorCache = {}; bool SkillComponent::CastPlayerSkill(const uint32_t behaviorId, const uint32_t skillUid, RakNet::BitStream* bitStream, const LWOOBJID target, uint32_t skillID) { - auto* context = new BehaviorContext(this->m_Parent->GetObjectID()); + auto* context = new BehaviorContext(this->m_ParentEntity->GetObjectID()); - context->caster = m_Parent->GetObjectID(); + context->caster = m_ParentEntity->GetObjectID(); context->skillID = skillID; @@ -130,11 +130,11 @@ void SkillComponent::RegisterPlayerProjectile(const LWOOBJID projectileId, Behav } void SkillComponent::Update(const float deltaTime) { - if (!m_Parent->HasComponent(eReplicaComponentType::BASE_COMBAT_AI) && m_Parent->GetLOT() != 1) { + if (!m_ParentEntity->HasComponent(eReplicaComponentType::BASE_COMBAT_AI) && m_ParentEntity->GetLOT() != 1) { CalculateUpdate(deltaTime); } - if (m_Parent->IsPlayer()) { + if (m_ParentEntity->IsPlayer()) { for (const auto& pair : this->m_managedBehaviors) pair.second->UpdatePlayerSyncs(deltaTime); } @@ -193,7 +193,7 @@ void SkillComponent::Reset() { void SkillComponent::Interrupt() { // TODO: need to check immunities on the destroyable component, but they aren't implemented - auto* combat = m_Parent->GetComponent(); + auto* combat = m_ParentEntity->GetComponent(); if (combat != nullptr && combat->GetStunImmune()) return; for (const auto& behavior : this->m_managedBehaviors) { @@ -201,7 +201,7 @@ void SkillComponent::Interrupt() { behaviorEndEntry.behavior->End(behavior.second, behaviorEndEntry.branchContext, behaviorEndEntry.second); } behavior.second->endEntries.clear(); - if (m_Parent->IsPlayer()) continue; + if (m_ParentEntity->IsPlayer()) continue; behavior.second->Interrupt(); } @@ -256,9 +256,9 @@ SkillExecutionResult SkillComponent::CalculateBehavior(const uint32_t skillId, c auto* behavior = Behavior::CreateBehavior(behaviorId); - auto* context = new BehaviorContext(originatorOverride != LWOOBJID_EMPTY ? originatorOverride : this->m_Parent->GetObjectID(), true); + auto* context = new BehaviorContext(originatorOverride != LWOOBJID_EMPTY ? originatorOverride : this->m_ParentEntity->GetObjectID(), true); - context->caster = m_Parent->GetObjectID(); + context->caster = m_ParentEntity->GetObjectID(); context->skillID = skillId; @@ -268,9 +268,7 @@ SkillExecutionResult SkillComponent::CalculateBehavior(const uint32_t skillId, c behavior->Calculate(context, bitStream, { target, 0 }); - for (auto* script : CppScripts::GetEntityScripts(m_Parent)) { - script->OnSkillCast(m_Parent, skillId); - } + m_ParentEntity->GetScript()->OnSkillCast(m_ParentEntity, skillId); if (!context->foundTarget) { delete bitStream; @@ -305,7 +303,7 @@ SkillExecutionResult SkillComponent::CalculateBehavior(const uint32_t skillId, c RakNet::BitStream message; PacketUtils::WriteHeader(message, eConnectionType::CLIENT, eClientMessageType::GAME_MSG); - message.Write(this->m_Parent->GetObjectID()); + message.Write(this->m_ParentEntity->GetObjectID()); start.Serialize(&message); Game::server->Send(&message, UNASSIGNED_SYSTEM_ADDRESS, true); @@ -431,14 +429,14 @@ void SkillComponent::SyncProjectileCalculation(const ProjectileSyncEntry& entry) DoClientProjectileImpact projectileImpact; projectileImpact.sBitStream.assign((char*)bitStream->GetData(), bitStream->GetNumberOfBytesUsed()); - projectileImpact.i64OwnerID = this->m_Parent->GetObjectID(); + projectileImpact.i64OwnerID = this->m_ParentEntity->GetObjectID(); projectileImpact.i64OrgID = entry.id; projectileImpact.i64TargetID = entry.branchContext.target; RakNet::BitStream message; PacketUtils::WriteHeader(message, eConnectionType::CLIENT, eClientMessageType::GAME_MSG); - message.Write(this->m_Parent->GetObjectID()); + message.Write(this->m_ParentEntity->GetObjectID()); projectileImpact.Serialize(&message); Game::server->Send(&message, UNASSIGNED_SYSTEM_ADDRESS, true); diff --git a/dGame/dComponents/SkillComponent.h b/dGame/dComponents/SkillComponent.h index 034e65ce7..c69b0bdf0 100644 --- a/dGame/dComponents/SkillComponent.h +++ b/dGame/dComponents/SkillComponent.h @@ -59,7 +59,7 @@ struct SkillExecutionResult { */ class SkillComponent : public Component { public: - static const eReplicaComponentType ComponentType = eReplicaComponentType::SKILL; + inline static const eReplicaComponentType ComponentType = eReplicaComponentType::SKILL; explicit SkillComponent(Entity* parent); ~SkillComponent() override; diff --git a/dGame/dComponents/SoundTriggerComponent.cpp b/dGame/dComponents/SoundTriggerComponent.cpp index be62beee5..38d0663e7 100644 --- a/dGame/dComponents/SoundTriggerComponent.cpp +++ b/dGame/dComponents/SoundTriggerComponent.cpp @@ -76,7 +76,7 @@ void SoundTriggerComponent::ActivateMusicCue(const std::string& name) { -1.0f }); dirty = true; - EntityManager::Instance()->SerializeEntity(m_Parent); + EntityManager::Instance()->SerializeEntity(m_ParentEntity); } } @@ -88,6 +88,6 @@ void SoundTriggerComponent::DeactivateMusicCue(const std::string& name) { if (musicCue != this->musicCues.end()) { this->musicCues.erase(musicCue); dirty = true; - EntityManager::Instance()->SerializeEntity(m_Parent); + EntityManager::Instance()->SerializeEntity(m_ParentEntity); } } diff --git a/dGame/dComponents/SoundTriggerComponent.h b/dGame/dComponents/SoundTriggerComponent.h index 954d8495e..ce40a0336 100644 --- a/dGame/dComponents/SoundTriggerComponent.h +++ b/dGame/dComponents/SoundTriggerComponent.h @@ -20,7 +20,7 @@ struct MusicCue { */ class SoundTriggerComponent : public Component { public: - static const eReplicaComponentType ComponentType = eReplicaComponentType::SOUND_TRIGGER; + inline static const eReplicaComponentType ComponentType = eReplicaComponentType::SOUND_TRIGGER; explicit SoundTriggerComponent(Entity* parent); ~SoundTriggerComponent() override; diff --git a/dGame/dComponents/SwitchComponent.cpp b/dGame/dComponents/SwitchComponent.cpp index b393bbef0..75c44c669 100644 --- a/dGame/dComponents/SwitchComponent.cpp +++ b/dGame/dComponents/SwitchComponent.cpp @@ -3,119 +3,102 @@ #include "eTriggerEventType.h" #include "RenderComponent.h" -std::vector SwitchComponent::petSwitches; +std::vector SwitchComponent::switches; SwitchComponent::SwitchComponent(Entity* parent) : Component(parent) { m_Active = false; +} - m_ResetTime = m_Parent->GetVarAs(u"switch_reset_time"); +void SwitchComponent::Startup() { + m_Rebuild = m_ParentEntity->GetComponent(); +} - m_Rebuild = m_Parent->GetComponent(); +void SwitchComponent::LoadConfigData() { + m_ResetTime = m_ParentEntity->GetVarAs(u"switch_reset_time"); } SwitchComponent::~SwitchComponent() { - const auto& iterator = std::find(petSwitches.begin(), petSwitches.end(), this); - - if (iterator != petSwitches.end()) { - petSwitches.erase(iterator); - } + switches.erase(std::remove(switches.begin(), switches.end(), this), switches.end()); } void SwitchComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags) { outBitStream->Write(m_Active); } -void SwitchComponent::SetActive(bool active) { +void SwitchComponent::SetActive(const bool active) { + if (m_Active == active) return; m_Active = active; - - if (m_PetBouncer != nullptr) { - m_PetBouncer->SetPetBouncerEnabled(active); - } -} - -bool SwitchComponent::GetActive() const { - return m_Active; + if (m_Bouncer) m_Bouncer->SetBouncerEnabled(active); } void SwitchComponent::EntityEnter(Entity* entity) { - if (!m_Active) { - if (m_Rebuild) { - if (m_Rebuild->GetState() != eRebuildState::COMPLETED) return; - } - m_Active = true; - if (!m_Parent) return; - m_Parent->TriggerEvent(eTriggerEventType::ACTIVATED, entity); + if (m_Active) return; + if (m_Rebuild) { + if (m_Rebuild->GetState() != eRebuildState::COMPLETED) return; + } + m_Active = true; + if (!m_ParentEntity) return; + m_ParentEntity->TriggerEvent(eTriggerEventType::ACTIVATED, entity); - const auto grpName = m_Parent->GetVarAsString(u"grp_name"); + const auto grpName = m_ParentEntity->GetVarAsString(u"grp_name"); - if (!grpName.empty()) { - const auto entities = EntityManager::Instance()->GetEntitiesInGroup(grpName); + if (!grpName.empty()) { + const auto entities = EntityManager::Instance()->GetEntitiesInGroup(grpName); - for (auto* entity : entities) { - entity->OnFireEventServerSide(entity, "OnActivated"); - } + for (auto* entity : entities) { + entity->OnFireEventServerSide(entity, "OnActivated"); } + } - m_Timer = m_ResetTime; - - if (m_PetBouncer != nullptr) { - GameMessages::SendPlayFXEffect(m_Parent->GetObjectID(), 2602, u"pettriggeractive", "BounceEffect", LWOOBJID_EMPTY, 1, 1, true); - RenderComponent::PlayAnimation(m_Parent, u"engaged"); - m_PetBouncer->SetPetBouncerEnabled(true); - } else { - EntityManager::Instance()->SerializeEntity(m_Parent); - } + m_Timer = m_ResetTime; + if (m_Bouncer) { + GameMessages::SendPlayFXEffect(m_ParentEntity->GetObjectID(), 2602, u"pettriggeractive", "BounceEffect", LWOOBJID_EMPTY, 1, 1, true); + RenderComponent::PlayAnimation(m_ParentEntity, u"engaged"); + m_Bouncer->SetBouncerEnabled(true); + } else { + EntityManager::Instance()->SerializeEntity(m_ParentEntity); } } -void SwitchComponent::EntityLeave(Entity* entity) { - -} +void SwitchComponent::Update(const float deltaTime) { + if (!m_Active) return; + m_Timer -= deltaTime; -void SwitchComponent::Update(float deltaTime) { - if (m_Active) { - m_Timer -= deltaTime; + if (m_Timer > 0.0f) return; - if (m_Timer <= 0.0f) { - m_Active = false; - if (!m_Parent) return; - m_Parent->TriggerEvent(eTriggerEventType::DEACTIVATED, m_Parent); + m_Active = false; + if (!m_ParentEntity) return; - const auto grpName = m_Parent->GetVarAsString(u"grp_name"); + m_ParentEntity->TriggerEvent(eTriggerEventType::DEACTIVATED, m_ParentEntity); - if (!grpName.empty()) { - const auto entities = EntityManager::Instance()->GetEntitiesInGroup(grpName); + const auto grpName = m_ParentEntity->GetVarAsString(u"grp_name"); - for (auto* entity : entities) { - entity->OnFireEventServerSide(entity, "OnDectivated"); - } - } + if (!grpName.empty()) { + const auto entities = EntityManager::Instance()->GetEntitiesInGroup(grpName); - if (m_PetBouncer != nullptr) { - m_PetBouncer->SetPetBouncerEnabled(false); - } else { - EntityManager::Instance()->SerializeEntity(m_Parent); - } + for (auto* entity : entities) { + entity->OnFireEventServerSide(entity, "OnDectivated"); } } -} -Entity* SwitchComponent::GetParentEntity() const { - return m_Parent; + if (m_Bouncer) { + m_Bouncer->SetBouncerEnabled(false); + } else { + EntityManager::Instance()->SerializeEntity(m_ParentEntity); + } } -SwitchComponent* SwitchComponent::GetClosestSwitch(NiPoint3 position) { - float closestDistance = 0; +SwitchComponent* SwitchComponent::GetClosestSwitch(const NiPoint3& position) { + float closestDistance = 0.0f; SwitchComponent* closest = nullptr; - for (SwitchComponent* petSwitch : petSwitches) { - float distance = Vector3::DistanceSquared(petSwitch->m_Parent->GetPosition(), position); + for (auto* petSwitch : switches) { + float distance = Vector3::DistanceSquared(petSwitch->m_ParentEntity->GetPosition(), position); - if (closest == nullptr || distance < closestDistance) { - closestDistance = distance; - closest = petSwitch; - } + if (closest && distance >= closestDistance) continue; + closestDistance = distance; + closest = petSwitch; } return closest; @@ -123,14 +106,10 @@ SwitchComponent* SwitchComponent::GetClosestSwitch(NiPoint3 position) { void SwitchComponent::SetPetBouncer(BouncerComponent* value) { - m_PetBouncer = value; - - if (value != nullptr) { - m_PetBouncer->SetPetEnabled(true); - petSwitches.push_back(this); - } -} + m_Bouncer = value; -BouncerComponent* SwitchComponent::GetPetBouncer() const { - return m_PetBouncer; + if (!value) return; + m_Bouncer->SetBounceOnCollision(true); + EntityManager::Instance()->SerializeEntity(m_Bouncer->GetParentEntity()); + switches.push_back(this); } diff --git a/dGame/dComponents/SwitchComponent.h b/dGame/dComponents/SwitchComponent.h index fde3cfc0e..1d7a75019 100644 --- a/dGame/dComponents/SwitchComponent.h +++ b/dGame/dComponents/SwitchComponent.h @@ -5,7 +5,7 @@ #include "Entity.h" #include "GameMessages.h" #include "EntityManager.h" -#include "RebuildComponent.h" +#include "QuickBuildComponent.h" #include "BouncerComponent.h" #include #include "Component.h" @@ -16,27 +16,27 @@ */ class SwitchComponent : public Component { public: - static const eReplicaComponentType ComponentType = eReplicaComponentType::SWITCH; + inline static const eReplicaComponentType ComponentType = eReplicaComponentType::SWITCH; SwitchComponent(Entity* parent); ~SwitchComponent() override; + void LoadConfigData() override; + void Startup() override; void Update(float deltaTime) override; - Entity* GetParentEntity() const; - void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags); /** * Sets whether the switch is on or off. * @param active whether the switch is on or off. */ - void SetActive(bool active); + void SetActive(const bool active); /** * Returns whether the switch is on or off. */ - bool GetActive() const; + bool GetActive() const { return m_Active; } /** * Sets the attached pet bouncer @@ -47,35 +47,30 @@ class SwitchComponent : public Component { /** * Returns the attached pet bouncer */ - BouncerComponent* GetPetBouncer() const; + BouncerComponent* GetBouncer() const { return m_Bouncer; } /** * Invoked when a entity enters the trigger area. */ void EntityEnter(Entity* entity); - /** - * Invoked when a entity leaves the trigger area. - */ - void EntityLeave(Entity* entity); - /** * Returns the closest switch from a given position * @param position the position to check * @return the closest switch from a given position */ - static SwitchComponent* GetClosestSwitch(NiPoint3 position); + static SwitchComponent* GetClosestSwitch(const NiPoint3& position); private: /** * A list of all pet switches. */ - static std::vector petSwitches; + static std::vector switches; /** * Attached rebuild component. */ - RebuildComponent* m_Rebuild; + QuickBuildComponent* m_Rebuild; /** * If the switch is on or off. @@ -100,7 +95,7 @@ class SwitchComponent : public Component { /** * Attached pet bouncer */ - BouncerComponent* m_PetBouncer = nullptr; + BouncerComponent* m_Bouncer = nullptr; }; #endif // SWITCHCOMPONENT_H diff --git a/dGame/dComponents/TriggerComponent.cpp b/dGame/dComponents/TriggerComponent.cpp index 7adf47a83..ee5900a1b 100644 --- a/dGame/dComponents/TriggerComponent.cpp +++ b/dGame/dComponents/TriggerComponent.cpp @@ -10,13 +10,13 @@ #include "MissionComponent.h" #include "PhantomPhysicsComponent.h" #include "Player.h" -#include "RebuildComponent.h" +#include "QuickBuildComponent.h" #include "SkillComponent.h" #include "eEndBehavior.h" TriggerComponent::TriggerComponent(Entity* parent, const std::string triggerInfo): Component(parent) { - m_Parent = parent; + m_ParentEntity = parent; m_Trigger = nullptr; std::vector tokens = GeneralUtils::SplitString(triggerInfo, ':'); @@ -116,7 +116,7 @@ void TriggerComponent::HandleTriggerCommand(LUTriggers::Command* command, Entity HandleCastSkill(targetEntity, command->args); break; case eTriggerCommandType::DISPLAY_ZONE_SUMMARY: - GameMessages::SendDisplayZoneSummary(targetEntity->GetObjectID(), targetEntity->GetSystemAddress(), false, command->args == "1", m_Parent->GetObjectID()); + GameMessages::SendDisplayZoneSummary(targetEntity->GetObjectID(), targetEntity->GetSystemAddress(), false, command->args == "1", m_ParentEntity->GetObjectID()); break; case eTriggerCommandType::SET_PHYSICS_VOLUME_EFFECT: HandleSetPhysicsVolumeEffect(targetEntity, argArray); @@ -164,7 +164,7 @@ void TriggerComponent::HandleTriggerCommand(LUTriggers::Command* command, Entity std::vector TriggerComponent::GatherTargets(LUTriggers::Command* command, Entity* optionalTarget) { std::vector entities = {}; - if (command->target == "self") entities.push_back(m_Parent); + if (command->target == "self") entities.push_back(m_ParentEntity); else if (command->target == "zone") { /*TODO*/ } else if (command->target == "target" && optionalTarget) entities.push_back(optionalTarget); else if (command->target == "targetTeam" && optionalTarget) { @@ -184,15 +184,13 @@ std::vector TriggerComponent::GatherTargets(LUTriggers::Command* comman } void TriggerComponent::HandleFireEvent(Entity* targetEntity, std::string args) { - for (CppScripts::Script* script : CppScripts::GetEntityScripts(targetEntity)) { - script->OnFireEventServerSide(targetEntity, m_Parent, args, 0, 0, 0); - } + targetEntity->GetScript()->OnFireEventServerSide(targetEntity, m_ParentEntity, args, 0, 0, 0); } void TriggerComponent::HandleDestroyObject(Entity* targetEntity, std::string args){ uint32_t killType; GeneralUtils::TryParse(args, killType); - targetEntity->Smash(m_Parent->GetObjectID(), static_cast(killType)); + targetEntity->Smash(m_ParentEntity->GetObjectID(), static_cast(killType)); } void TriggerComponent::HandleToggleTrigger(Entity* targetEntity, std::string args){ @@ -205,12 +203,12 @@ void TriggerComponent::HandleToggleTrigger(Entity* targetEntity, std::string arg } void TriggerComponent::HandleResetRebuild(Entity* targetEntity, std::string args){ - auto* rebuildComponent = targetEntity->GetComponent(); - if (!rebuildComponent) { + auto* quickBuildComponent = targetEntity->GetComponent(); + if (!quickBuildComponent) { Game::logger->LogDebug("TriggerComponent::HandleResetRebuild", "Rebuild component not found!"); return; } - rebuildComponent->ResetRebuild(args == "1"); + quickBuildComponent->ResetRebuild(args == "1"); } void TriggerComponent::HandleMoveObject(Entity* targetEntity, std::vector argArray){ @@ -237,7 +235,7 @@ void TriggerComponent::HandleRotateObject(Entity* targetEntity, std::vector argArray){ if (argArray.size() < 3) return; - auto* phantomPhysicsComponent = m_Parent->GetComponent(); + auto* phantomPhysicsComponent = m_ParentEntity->GetComponent(); if (!phantomPhysicsComponent) { Game::logger->LogDebug("TriggerComponent::HandlePushObject", "Phantom Physics component not found!"); return; @@ -249,12 +247,12 @@ void TriggerComponent::HandlePushObject(Entity* targetEntity, std::vectorSetDirection(direction); - EntityManager::Instance()->SerializeEntity(m_Parent); + EntityManager::Instance()->SerializeEntity(m_ParentEntity); } void TriggerComponent::HandleRepelObject(Entity* targetEntity, std::string args){ - auto* phantomPhysicsComponent = m_Parent->GetComponent(); + auto* phantomPhysicsComponent = m_ParentEntity->GetComponent(); if (!phantomPhysicsComponent) { Game::logger->LogDebug("TriggerComponent::HandleRepelObject", "Phantom Physics component not found!"); return; @@ -265,7 +263,7 @@ void TriggerComponent::HandleRepelObject(Entity* targetEntity, std::string args) phantomPhysicsComponent->SetEffectType(ePhysicsEffectType::REPULSE); phantomPhysicsComponent->SetDirectionalMultiplier(forceMultiplier); - auto triggerPos = m_Parent->GetPosition(); + auto triggerPos = m_ParentEntity->GetPosition(); auto targetPos = targetEntity->GetPosition(); // normalize the vectors to get the direction @@ -274,7 +272,7 @@ void TriggerComponent::HandleRepelObject(Entity* targetEntity, std::string args) NiPoint3 direction = delta / length; phantomPhysicsComponent->SetDirection(direction); - EntityManager::Instance()->SerializeEntity(m_Parent); + EntityManager::Instance()->SerializeEntity(m_ParentEntity); } void TriggerComponent::HandleSetTimer(Entity* targetEntity, std::vector argArray){ @@ -284,11 +282,11 @@ void TriggerComponent::HandleSetTimer(Entity* targetEntity, std::vector(argArray.at(1), time); - m_Parent->AddTimer(argArray.at(0), time); + m_ParentEntity->AddTimer(argArray.at(0), time); } void TriggerComponent::HandleCancelTimer(Entity* targetEntity, std::string args){ - m_Parent->CancelTimer(args); + m_ParentEntity->CancelTimer(args); } void TriggerComponent::HandlePlayCinematic(Entity* targetEntity, std::vector argArray) { @@ -334,7 +332,7 @@ void TriggerComponent::HandleUpdateMission(Entity* targetEntity, std::vectorGetComponent(); + auto* missionComponent = targetEntity->GetComponent(); if (!missionComponent){ Game::logger->LogDebug("TriggerComponent::HandleUpdateMission", "Mission component not found!"); return; diff --git a/dGame/dComponents/TriggerComponent.h b/dGame/dComponents/TriggerComponent.h index df65707f3..90ecc52c7 100644 --- a/dGame/dComponents/TriggerComponent.h +++ b/dGame/dComponents/TriggerComponent.h @@ -7,7 +7,7 @@ class TriggerComponent : public Component { public: - static const eReplicaComponentType ComponentType = eReplicaComponentType::TRIGGER; + inline static const eReplicaComponentType ComponentType = eReplicaComponentType::TRIGGER; explicit TriggerComponent(Entity* parent, const std::string triggerInfo); diff --git a/dGame/dComponents/VendorComponent.cpp b/dGame/dComponents/VendorComponent.cpp index e89cc9263..73f8363dc 100644 --- a/dGame/dComponents/VendorComponent.cpp +++ b/dGame/dComponents/VendorComponent.cpp @@ -1,6 +1,6 @@ #include "VendorComponent.h" -#include +#include "BitStream.h" #include "Game.h" #include "dServer.h" @@ -9,132 +9,132 @@ #include "CDVendorComponentTable.h" #include "CDLootMatrixTable.h" #include "CDLootTableTable.h" +#include "CDItemComponentTable.h" VendorComponent::VendorComponent(Entity* parent) : Component(parent) { + m_HasStandardCostItems = false; + m_HasMultiCostItems = false; +} + +void VendorComponent::Startup() { SetupConstants(); RefreshInventory(true); } -VendorComponent::~VendorComponent() = default; - void VendorComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags) { - outBitStream->Write1(); - outBitStream->Write1(); // Has standard items (Required for vendors with missions.) - outBitStream->Write(HasCraftingStation()); // Has multi use items + outBitStream->Write(bIsInitialUpdate || m_DirtyVendor); + if (bIsInitialUpdate || m_DirtyVendor) { + outBitStream->Write(m_HasStandardCostItems); + outBitStream->Write(m_HasMultiCostItems); + } } void VendorComponent::OnUse(Entity* originator) { - GameMessages::SendVendorOpenWindow(m_Parent, originator->GetSystemAddress()); - GameMessages::SendVendorStatusUpdate(m_Parent, originator->GetSystemAddress()); -} - -float VendorComponent::GetBuyScalar() const { - return m_BuyScalar; -} - -float VendorComponent::GetSellScalar() const { - return m_SellScalar; -} - -void VendorComponent::SetBuyScalar(float value) { - m_BuyScalar = value; -} - -void VendorComponent::SetSellScalar(float value) { - m_SellScalar = value; -} - -std::map& VendorComponent::GetInventory() { - return m_Inventory; -} - -bool VendorComponent::HasCraftingStation() { - // As far as we know, only Umami has a crafting station - return m_Parent->GetLOT() == 13800; + GameMessages::SendVendorOpenWindow(m_ParentEntity, originator->GetSystemAddress()); + GameMessages::SendVendorStatusUpdate(m_ParentEntity, originator->GetSystemAddress()); } void VendorComponent::RefreshInventory(bool isCreation) { - //Custom code for Max vanity NPC - if (m_Parent->GetLOT() == 9749 && Game::server->GetZoneID() == 1201) { + SetHasStandardCostItems(false); + SetHasMultiCostItems(false); + + // Custom code for Max vanity NPC + if (m_ParentEntity->GetLOT() == 9749 && Game::server->GetZoneID() == 1201) { if (!isCreation) return; - m_Inventory.insert({ 11909, 0 }); //Top hat w frog - m_Inventory.insert({ 7785, 0 }); //Flash bulb - m_Inventory.insert({ 12764, 0 }); //Big fountain soda - m_Inventory.insert({ 12241, 0 }); //Hot cocoa (from fb) + SetHasStandardCostItems(true); + m_Inventory.push_back(SoldItem(11909, 0)); // Top hat w frog + m_Inventory.push_back(SoldItem(7785, 0)); // Flash bulb + m_Inventory.push_back(SoldItem(12764, 0)); // Big fountain soda + m_Inventory.push_back(SoldItem(12241, 0)); // Hot cocoa (from fb) return; } m_Inventory.clear(); auto* lootMatrixTable = CDClientManager::Instance().GetTable(); - std::vector lootMatrices = lootMatrixTable->Query([=](CDLootMatrix entry) { return (entry.LootMatrixIndex == m_LootMatrixID); }); + const auto lootMatrices = lootMatrixTable->Query([=](CDLootMatrix entry) { return (entry.LootMatrixIndex == m_LootMatrixID); }); if (lootMatrices.empty()) return; // Done with lootMatrix table auto* lootTableTable = CDClientManager::Instance().GetTable(); + auto* itemComponentTable = CDClientManager::Instance().GetTable(); + auto* compRegistryTable = CDClientManager::Instance().GetTable(); for (const auto& lootMatrix : lootMatrices) { int lootTableID = lootMatrix.LootTableIndex; - std::vector vendorItems = lootTableTable->Query([=](CDLootTable entry) { return (entry.LootTableIndex == lootTableID); }); + auto vendorItems = lootTableTable->Query([=](CDLootTable entry) { return (entry.LootTableIndex == lootTableID); }); if (lootMatrix.maxToDrop == 0 || lootMatrix.minToDrop == 0) { - for (CDLootTable item : vendorItems) { - m_Inventory.insert({ item.itemid, item.sortPriority }); + for (const auto& item : vendorItems) { + if (!m_HasStandardCostItems || !m_HasMultiCostItems) { + auto itemComponentID = compRegistryTable->GetByIDAndType(item.itemid, eReplicaComponentType::ITEM); + auto itemComponent = itemComponentTable->GetItemComponentByID(itemComponentID); + if (!m_HasStandardCostItems && itemComponent.baseValue != -1) SetHasStandardCostItems(true); + if (!m_HasMultiCostItems && !itemComponent.currencyCosts.empty()) SetHasMultiCostItems(true); + } + m_Inventory.push_back(SoldItem(item.itemid, item.sortPriority)); } } else { auto randomCount = GeneralUtils::GenerateRandomNumber(lootMatrix.minToDrop, lootMatrix.maxToDrop); for (size_t i = 0; i < randomCount; i++) { if (vendorItems.empty()) break; - auto randomItemIndex = GeneralUtils::GenerateRandomNumber(0, vendorItems.size() - 1); - - const auto& randomItem = vendorItems[randomItemIndex]; - + const auto& randomItem = vendorItems.at(randomItemIndex); vendorItems.erase(vendorItems.begin() + randomItemIndex); - - m_Inventory.insert({ randomItem.itemid, randomItem.sortPriority }); + if (!m_HasStandardCostItems || !m_HasMultiCostItems) { + auto itemComponentID = compRegistryTable->GetByIDAndType(randomItem.itemid, eReplicaComponentType::ITEM, -1); + if (itemComponentID == -1) { + Game::logger->Log("VendorComponent", "Attempted to add item %i with ItemComponent ID -1 to vendor %i inventory. Not adding item!", itemComponentID, m_ParentEntity->GetLOT()); + continue; + } + auto itemComponent = itemComponentTable->GetItemComponentByID(itemComponentID); + if (!m_HasStandardCostItems && itemComponent.baseValue != -1) SetHasStandardCostItems(true); + if (!m_HasMultiCostItems && !itemComponent.currencyCosts.empty()) SetHasMultiCostItems(true); + } + m_Inventory.push_back(SoldItem(randomItem.itemid, randomItem.sortPriority)); } } } - //Because I want a vendor to sell these cameras - if (m_Parent->GetLOT() == 13569) { + // Because I (Max) want a vendor to sell these cameras + if (m_ParentEntity->GetLOT() == 13569) { auto randomCamera = GeneralUtils::GenerateRandomNumber(0, 2); + LOT camera = 0; + DluAssert(randomCamera >= 0 && randomCamera <= 2); switch (randomCamera) { case 0: - m_Inventory.insert({ 16253, 0 }); //Grungagroid + camera = 16253; // Grungagroid break; case 1: - m_Inventory.insert({ 16254, 0 }); //Hipstabrick + camera = 16254; // Hipstabrick break; case 2: - m_Inventory.insert({ 16204, 0 }); //Megabrixel snapshot - break; - default: + camera = 16204; // Megabrixel snapshot break; } + m_Inventory.push_back(SoldItem(camera, 0)); //Megabrixel snapshot } // Callback timer to refresh this inventory. - m_Parent->AddCallbackTimer(m_RefreshTimeSeconds, [this]() { + m_ParentEntity->AddCallbackTimer(m_RefreshTimeSeconds, [this]() { RefreshInventory(); }); - GameMessages::SendVendorStatusUpdate(m_Parent, UNASSIGNED_SYSTEM_ADDRESS); + EntityManager::Instance()->SerializeEntity(m_ParentEntity); + GameMessages::SendVendorStatusUpdate(m_ParentEntity, UNASSIGNED_SYSTEM_ADDRESS); } void VendorComponent::SetupConstants() { auto* compRegistryTable = CDClientManager::Instance().GetTable(); - int componentID = compRegistryTable->GetByIDAndType(m_Parent->GetLOT(), eReplicaComponentType::VENDOR); + int componentID = compRegistryTable->GetByIDAndType(m_ParentEntity->GetLOT(), eReplicaComponentType::VENDOR); auto* vendorComponentTable = CDClientManager::Instance().GetTable(); std::vector vendorComps = vendorComponentTable->Query([=](CDVendorComponent entry) { return (entry.id == componentID); }); if (vendorComps.empty()) return; - m_BuyScalar = vendorComps[0].buyScalar; - m_SellScalar = vendorComps[0].sellScalar; - m_RefreshTimeSeconds = vendorComps[0].refreshTimeSeconds; - m_LootMatrixID = vendorComps[0].LootMatrixIndex; + auto vendorData = vendorComps.at(0); + m_BuyScalar = vendorData.buyScalar; + m_SellScalar = vendorData.sellScalar; + m_RefreshTimeSeconds = vendorData.refreshTimeSeconds; + m_LootMatrixID = vendorData.LootMatrixIndex; } -bool VendorComponent::SellsItem(const LOT item) const { - return m_Inventory.find(item) != m_Inventory.end(); -} + diff --git a/dGame/dComponents/VendorComponent.h b/dGame/dComponents/VendorComponent.h index cbff0cfdb..70f748a2f 100644 --- a/dGame/dComponents/VendorComponent.h +++ b/dGame/dComponents/VendorComponent.h @@ -2,6 +2,7 @@ #ifndef VENDORCOMPONENT_H #define VENDORCOMPONENT_H +#include #include "CDClientManager.h" #include "Component.h" #include "Entity.h" @@ -9,54 +10,52 @@ #include "RakNetTypes.h" #include "eReplicaComponentType.h" +struct SoldItem { + SoldItem(const LOT lot, const int32_t sortPriority) { + this->lot = lot; + this->sortPriority = sortPriority; + }; + LOT lot = 0; + int32_t sortPriority = 0; +}; + /** * A component for vendor NPCs. A vendor sells items to the player. */ class VendorComponent : public Component { public: - static const eReplicaComponentType ComponentType = eReplicaComponentType::VENDOR; + inline static const eReplicaComponentType ComponentType = eReplicaComponentType::VENDOR; VendorComponent(Entity* parent); - ~VendorComponent() override; + void Startup() override; void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags); void OnUse(Entity* originator) override; - /** - * Gets the buy scaler - * @return the buy scaler - */ - float GetBuyScalar() const; + float GetBuyScalar() const { return m_BuyScalar; } - /** - * Sets the buy scalar. - * @param value the new value. - */ - void SetBuyScalar(float value); + float GetSellScalar() const { return m_SellScalar; } - /** - * Gets the buy scaler - * @return the buy scaler - */ - float GetSellScalar() const; + void SetBuyScalar(const float value) { m_BuyScalar = value; } - /** - * Sets the sell scalar. - * @param value the new value. - */ - void SetSellScalar(float value); + void SetSellScalar(const float value) { m_SellScalar = value; } - /** - * True if the NPC LOT is 13800, the only NPC with a crafting station. - */ - bool HasCraftingStation(); + std::vector& GetInventory() { + return m_Inventory; + } - /** - * Gets the list if items the vendor sells. - * @return the list of items. - */ - std::map& GetInventory(); + void SetHasMultiCostItems(const bool hasMultiCostItems) { + if (m_HasMultiCostItems == hasMultiCostItems) return; + m_HasMultiCostItems = hasMultiCostItems; + m_DirtyVendor = true; + } + + void SetHasStandardCostItems(const bool hasStandardCostItems) { + if (m_HasStandardCostItems == hasStandardCostItems) return; + m_HasStandardCostItems = hasStandardCostItems; + m_DirtyVendor = true; + } /** * Refresh the inventory of this vendor. @@ -68,32 +67,40 @@ class VendorComponent : public Component { */ void SetupConstants(); - bool SellsItem(const LOT item) const; + bool SellsItem(const LOT item) const { + return std::count_if(m_Inventory.begin(), m_Inventory.end(), [item](const SoldItem& lhs) { + return lhs.lot == item; + }) > 0; + } private: /** * The buy scalar. */ - float m_BuyScalar; + float m_BuyScalar = 0.0f; /** * The sell scalar. */ - float m_SellScalar; + float m_SellScalar = 0.0f; /** * The refresh time of this vendors' inventory. */ - float m_RefreshTimeSeconds; + float m_RefreshTimeSeconds = 0.0f; /** * Loot matrix id of this vendor. */ - uint32_t m_LootMatrixID; + uint32_t m_LootMatrixID = 0; /** * The list of items the vendor sells. */ - std::map m_Inventory; + std::vector m_Inventory; + + bool m_DirtyVendor = false; + bool m_HasStandardCostItems = false; + bool m_HasMultiCostItems = false; }; #endif // VENDORCOMPONENT_H diff --git a/dGame/dEntity/CMakeLists.txt b/dGame/dEntity/CMakeLists.txt index 4bb49799e..e1f28ee41 100644 --- a/dGame/dEntity/CMakeLists.txt +++ b/dGame/dEntity/CMakeLists.txt @@ -1,2 +1,5 @@ -set(DGAME_DENTITY_SOURCES "EntityCallbackTimer.cpp" - "EntityTimer.cpp" PARENT_SCOPE) +set(DGAME_DENTITY_SOURCES + "Entity.cpp" + "EntityCallbackTimer.cpp" + "EntityTimer.cpp" + PARENT_SCOPE) diff --git a/dGame/dEntity/Entity.cpp b/dGame/dEntity/Entity.cpp new file mode 100644 index 000000000..e16f573f2 --- /dev/null +++ b/dGame/dEntity/Entity.cpp @@ -0,0 +1,1521 @@ +#include "dCommonVars.h" +#include "Entity.h" +#include "CDClientManager.h" +#include "Game.h" +#include "dLogger.h" +#include "dServer.h" +#include "GameMessages.h" +#include "EntityManager.h" +#include "dZoneManager.h" +#include "Zone.h" +#include "Spawner.h" +#include "Player.h" +#include "LUTriggers.h" +#include "User.h" +#include "EntityTimer.h" +#include "EntityCallbackTimer.h" +#include "Loot.h" +#include "eMissionTaskType.h" +#include "eTriggerEventType.h" +#include "eObjectBits.h" + +//Component includes: +#include "Component.h" +#include "ControllablePhysicsComponent.h" +#include "RenderComponent.h" +#include "MultiZoneEntranceComponent.h" +#include "CharacterComponent.h" +#include "DestroyableComponent.h" +#include "BuffComponent.h" +#include "BouncerComponent.h" +#include "InventoryComponent.h" +#include "LevelProgressionComponent.h" +#include "PlayerForcedMovementComponent.h" +#include "ScriptComponent.h" +#include "SkillComponent.h" +#include "SimplePhysicsComponent.h" +#include "SwitchComponent.h" +#include "PhantomPhysicsComponent.h" +#include "RigidbodyPhantomPhysicsComponent.h" +#include "MovingPlatformComponent.h" +#include "MissionComponent.h" +#include "MissionOfferComponent.h" +#include "QuickBuildComponent.h" +#include "BuildBorderComponent.h" +#include "MovementAIComponent.h" +#include "VendorComponent.h" +#include "RocketLaunchpadControlComponent.h" +#include "PropertyComponent.h" +#include "CollectibleComponent.h" +#include "BaseCombatAIComponent.h" +#include "PropertyManagementComponent.h" +#include "PropertyVendorComponent.h" +#include "ProximityMonitorComponent.h" +#include "PropertyEntranceComponent.h" +#include "ModelBehaviorComponent.h" +#include "PetComponent.h" +#include "HavokVehiclePhysicsComponent.h" +#include "PossessableComponent.h" +#include "PossessionComponent.h" +#include "ModuleAssemblyComponent.h" +#include "SoundTriggerComponent.h" +#include "ShootingGalleryComponent.h" +#include "RailActivatorComponent.h" +#include "LUPExhibitComponent.h" +#include "TriggerComponent.h" +#include "eGameMasterLevel.h" +#include "eReplicaComponentType.h" +#include "eReplicaPacketType.h" +#include "RacingStatsComponent.h" +#include "MinigameControlComponent.h" +#include "ItemComponent.h" +#include "DonationVendorComponent.h" +#include "GateRushComponent.h" +#include "RacingSoundTriggerComponent.h" +#include "AchievementVendorComponent.h" +#include "MutableModelBehaviorComponent.h" +#include "RacingComponent.h" + +// Table includes +#include "CDComponentsRegistryTable.h" +#include "CDObjectSkillsTable.h" +#include "CDObjectsTable.h" +#include "CDSkillBehaviorTable.h" +#include "CppScripts.h" +#include "InvalidScript.h" + +const std::vector Entity::m_ComponentWhitelists = { + { // Unknown use case + eReplicaComponentType::CONTROLLABLE_PHYSICS, + eReplicaComponentType::SIMPLE_PHYSICS, + eReplicaComponentType::RENDER + }, + { // Used for BBB + eReplicaComponentType::RENDER, + eReplicaComponentType::DESTROYABLE, + eReplicaComponentType::ITEM, + eReplicaComponentType::BLUEPRINT, + eReplicaComponentType::MODEL_BEHAVIOR, + eReplicaComponentType::CONTROLLABLE_PHYSICS, + eReplicaComponentType::SIMPLE_PHYSICS, + eReplicaComponentType::SPAWN + }, + { // Unknown use case + eReplicaComponentType::RENDER, + eReplicaComponentType::ITEM, + eReplicaComponentType::BLUEPRINT, + }, + { // Used for Pets + eReplicaComponentType::PET, + eReplicaComponentType::SKILL, + eReplicaComponentType::DESTROYABLE, + eReplicaComponentType::RENDER, + eReplicaComponentType::CONTROLLABLE_PHYSICS + }, + { // Unknown use case + eReplicaComponentType::CONTROLLABLE_PHYSICS, + eReplicaComponentType::SIMPLE_PHYSICS, + eReplicaComponentType::RENDER, + }, +}; + +const std::array Entity::m_ComponentOrder = { + eReplicaComponentType::POSSESSABLE, + eReplicaComponentType::BLUEPRINT, + eReplicaComponentType::MODULE_ASSEMBLY, + eReplicaComponentType::CONTROLLABLE_PHYSICS, + eReplicaComponentType::PROJECTILE_PHYSICS, + eReplicaComponentType::PHYSICS_SYSTEM, + eReplicaComponentType::VEHICLE_PHYSICS, + eReplicaComponentType::HAVOK_VEHICLE_PHYSICS, + eReplicaComponentType::RIGID_BODY_PHANTOM_PHYSICS, + eReplicaComponentType::SIMPLE_PHYSICS, + eReplicaComponentType::PHANTOM_PHYSICS, + eReplicaComponentType::DESTROYABLE, + eReplicaComponentType::MINIFIG, + eReplicaComponentType::CHARACTER, + eReplicaComponentType::PLAYER_FORCED_MOVEMENT, + eReplicaComponentType::POSSESSION, + eReplicaComponentType::MOUNT_CONTROL, + eReplicaComponentType::PET, + eReplicaComponentType::INVENTORY, + eReplicaComponentType::PROXIMITY_MONITOR, + eReplicaComponentType::MOVEMENT_AI, + eReplicaComponentType::SCRIPT, + eReplicaComponentType::SKILL, + eReplicaComponentType::BASE_COMBAT_AI, + eReplicaComponentType::SPAWN, + eReplicaComponentType::ITEM, + eReplicaComponentType::QUICK_BUILD, + eReplicaComponentType::BUILD_BORDER, + eReplicaComponentType::MODULE, + eReplicaComponentType::BUILD_CONTROLLER, + eReplicaComponentType::BUILD_ACTIVATOR, + eReplicaComponentType::QUICK_BUILD, + eReplicaComponentType::MISSION_OFFER, + eReplicaComponentType::VENDOR, + eReplicaComponentType::DONATION_VENDOR, + eReplicaComponentType::ACHIEVEMENT_VENDOR, + eReplicaComponentType::SHOOTING_GALLERY, + eReplicaComponentType::ROLLER, + eReplicaComponentType::SCRIPTED_ACTIVITY, + eReplicaComponentType::COLLECTIBLE, + eReplicaComponentType::MOVING_PLATFORM, + eReplicaComponentType::CHANGLING_BUILD, + eReplicaComponentType::CHOICE_BUILD, + eReplicaComponentType::PACKAGE, + eReplicaComponentType::PLATFORM_BOUNDARY, + eReplicaComponentType::HF_LIGHT_DIRECTION_GADGET, + eReplicaComponentType::CULLING_PLANE, + eReplicaComponentType::EXHIBIT, + eReplicaComponentType::LUP_EXHIBIT, + eReplicaComponentType::PROPERTY, + eReplicaComponentType::PROPERTY_PLAQUE, + eReplicaComponentType::MODEL_BEHAVIOR, + eReplicaComponentType::PROPERTY_ENTRANCE, + eReplicaComponentType::MULTI_ZONE_ENTRANCE, + eReplicaComponentType::PROPERTY_MANAGEMENT, + eReplicaComponentType::RAIL_ACTIVATOR, + eReplicaComponentType::BOUNCER, + eReplicaComponentType::SPRINGPAD, + eReplicaComponentType::SWITCH, + eReplicaComponentType::MINIGAME_CONTROL, + eReplicaComponentType::MODEL_BUILDER, + eReplicaComponentType::SOUND_REPEATER, + eReplicaComponentType::CHEST, + eReplicaComponentType::SHOWCASE_MODEL_HANDLER, + eReplicaComponentType::JETPACKPAD, + eReplicaComponentType::RENDER, + eReplicaComponentType::SOUND_AMBIENT_2D, + eReplicaComponentType::SOUND_AMBIENT_3D, + eReplicaComponentType::SOUND_TRIGGER, + eReplicaComponentType::RACING_SOUND_TRIGGER, + eReplicaComponentType::CUSTOM_BUILD_ASSEMBLY, + eReplicaComponentType::GENERIC_ACTIVATOR, + eReplicaComponentType::PROPERTY_VENDOR, + eReplicaComponentType::ROCKET_LAUNCHPAD_CONTROL, + eReplicaComponentType::ROCKET_ANIMATION_CONTROL, + eReplicaComponentType::FACTION_TRIGGER, + eReplicaComponentType::RACING_CONTROL, + eReplicaComponentType::GATE_RUSH_CONTROL, + eReplicaComponentType::BBB, + eReplicaComponentType::CRAFTING +}; + +Entity::Entity(const LWOOBJID& objectID, EntityInfo info, Entity* parentEntity) { + m_ObjectID = objectID; + m_TemplateID = info.lot; + m_ParentEntity = parentEntity; + m_Character = nullptr; + m_GMLevel = eGameMasterLevel::CIVILIAN; + m_NetworkID = 0; + m_Observers = 0; + m_OwnerOverride = LWOOBJID_EMPTY; + m_Groups.clear(); + m_Timers.clear(); + m_ChildEntities.clear(); + m_TargetsInPhantom.clear(); + m_DieCallbacks.clear(); + m_PhantomCollisionCallbacks.clear(); + m_ScheduleKiller = nullptr; + m_IsParentChildDirty = true; + m_IsGhostingCandidate = true; + m_PlayerIsReadyForUpdates = false; + m_ShouldDestroyAfterUpdate = false; + + m_Settings = info.settings; + m_NetworkSettings = info.networkSettings; + m_DefaultPosition = info.pos; + m_DefaultRotation = info.rot; + m_Scale = info.scale; + m_Spawner = info.spawner; + m_SpawnerID = info.spawnerID; + m_HasSpawnerNodeID = info.hasSpawnerNodeID; + m_SpawnerNodeID = info.spawnerNodeID; + + if (info.lot != 1) m_PlayerIsReadyForUpdates = true; +} + +Entity::~Entity() { + if (m_Character) m_Character->SaveXMLToDatabase(); + + CancelAllTimers(); + CancelCallbackTimers(); + + for (auto child : m_ChildEntities) if (child) child->RemoveParent(); + + if (m_ParentEntity) m_ParentEntity->RemoveChild(this); +} + +void Entity::ApplyComponentWhitelist(TemplateComponents& components) const { + const auto whitelistIndex = GetVar(u"componentWhitelist"); + if (whitelistIndex < 0 || whitelistIndex >= m_ComponentWhitelists.size()) return; + + const auto& whitelist = m_ComponentWhitelists.at(whitelistIndex); + const auto endRange = std::remove_if(components.begin(), components.end(), [&whitelist](const auto& componentCandidate) { + return std::find(whitelist.begin(), whitelist.end(), componentCandidate.first) == whitelist.end(); + }); + components.erase(endRange, components.end()); +} + +void Entity::ApplyComponentBlacklist(TemplateComponents& components) const { + bool hasPetComponent = std::find_if(components.begin(), components.end(), [](const auto& componentCandidate) { + return componentCandidate.first == eReplicaComponentType::PET; + }) != components.end(); + if (hasPetComponent) { + auto toRemove = std::remove_if(components.begin(), components.end(), [](const auto& componentCandidate) { + return componentCandidate.first == eReplicaComponentType::MODEL_BEHAVIOR || componentCandidate.first == eReplicaComponentType::ITEM; + }); + components.erase(toRemove, components.end()); + } +} + +void Entity::ApplyComponentConfig(TemplateComponents& components) const { + if (GetVar(u"markedAsPhantom")) { + auto toRemove = std::remove_if(components.begin(), components.end(), [](const auto& componentCandidate) { + return componentCandidate.first == eReplicaComponentType::SIMPLE_PHYSICS || + componentCandidate.first == eReplicaComponentType::PHANTOM_PHYSICS; // Just make sure we dont have phantom physics already + }); + components.erase(toRemove, components.end()); + components.emplace_back(eReplicaComponentType::PHANTOM_PHYSICS, 0U); + } +} + +void Entity::AddPathComponent(TemplateComponents& components) const { + const Path* path = dZoneManager::Instance()->GetZone()->GetPath(GetVarAsString(u"attached_path")); + //Check to see if we have an attached path and add the appropiate component to handle it: + if (path) { + // if we have a moving platform path, then we need a moving platform component + if (path->pathType == PathType::MovingPlatform) { + bool hasMovingPlatform = std::count_if(components.begin(), components.end(), [](const auto& componentCandidate) { + return componentCandidate.first == eReplicaComponentType::MOVING_PLATFORM; + }) > 0; + if (!hasMovingPlatform) components.emplace_back(eReplicaComponentType::MOVING_PLATFORM, 0U); + } else if (path->pathType == PathType::Movement) { + bool hasMovementAi = std::count_if(components.begin(), components.end(), [](const auto& componentCandidate) { + return componentCandidate.first == eReplicaComponentType::MOVEMENT_AI; + }) > 0; + if (!hasMovementAi) { + components.emplace_back(eReplicaComponentType::MOVEMENT_AI, 0U); + } + } else { + Game::logger->Log("Entity", "Unsupported path type %i provided for lot %i.", path->pathType, GetLOT()); + } + } +} + +void Entity::Initialize() { + bool hasPhysicsComponent = false; + bool hasDestroyableComponent = false; + bool hasPathFindingComponent = false; + bool hasProximityMonitorComponent = false; + bool hasScriptComponent = false; + bool hasDroppedLootComponent = false; + uint32_t physicsComponentID = -1; + + // A few edge cases to tackle first + const auto triggerInfo = GetVarAsString(u"trigger_id"); + if (!triggerInfo.empty()) AddComponent(triggerInfo); + + const auto groupIDs = GetVarAsString(u"groupID"); + if (!groupIDs.empty()) { + m_Groups = GeneralUtils::SplitString(groupIDs, ';'); + if (m_Groups.back().empty()) m_Groups.erase(m_Groups.end() - 1); + } + + if (m_ParentEntity) m_ParentEntity->AddChild(this); + + auto* componentsRegistry = CDClientManager::Instance().GetTable(); + TemplateComponents components = componentsRegistry->GetTemplateComponents(m_TemplateID); + ApplyComponentWhitelist(components); + ApplyComponentBlacklist(components); + AddPathComponent(components); + + for (const auto& [componentTemplate, componentId] : components) { + switch (componentTemplate) { + case eReplicaComponentType::CONTROLLABLE_PHYSICS: + if (hasPhysicsComponent) continue; + AddComponent(); + physicsComponentID = componentId; + hasPhysicsComponent = true; + break; + case eReplicaComponentType::RENDER: + AddComponent(); + break; + case eReplicaComponentType::SIMPLE_PHYSICS: + if (hasPhysicsComponent) continue; + AddComponent(componentId); + physicsComponentID = componentId; + hasPhysicsComponent = true; + break; + case eReplicaComponentType::CHARACTER: + AddComponent(m_Character); + AddComponent(); + AddComponent(); + AddComponent(); + AddComponent(); + break; + case eReplicaComponentType::SCRIPT: { + std::string script; + if (m_TemplateID == LOT_ZONE_CONTROL) { + script = ScriptComponent::GetZoneScriptName(componentId); + } else { + script = ScriptComponent::GetScriptName(this, componentId); + } + AddComponent(script); // Technically this should check for if the script name is empty and not create a component if it is. + hasScriptComponent = true; + break; + } + case eReplicaComponentType::BOUNCER: + AddComponent(); + break; + case eReplicaComponentType::DESTROYABLE: + if (hasDestroyableComponent) continue; + AddComponent(componentId); + hasDestroyableComponent = true; + break; + case eReplicaComponentType::SKILL: + AddComponent(); + break; + case eReplicaComponentType::ITEM: + AddComponent(); + break; + case eReplicaComponentType::VENDOR: + AddComponent(); + if (hasProximityMonitorComponent) continue; + AddComponent(); + hasProximityMonitorComponent = true; + break; + case eReplicaComponentType::INVENTORY: + AddComponent(); + break; + case eReplicaComponentType::SHOOTING_GALLERY: + AddComponent(componentId); + break; + case eReplicaComponentType::RIGID_BODY_PHANTOM_PHYSICS: + if (hasPhysicsComponent) continue; + AddComponent(); + physicsComponentID = componentId; + hasPhysicsComponent = true; + break; + case eReplicaComponentType::COLLECTIBLE: + AddComponent(); + if (!hasDestroyableComponent) AddComponent(componentId); + break; + case eReplicaComponentType::MOVING_PLATFORM: + AddComponent(GetVarAsString(u"attached_path")); + m_IsGhostingCandidate = false; + break; + case eReplicaComponentType::PET: + AddComponent(componentId); + AddComponent(); + break; + case eReplicaComponentType::HAVOK_VEHICLE_PHYSICS: { + if (hasPhysicsComponent) continue; + if (GetVar(u"use_simple_physics")) { + AddComponent(componentId); + } else { + auto* havokVehiclePhysicsComponent = AddComponent(); + if (havokVehiclePhysicsComponent) { + havokVehiclePhysicsComponent->SetPosition(m_DefaultPosition); + havokVehiclePhysicsComponent->SetRotation(m_DefaultRotation); + } + } + physicsComponentID = componentId; + m_IsGhostingCandidate = false; + hasPhysicsComponent = true; + break; + } + case eReplicaComponentType::MOVEMENT_AI: + AddComponent(); + break; + case eReplicaComponentType::PROPERTY: + AddComponent(); + m_IsGhostingCandidate = false; + break; + case eReplicaComponentType::SCRIPTED_ACTIVITY: + AddComponent(componentId); + m_IsGhostingCandidate = false; + break; + case eReplicaComponentType::PHANTOM_PHYSICS: { + if (hasPhysicsComponent) continue; + auto* phantomPhysicsComponent = AddComponent(); + if (phantomPhysicsComponent) phantomPhysicsComponent->SetPhysicsEffectActive(false); + physicsComponentID = componentId; + hasPhysicsComponent = true; + m_IsGhostingCandidate = false; + break; + } + case eReplicaComponentType::MODEL_BEHAVIOR: { + AddComponent(); + // Get Model Type form ldf + if (!hasPhysicsComponent) { + uint32_t modelType = -1; + if (modelType == 0) { + if (physicsComponentID == -1) physicsComponentID = 4246U; + AddComponent(); + hasPhysicsComponent = true; + } else { + if (physicsComponentID == -1) physicsComponentID = 4247U; + AddComponent(physicsComponentID); + hasPhysicsComponent = true; + } + } + if (hasDestroyableComponent) continue; + auto* destroyableComponent = AddComponent(componentId); + if (!destroyableComponent) continue; + destroyableComponent->SetHealth(1); + destroyableComponent->SetMaxHealth(1.0f); + destroyableComponent->SetFaction(-1, true); + destroyableComponent->SetIsSmashable(true); + hasDestroyableComponent = true; + break; + } + case eReplicaComponentType::PROPERTY_ENTRANCE: + AddComponent(componentId); + break; + case eReplicaComponentType::PROPERTY_MANAGEMENT: + AddComponent(); + break; + case eReplicaComponentType::QUICK_BUILD: + AddComponent(componentId); + if (hasDestroyableComponent) continue; + AddComponent(componentId); + hasDestroyableComponent = true; + break; + case eReplicaComponentType::SWITCH: + AddComponent(); + break; + case eReplicaComponentType::MINIGAME_CONTROL: + AddComponent(componentId); + break; + case eReplicaComponentType::BASE_COMBAT_AI: { + auto* baseCombatAiComponent = AddComponent(componentId); + if (baseCombatAiComponent && baseCombatAiComponent->GetTetherSpeed() > 0.0f) { + auto* movementAiComponent = AddComponent(); + if (!movementAiComponent) break; + MovementAIInfo movementAiInfo{}; + movementAiInfo.movementType = ""; + movementAiInfo.wanderChance = 0; + movementAiInfo.wanderRadius = 16; + movementAiInfo.wanderSpeed = 2.5f; + movementAiInfo.wanderDelayMax = 5; + movementAiInfo.wanderDelayMin = 2; + movementAiComponent->SetMoveInfo(movementAiInfo); + } + break; + } + case eReplicaComponentType::MODULE_ASSEMBLY: + AddComponent(); + break; + case eReplicaComponentType::PROPERTY_VENDOR: + AddComponent(); + break; + case eReplicaComponentType::ROCKET_LAUNCHPAD_CONTROL: + AddComponent(componentId); + break; + case eReplicaComponentType::RACING_CONTROL: + AddComponent(componentId); + m_IsGhostingCandidate = false; + break; + case eReplicaComponentType::MISSION_OFFER: + AddComponent(componentId); + break; + case eReplicaComponentType::RACING_STATS: + AddComponent(); + break; + case eReplicaComponentType::LUP_EXHIBIT: + AddComponent(); + break; + case eReplicaComponentType::SOUND_TRIGGER: + AddComponent(); + break; + case eReplicaComponentType::PROXIMITY_MONITOR: + AddComponent(); + break; + case eReplicaComponentType::MULTI_ZONE_ENTRANCE: + AddComponent(); + break; + case eReplicaComponentType::BUFF: + AddComponent(); + break; + case eReplicaComponentType::RAIL_ACTIVATOR: + AddComponent(componentId); + break; + case eReplicaComponentType::POSSESSABLE: + AddComponent(componentId); + break; + case eReplicaComponentType::BUILD_BORDER: + AddComponent(); + break; + case eReplicaComponentType::DONATION_VENDOR: + AddComponent(); + if (hasProximityMonitorComponent) continue; + AddComponent(); + hasProximityMonitorComponent = true; + break; + case eReplicaComponentType::GATE_RUSH_CONTROL: + AddComponent(componentId); + break; + case eReplicaComponentType::RACING_SOUND_TRIGGER: + AddComponent(); + break; + case eReplicaComponentType::ACHIEVEMENT_VENDOR: + AddComponent(); + break; + case eReplicaComponentType::PROJECTILE_PHYSICS: + if (hasPhysicsComponent) continue; + // AddComponent(); + physicsComponentID = componentId; + hasPhysicsComponent = true; + break; + case eReplicaComponentType::VEHICLE_PHYSICS: + if (hasPhysicsComponent) continue; + // AddComponent(); + physicsComponentID = componentId; + hasPhysicsComponent = true; + break; + case eReplicaComponentType::PHYSICS_SYSTEM: + // AddComponent(); + physicsComponentID = componentId; + hasPhysicsComponent = true; + break; + case eReplicaComponentType::GHOST: + case eReplicaComponentType::SPAWN: + case eReplicaComponentType::MODULAR_BUILD: + case eReplicaComponentType::BUILD_CONTROLLER: + case eReplicaComponentType::BUILD_ACTIVATOR: + case eReplicaComponentType::ICON_ONLY: + case eReplicaComponentType::DROP_EFFECT: + case eReplicaComponentType::CHEST: + case eReplicaComponentType::BLUEPRINT: + case eReplicaComponentType::PLATFORM_BOUNDARY: + case eReplicaComponentType::MODULE: + case eReplicaComponentType::JETPACKPAD: + case eReplicaComponentType::EXHIBIT: + case eReplicaComponentType::OVERHEAD_ICON: + case eReplicaComponentType::PET_CONTROL: + case eReplicaComponentType::MINIFIG: + case eReplicaComponentType::PET_CREATOR: + case eReplicaComponentType::MODEL_BUILDER: + case eReplicaComponentType::SPRINGPAD: + case eReplicaComponentType::FX: + case eReplicaComponentType::CHANGLING_BUILD: + case eReplicaComponentType::CHOICE_BUILD: + case eReplicaComponentType::PACKAGE: + case eReplicaComponentType::SOUND_REPEATER: + case eReplicaComponentType::SOUND_AMBIENT_2D: + case eReplicaComponentType::SOUND_AMBIENT_3D: + case eReplicaComponentType::PRECONDITION: + case eReplicaComponentType::FLAG: + case eReplicaComponentType::CUSTOM_BUILD_ASSEMBLY: + case eReplicaComponentType::SHOWCASE_MODEL_HANDLER: + case eReplicaComponentType::RACING_MODULE: + case eReplicaComponentType::GENERIC_ACTIVATOR: + case eReplicaComponentType::HF_LIGHT_DIRECTION_GADGET: + case eReplicaComponentType::ROCKET_ANIMATION_CONTROL: + case eReplicaComponentType::TRIGGER: + case eReplicaComponentType::DROPPED_LOOT: + case eReplicaComponentType::FACTION_TRIGGER: + case eReplicaComponentType::BBB: + case eReplicaComponentType::CHAT_BUBBLE: + case eReplicaComponentType::FRIENDS_LIST: + case eReplicaComponentType::GUILD: + case eReplicaComponentType::LOCAL_SYSTEM: + case eReplicaComponentType::MISSION: + case eReplicaComponentType::MUTABLE_MODEL_BEHAVIORS: + case eReplicaComponentType::PATHFINDING: + case eReplicaComponentType::PET_TAMING_CONTROL: + case eReplicaComponentType::PROPERTY_EDITOR: + case eReplicaComponentType::SKINNED_RENDER: + case eReplicaComponentType::SLASH_COMMAND: + case eReplicaComponentType::STATUS_EFFECT: + case eReplicaComponentType::TEAMS: + case eReplicaComponentType::TEXT_EFFECT: + case eReplicaComponentType::TRADE: + case eReplicaComponentType::USER_CONTROL: + case eReplicaComponentType::IGNORE_LIST: + case eReplicaComponentType::INTERACTION_MANAGER: + case eReplicaComponentType::COMBAT_MEDIATOR: + case eReplicaComponentType::ROLLER: + case eReplicaComponentType::PLAYER_FORCED_MOVEMENT: + case eReplicaComponentType::CRAFTING: + case eReplicaComponentType::LEVEL_PROGRESSION: + case eReplicaComponentType::POSSESSION: + case eReplicaComponentType::MOUNT_CONTROL: + case eReplicaComponentType::UNKNOWN_112: + case eReplicaComponentType::PROPERTY_PLAQUE: + case eReplicaComponentType::UNKNOWN_115: + case eReplicaComponentType::CULLING_PLANE: + case eReplicaComponentType::NUMBER_OF_COMPONENTS: + case eReplicaComponentType::INVALID: + default: + Game::logger->Log("Entity", "Attempted to create component %i for lot %i but no creator exists. Component will not be created.", componentId, m_TemplateID); + } + } + + AddCallbackTimer(0.0f, [this]() { + GetScript()->OnStartup(this); + }); + + // Load data specific to this LOT first. These act as defaults for the components. + std::for_each(m_Components.begin(), m_Components.end(), [this](auto& component) { + component.second->LoadTemplateData(); + }); + + // Then load data specific to this Entity. This will vary on an Entity to Entity basis. + // If there is data you want to override the LOT default value, generally you would attach it via LDF + // and it would get loaded and overridden here. + std::for_each(m_Components.begin(), m_Components.end(), [this](auto& component) { + component.second->LoadConfigData(); + }); + + /** + * Startup all the components. Some components need or want data from other components so + * we want to ensure that + * A) Most if not all components are newed and ready to be accessed. + * B) All components have their personal data loaded and ready to be used. + */ + std::for_each(m_Components.begin(), m_Components.end(), [this](auto& component) { + component.second->Startup(); + }); + + /** + * Load the player save data from XML. Ideally we do this after all initialization so the player + * save data overrides any defaults that may be applied. + */ + if (!IsPlayer()) std::for_each(m_Components.begin(), m_Components.end(), [this](auto& component) { + component.second->LoadFromXml(m_Character->GetXMLDoc()); + }); + + TriggerEvent(eTriggerEventType::CREATE, this); + if (!m_Character && EntityManager::Instance()->GetGhostingEnabled()) IsGhosted(); +} + +void Entity::IsGhosted() { + // Don't ghost what is likely large scene elements + if (m_Components.size() == 2 && HasComponent(eReplicaComponentType::SIMPLE_PHYSICS) && HasComponent(eReplicaComponentType::RENDER)) { + m_IsGhostingCandidate = false; + } else if (m_Components.size() == 3 && HasComponent(eReplicaComponentType::SIMPLE_PHYSICS) && HasComponent(eReplicaComponentType::RENDER) && HasComponent(eReplicaComponentType::TRIGGER)) { + m_IsGhostingCandidate = false; + } else if (EntityManager::IsExcludedFromGhosting(GetLOT())) { + m_IsGhostingCandidate = false; + } +} + +bool Entity::operator==(const Entity& other) const { + return other.m_ObjectID == m_ObjectID; +} + +bool Entity::operator!=(const Entity& other) const { + return !(other.m_ObjectID == m_ObjectID); +} + +bool Entity::HasComponent(const eReplicaComponentType componentId) const { + return m_Components.find(componentId) != m_Components.end(); +} + +// Fine +void Entity::Subscribe(CppScripts::Script* scriptToAdd, const std::string& notificationName) { + Game::logger->Log("Entity", "Subscribing a script with notification %s", notificationName.c_str()); + if (notificationName == "HitOrHealResult" || notificationName == "Hit") { + auto* destroyableComponent = GetComponent(); + if (destroyableComponent) destroyableComponent->Subscribe(scriptToAdd); + } +} + +// Fine +void Entity::Unsubscribe(CppScripts::Script* scriptToRemove, const std::string& notificationName) { + Game::logger->Log("Entity", "Unsubscribing a script with notification %s", notificationName.c_str()); + if (notificationName == "HitOrHealResult" || notificationName == "Hit") { + auto* destroyableComponent = GetComponent(); + if (destroyableComponent) destroyableComponent->Unsubscribe(scriptToRemove); + } +} + +// Fine +void Entity::AddProximityRadius(const float proxRadius, const std::string& name) { + auto* proximityMonitorComponent = AddComponent(); + if (proximityMonitorComponent) proximityMonitorComponent->AddProximityRadius(proxRadius, name); +} + +// Remove in favor of a square constructor +void Entity::AddProximityRadius(const BoxDimensions& dimensions, const std::string& name) { + auto* proximityMonitorComponent = AddComponent(); + if (proximityMonitorComponent) proximityMonitorComponent->AddProximityRadius(dimensions, name); +} + +void Entity::SetGMLevel(eGameMasterLevel value) { + m_GMLevel = value; + // User m_Character? + if (GetParentUser()) { + Character* character = GetParentUser()->GetLastUsedChar(); + + if (character) { + character->SetGMLevel(value); + } + } + + auto* character = GetComponent(); + if (character) character->SetGMLevel(value); + + GameMessages::SendGMLevelBroadcast(m_ObjectID, value); +} + +void Entity::WriteBaseReplicaData(RakNet::BitStream* outBitStream, const eReplicaPacketType packetType) { + if (packetType == eReplicaPacketType::CONSTRUCTION) { + outBitStream->Write(m_ObjectID); + outBitStream->Write(m_TemplateID); + + if (IsPlayer()) { + std::string name = m_Character != nullptr ? m_Character->GetName() : "Invalid"; + outBitStream->Write(uint8_t(name.size())); + + for (size_t i = 0; i < name.size(); ++i) { + outBitStream->Write(name[i]); + } + } else { + const auto& name = GetVar(u"npcName"); + outBitStream->Write(uint8_t(name.size())); + + for (size_t i = 0; i < name.size(); ++i) { + outBitStream->Write(name[i]); + } + } + + outBitStream->Write(0); //Time since created on server + + const auto& syncLDF = GetVar>(u"syncLDF"); + + // Only sync for models. + // PetComponent check un-needed since we should be removing the component during construction. + if (m_Settings.size() > 0 && (GetComponent() && !GetComponent())) { + outBitStream->Write1(); //ldf data + + RakNet::BitStream settingStream; + settingStream.Write(m_Settings.size()); + + for (LDFBaseData* data : m_Settings) { + if (data) { + data->WriteToPacket(&settingStream); + } + } + + outBitStream->Write(settingStream.GetNumberOfBytesUsed() + 1); + outBitStream->Write(0); //no compression used + outBitStream->Write(settingStream); + } else if (!syncLDF.empty()) { + std::vector ldfData; + + for (const auto& data : syncLDF) { + ldfData.push_back(GetVarData(data)); + } + + outBitStream->Write1(); //ldf data + + RakNet::BitStream settingStream; + settingStream.Write(ldfData.size()); + + for (LDFBaseData* data : ldfData) { + if (data) { + data->WriteToPacket(&settingStream); + } + } + + outBitStream->Write(settingStream.GetNumberOfBytesUsed() + 1); + outBitStream->Write(0); //no compression used + outBitStream->Write(settingStream); + } else { + outBitStream->Write0(); //No ldf data + } + + auto* triggerComponent = GetComponent(); + if (triggerComponent) { + // has trigger component, check to see if we have events to handle + auto* trigger = triggerComponent->GetTrigger(); + outBitStream->Write(trigger && trigger->events.size() > 0); + } else { // no trigger componenet, so definitely no triggers + outBitStream->Write0(); + } + + outBitStream->Write(m_ParentEntity != nullptr || m_SpawnerID != 0); + if (m_ParentEntity != nullptr || m_SpawnerID != 0) { + if (m_ParentEntity != nullptr) outBitStream->Write(GeneralUtils::SetBit(m_ParentEntity->GetObjectID(), static_cast(eObjectBits::CLIENT))); + else if (m_Spawner != nullptr && m_Spawner->m_Info.isNetwork) outBitStream->Write(m_SpawnerID); + else outBitStream->Write(GeneralUtils::SetBit(m_SpawnerID, static_cast(eObjectBits::CLIENT))); + } + + outBitStream->Write(m_HasSpawnerNodeID); + if (m_HasSpawnerNodeID) outBitStream->Write(m_SpawnerNodeID); + + //outBitStream->Write0(); //Spawner node id + + outBitStream->Write(m_Scale != 1.0f || m_Scale != 0.0f); + if (m_Scale != 1.0f || m_Scale != 0.0f) outBitStream->Write(m_Scale); + + outBitStream->Write0(); //ObjectWorldState + + outBitStream->Write(m_GMLevel != eGameMasterLevel::CIVILIAN); + if (m_GMLevel != eGameMasterLevel::CIVILIAN) { + outBitStream->Write(m_GMLevel); + } + } + + // Only serialize parent / child info should the info be dirty (changed) or if this is the construction of the entity. + outBitStream->Write(m_IsParentChildDirty || packetType == eReplicaPacketType::CONSTRUCTION); + if (m_IsParentChildDirty || packetType == eReplicaPacketType::CONSTRUCTION) { + m_IsParentChildDirty = false; + outBitStream->Write(m_ParentEntity != nullptr); + if (m_ParentEntity) { + outBitStream->Write(m_ParentEntity->GetObjectID()); + outBitStream->Write0(); + } + outBitStream->Write(m_ChildEntities.size() > 0); + if (m_ChildEntities.size() > 0) { + outBitStream->Write(m_ChildEntities.size()); + for (Entity* child : m_ChildEntities) { + outBitStream->Write(child->GetObjectID()); + } + } + } +} + +// uh +void Entity::WriteComponents(RakNet::BitStream* outBitStream, const eReplicaPacketType packetType) { + +} + +// We should be able to use this at some point +void Entity::ResetFlags() { + // Unused +} + +void Entity::UpdateXMLDoc(tinyxml2::XMLDocument* doc) { + DluAssert(doc != nullptr); + for (const auto& [componentId, component] : m_Components) { + if (component) component->UpdateXml(doc); + } +} + +CppScripts::Script* Entity::GetScript() const { + auto* scriptComponent = GetComponent(); + if (!scriptComponent) return CppScripts::invalidScript.get(); + auto* script = scriptComponent->GetScript(); + DluAssert(script != nullptr); + return script; +} + +void Entity::Update(const float deltaTime) { + auto namedTimerItr = std::remove_if(m_Timers.begin(), m_Timers.end(), [this, &deltaTime](const std::unique_ptr& timer) { + timer->Update(deltaTime); + if (timer->GetTime() <= 0) { + GetScript()->OnTimerDone(this, timer->GetName()); + TriggerEvent(eTriggerEventType::TIMER_DONE, this); + return true; + } + return false; + }); + m_Timers.erase(namedTimerItr, m_Timers.end()); + + auto callbackTimerItr = std::remove_if(m_CallbackTimers.begin(), m_CallbackTimers.end(), [this, &deltaTime](const std::unique_ptr& timer) { + timer->Update(deltaTime); + if (timer->GetTime() <= 0) { + timer->ExecuteCallback(); + return true; + } + return false; + }); + m_CallbackTimers.erase(callbackTimerItr, m_CallbackTimers.end()); + + // Add pending timers to the list of timers so they start next tick. + if (!m_PendingTimers.empty()) { + this->m_Timers.reserve(m_Timers.size() + m_PendingTimers.size()); + for (auto& timer : m_PendingTimers) { + this->m_Timers.push_back(std::move(timer)); + } + m_PendingTimers.clear(); + } + + if (IsSleeping()) { + Sleep(); + + return; + } + Wake(); + + GetScript()->OnUpdate(this); + + for (const auto& [componentId, component] : m_Components) { + if (component) component->Update(deltaTime); + } + + if (m_ShouldDestroyAfterUpdate) EntityManager::Instance()->DestroyEntity(this); +} + +void Entity::OnCollisionProximity(LWOOBJID otherEntity, const std::string& proxName, const std::string& status) { + auto* other = EntityManager::Instance()->GetEntity(otherEntity); + if (!other) return; + + GetScript()->OnProximityUpdate(this, other, proxName, status); + + auto* rocketLaunchpadControlComponent = GetComponent(); + if (!rocketLaunchpadControlComponent) return; + + rocketLaunchpadControlComponent->OnProximityUpdate(other, proxName, status); +} + +void Entity::OnCollisionPhantom(const LWOOBJID otherEntity) { + auto* other = EntityManager::Instance()->GetEntity(otherEntity); + if (!other) return; + + GetScript()->OnCollisionPhantom(this, other); + + std::for_each(m_PhantomCollisionCallbacks.begin(), m_PhantomCollisionCallbacks.end(), [other](const auto& callback) { + callback(other); + }); + + auto* switchComponent = GetComponent(); + if (switchComponent) switchComponent->EntityEnter(other); + + TriggerEvent(eTriggerEventType::ENTER, other); + + // POI system + const auto& poi = GetVar(u"POI"); + + if (!poi.empty()) { + auto* missionComponent = other->GetComponent(); + + if (missionComponent != nullptr) { + missionComponent->Progress(eMissionTaskType::EXPLORE, 0, 0, GeneralUtils::UTF16ToWTF8(poi)); + } + } + + if (!other->IsDead()) { + auto* combat = GetComponent(); + + if (combat != nullptr) { + const auto index = std::find(m_TargetsInPhantom.begin(), m_TargetsInPhantom.end(), otherEntity); + + if (index != m_TargetsInPhantom.end()) return; + + const auto valid = combat->IsEnemy(otherEntity); + + if (!valid) return; + + m_TargetsInPhantom.push_back(otherEntity); + } + } +} + +void Entity::OnCollisionLeavePhantom(const LWOOBJID otherEntity) { + auto* other = EntityManager::Instance()->GetEntity(otherEntity); + if (!other) return; + + GetScript()->OnOffCollisionPhantom(this, other); + + TriggerEvent(eTriggerEventType::EXIT, other); + + const auto index = std::find(m_TargetsInPhantom.begin(), m_TargetsInPhantom.end(), otherEntity); + + if (index == m_TargetsInPhantom.end()) return; + + m_TargetsInPhantom.erase(index); +} + +void Entity::OnFireEventServerSide(Entity* sender, std::string args, int32_t param1, int32_t param2, int32_t param3) { + GetScript()->OnFireEventServerSide(this, sender, args, param1, param2, param3); +} + +void Entity::OnActivityStateChangeRequest(LWOOBJID senderID, int32_t value1, int32_t value2, const std::u16string& stringValue) { + GetScript()->OnActivityStateChangeRequest(this, senderID, value1, value2, stringValue); +} + +void Entity::OnCinematicUpdate(Entity* self, Entity* sender, eCinematicEvent event, const std::u16string& pathName, + float_t pathTime, float_t totalTime, int32_t waypoint) { + GetScript()->OnCinematicUpdate(self, sender, event, pathName, pathTime, totalTime, waypoint); +} + +void Entity::CancelCallbackTimers() { + m_CallbackTimers.clear(); +} + +void Entity::NotifyObject(Entity* sender, const std::u16string& name, int32_t param1, int32_t param2) { + GameMessages::SendNotifyObject(GetObjectID(), sender->GetObjectID(), name, UNASSIGNED_SYSTEM_ADDRESS, param1, param2); + + GetScript()->OnNotifyObject(this, sender, name, param1, param2); +} + +void Entity::OnEmoteReceived(const int32_t emote, Entity* target) { + GetScript()->OnEmoteReceived(this, emote, target); +} + +void Entity::OnUse(Entity* originator) { + TriggerEvent(eTriggerEventType::INTERACT, originator); + + GetScript()->OnUse(this, originator); + + for (const auto& [componentId, component] : m_Components) { + if (component) component->OnUse(originator); + } +} + +void Entity::OnHitOrHealResult(Entity* attacker, int32_t damage) { + GetScript()->OnHitOrHealResult(this, attacker, damage); +} + +void Entity::OnHit(Entity* attacker) { + TriggerEvent(eTriggerEventType::HIT, attacker); + GetScript()->OnHit(this, attacker); +} + +void Entity::OnZonePropertyEditBegin() { + GetScript()->OnZonePropertyEditBegin(this); +} + +void Entity::OnZonePropertyEditEnd() { + GetScript()->OnZonePropertyEditEnd(this); +} + +void Entity::OnZonePropertyModelEquipped() { + GetScript()->OnZonePropertyModelEquipped(this); +} + +void Entity::OnZonePropertyModelPlaced(Entity* player) { + GetScript()->OnZonePropertyModelPlaced(this, player); +} + +void Entity::OnZonePropertyModelPickedUp(Entity* player) { + GetScript()->OnZonePropertyModelPickedUp(this, player); +} + +void Entity::OnZonePropertyModelRemoved(Entity* player) { + GetScript()->OnZonePropertyModelRemoved(this, player); +} + +void Entity::OnZonePropertyModelRemovedWhileEquipped(Entity* player) { + GetScript()->OnZonePropertyModelRemovedWhileEquipped(this, player); +} + +void Entity::OnZonePropertyModelRotated(Entity* player) { + GetScript()->OnZonePropertyModelRotated(this, player); +} + +void Entity::OnMessageBoxResponse(Entity* sender, int32_t button, const std::u16string& identifier, const std::u16string& userData) { + GetScript()->OnMessageBoxResponse(this, sender, button, identifier, userData); +} + +void Entity::OnChoiceBoxResponse(Entity* sender, int32_t button, const std::u16string& buttonIdentifier, const std::u16string& identifier) { + GetScript()->OnChoiceBoxResponse(this, sender, button, buttonIdentifier, identifier); +} + +void Entity::RequestActivityExit(Entity* sender, const LWOOBJID& player, const bool canceled) { + GetScript()->OnRequestActivityExit(sender, player, canceled); +} + +void Entity::Smash(const LWOOBJID source, const eKillType killType, const std::u16string& deathType) { + if (!m_PlayerIsReadyForUpdates) return; + + auto* destroyableComponent = GetComponent(); + if (!destroyableComponent) { + Kill(EntityManager::Instance()->GetEntity(source)); + return; + } + auto* possessionComponent = GetComponent(); + if (possessionComponent) { + if (possessionComponent->GetPossessable() != LWOOBJID_EMPTY) { + auto* mount = EntityManager::Instance()->GetEntity(possessionComponent->GetPossessable()); + if (mount) possessionComponent->Dismount(mount, true); + } + } + + destroyableComponent->Smash(source, killType, deathType); +} + +void Entity::Kill(Entity* murderer) { + if (!m_PlayerIsReadyForUpdates) return; + + for (const auto& cb : m_DieCallbacks) cb(); + + m_DieCallbacks.clear(); + + //OMAI WA MOU, SHINDERIU + + GetScript()->OnDie(this, murderer); + + if (m_Spawner) m_Spawner->NotifyOfEntityDeath(m_ObjectID); + + if (!IsPlayer()) EntityManager::Instance()->DestroyEntity(this); + + const auto& grpNameQBShowBricks = GetVar(u"grpNameQBShowBricks"); + + if (!grpNameQBShowBricks.empty()) { + auto spawners = dZoneManager::Instance()->GetSpawnersByName(grpNameQBShowBricks); + + Spawner* spawner = nullptr; + + if (!spawners.empty()) { + spawner = spawners.front(); + } else { + spawners = dZoneManager::Instance()->GetSpawnersInGroup(grpNameQBShowBricks); + + if (!spawners.empty()) spawner = spawners.front(); + } + + if (spawner) spawner->Spawn(); + } + + // Track a player being smashed + auto* characterComponent = GetComponent(); + if (characterComponent) { + characterComponent->UpdatePlayerStatistic(TimesSmashed); + } + + // Track a player smashing something else + if (!murderer) return; + auto* murdererCharacterComponent = murderer->GetComponent(); + if (murdererCharacterComponent) { + murdererCharacterComponent->UpdatePlayerStatistic(SmashablesSmashed); + } +} + +void Entity::AddRebuildCompleteCallback(const std::function& callback) const { + auto* quickBuildComponent = GetComponent(); + if (quickBuildComponent) quickBuildComponent->AddRebuildCompleteCallback(callback); +} + +bool Entity::IsDead() const { + auto* dest = GetComponent(); + return dest && dest->GetArmor() == 0 && dest->GetHealth() == 0; +} + +void Entity::AddLootItem(const Loot::Info& info) { + if (!IsPlayer()) return; + auto* player = dynamic_cast(this); + if (player) player->GetDroppedLoot().insert(std::make_pair(info.id, info)); // Move this to Player +} + +// Replace static_cast with dynamic_cast +void Entity::PickupItem(const LWOOBJID& objectID) { + if (!IsPlayer()) return; + auto* inventoryComponent = GetComponent(); + if (!inventoryComponent) return; + + auto* objectsTable = CDClientManager::Instance().GetTable(); + auto* skillsTable = CDClientManager::Instance().GetTable(); + + auto* player = dynamic_cast(this); + if (!player) return; + auto& droppedLoot = player->GetDroppedLoot(); + + auto lootIterator = droppedLoot.find(objectID); + if (lootIterator == droppedLoot.end()) return; + const auto& [objId, loot] = *lootIterator; + + auto* characterComponent = GetComponent(); + if (characterComponent) characterComponent->TrackLOTCollection(loot.lot); + + const CDObjects& object = objectsTable->GetByID(loot.lot); + if (object.id != 0 && object.type == "Powerup") { + const auto lootLot = loot.lot; + auto skills = skillsTable->Query([lootLot](CDObjectSkills entry) { return (entry.objectTemplate == lootLot); }); + auto* skillBehaviorTable = CDClientManager::Instance().GetTable(); + for (const auto& skill : skills) { + auto behaviorData = skillBehaviorTable->GetSkillByID(skill.skillID); + // This should take a skillID, not a behaviorID. + SkillComponent::HandleUnmanaged(behaviorData.behaviorID, GetObjectID()); + + auto* missionComponent = GetComponent(); + + if (missionComponent) missionComponent->Progress(eMissionTaskType::POWERUP, skill.skillID); + } + } else { + inventoryComponent->AddItem(loot.lot, loot.count, eLootSourceType::PICKUP, eInventoryType::INVALID, {}, LWOOBJID_EMPTY, true, false, LWOOBJID_EMPTY, eInventoryType::INVALID, 1); + } + droppedLoot.erase(lootIterator); +} + +// This functions name is misleading and should not modify the number of dropped coins. +// A separate function, PickupCoins should modify that. +// Replace static_cast with dynamic_cast +bool Entity::CanPickupCoins(const uint64_t& count) const { + if (!IsPlayer()) return false; + const auto* player = dynamic_cast(this); + return count <= player->GetDroppedCoins(); +} + +void Entity::PickupCoins(const uint64_t& count) { + if (!IsPlayer()) return; + auto* player = dynamic_cast(this); + if (!player) return; + const auto droppedCoins = player->GetDroppedCoins(); + if (count <= droppedCoins) player->SetDroppedCoins(droppedCoins - count); +} + +void Entity::RegisterCoinDrop(const uint64_t& coinsDropped) { + if (!IsPlayer()) return; + auto* player = dynamic_cast(this); + if (!player) return; + player->SetDroppedCoins(player->GetDroppedCoins() + coinsDropped); +} + +void Entity::AddChild(Entity* child) { + if (!child) return; + m_IsParentChildDirty = true; + if (std::find(m_ChildEntities.begin(), m_ChildEntities.end(), child) == m_ChildEntities.end()) m_ChildEntities.push_back(child); + else Game::logger->Log("Entity", "WARNING: Entity (objid:lot) %llu:%i already has (%llu:%i) as a child", GetObjectID(), GetLOT(), child->GetObjectID(), child->GetLOT()); +} + +void Entity::RemoveChild(Entity* child) { + if (!child) return; + uint32_t entityPosition = 0; + auto toRemove = std::remove(m_ChildEntities.begin(), m_ChildEntities.end(), child); + if (toRemove != m_ChildEntities.end()) { + m_ChildEntities.erase(toRemove, m_ChildEntities.end()); + m_IsParentChildDirty = true; + } +} + +void Entity::RemoveParent() { + if (m_ParentEntity) m_IsParentChildDirty = true; + else Game::logger->Log("Entity", "WARNING: Attempted to remove parent from (objid:lot) (%llu:%i) when no parent existed", GetObjectID(), GetLOT()); + this->m_ParentEntity = nullptr; +} + +void Entity::AddTimer(const std::string& name, float time) { + m_PendingTimers.emplace_back(std::make_unique(name, time)); +} + +void Entity::AddCallbackTimer(const float time, const std::function& callback) { + m_CallbackTimers.emplace_back(std::make_unique(time, callback)); +} + +bool Entity::HasTimer(const std::string& name) { + auto possibleTimer = std::find_if(m_Timers.begin(), m_Timers.end(), [name](const std::unique_ptr& timer) { + return timer->GetName() == name; + }); + return possibleTimer != m_Timers.end(); +} + +void Entity::ScheduleKillAfterUpdate(Entity* murderer) { + EntityManager::Instance()->ScheduleForKill(this); + if (murderer) m_ScheduleKiller = murderer; +} + +void Entity::CancelTimer(const std::string& name) { + auto toErase = std::remove_if(m_Timers.begin(), m_Timers.end(), [&name](const std::unique_ptr& timer) { + return timer->GetName() == name; + }); + m_Timers.erase(m_Timers.begin(), toErase); +} + +void Entity::CancelAllTimers() { + m_Timers.clear(); + CancelCallbackTimers(); +} + +bool Entity::IsPlayer() const { + return m_TemplateID == 1 && GetSystemAddress() != UNASSIGNED_SYSTEM_ADDRESS; +} + +void Entity::TriggerEvent(eTriggerEventType event, Entity* optionalTarget) { + auto* triggerComponent = GetComponent(); + if (triggerComponent) triggerComponent->TriggerEvent(event, optionalTarget); +} + +Entity* Entity::GetOwner() const { + if (m_OwnerOverride != LWOOBJID_EMPTY) { + auto* other = EntityManager::Instance()->GetEntity(m_OwnerOverride); + + if (other != nullptr) { + return other->GetOwner(); + } + } + + return const_cast(this); +} + +void Entity::SetObservers(int8_t value) { + if (value < 0) value = 0; + + m_Observers = value; +} + +void Entity::Sleep() { + auto* baseCombatAIComponent = GetComponent(); + if (baseCombatAIComponent) baseCombatAIComponent->Sleep(); +} + +void Entity::Wake() { + auto* baseCombatAIComponent = GetComponent(); + if (baseCombatAIComponent) baseCombatAIComponent->Wake(); +} + +bool Entity::IsSleeping() const { + return m_IsGhostingCandidate && m_Observers == 0; +} + +// The following 3 should share a base component class so we can else if it. +const NiPoint3& Entity::GetPosition() const { + auto* controllablePhysicsComponent = GetComponent(); + + if (controllablePhysicsComponent != nullptr) { + return controllablePhysicsComponent->GetPosition(); + } + + auto* phantomPhysicsComponent = GetComponent(); + + if (phantomPhysicsComponent != nullptr) { + return phantomPhysicsComponent->GetPosition(); + } + + auto* simplePhysicsComponent = GetComponent(); + + if (simplePhysicsComponent != nullptr) { + return simplePhysicsComponent->GetPosition(); + } + + auto* vehiclePhysicsComponent = GetComponent(); + + if (vehiclePhysicsComponent != nullptr) { + return vehiclePhysicsComponent->GetPosition(); + } + + return NiPoint3::ZERO; +} + +const NiQuaternion& Entity::GetRotation() const { + auto* controllablePhysicsComponent = GetComponent(); + + if (controllablePhysicsComponent != nullptr) { + return controllablePhysicsComponent->GetRotation(); + } + + auto* phantomPhysicsComponent = GetComponent(); + + if (phantomPhysicsComponent != nullptr) { + return phantomPhysicsComponent->GetRotation(); + } + + auto* simplePhysicsComponent = GetComponent(); + + if (simplePhysicsComponent != nullptr) { + return simplePhysicsComponent->GetRotation(); + } + + auto* vehiclePhysicsComponent = GetComponent(); + + if (vehiclePhysicsComponent != nullptr) { + return vehiclePhysicsComponent->GetRotation(); + } + + return NiQuaternion::IDENTITY; +} + +void Entity::SetPosition(const NiPoint3& position) { + auto* controllablePhysicsComponent = GetComponent(); + + if (controllablePhysicsComponent != nullptr) { + controllablePhysicsComponent->SetPosition(position); + } + + auto* phantomPhysicsComponent = GetComponent(); + + if (phantomPhysicsComponent != nullptr) { + phantomPhysicsComponent->SetPosition(position); + } + + auto* simplePhysicsComponent = GetComponent(); + + if (simplePhysicsComponent != nullptr) { + simplePhysicsComponent->SetPosition(position); + } + + auto* vehiclePhysicsComponent = GetComponent(); + + if (vehiclePhysicsComponent != nullptr) { + vehiclePhysicsComponent->SetPosition(position); + } + + EntityManager::Instance()->SerializeEntity(this); +} + +void Entity::SetRotation(const NiQuaternion& rotation) { + auto* controllablePhysicsComponent = GetComponent(); + + if (controllablePhysicsComponent != nullptr) { + controllablePhysicsComponent->SetRotation(rotation); + } + + auto* phantomPhysicsComponent = GetComponent(); + + if (phantomPhysicsComponent != nullptr) { + phantomPhysicsComponent->SetRotation(rotation); + } + + auto* simplePhysicsComponent = GetComponent(); + + if (simplePhysicsComponent != nullptr) { + simplePhysicsComponent->SetRotation(rotation); + } + + auto* vehiclePhysicsComponent = GetComponent(); + + if (vehiclePhysicsComponent != nullptr) { + vehiclePhysicsComponent->SetRotation(rotation); + } + + EntityManager::Instance()->SerializeEntity(this); +} + +bool Entity::HasVar(const std::u16string& name) const { + auto hasVar = std::find_if(m_Settings.begin(), m_Settings.end(), [&name](const LDFBaseData* data) { + return data->GetKey() == name; + }); + return hasVar != m_Settings.end(); +} + +std::vector& Entity::GetTargetsInPhantom() { + auto toRemove = std::remove_if(m_TargetsInPhantom.begin(), m_TargetsInPhantom.end(), [this](const LWOOBJID& id) { + return EntityManager::Instance()->GetEntity(id) == nullptr; + }); + m_TargetsInPhantom.erase(toRemove, m_TargetsInPhantom.end()); + return m_TargetsInPhantom; +} + +void Entity::SendNetworkVar(const std::string& data, const SystemAddress& sysAddr) { + GameMessages::SendSetNetworkScriptVar(this, sysAddr, data); +} + +LDFBaseData* Entity::GetVarData(const std::u16string& name) const { + for (auto* data : m_Settings) { + if (data && data->GetKey() == name) return data; + } + return nullptr; +} + +std::string Entity::GetVarAsString(const std::u16string& name) const { + auto* data = GetVarData(name); + return data ? data->GetValueAsString() : ""; +} + +void Entity::Resurrect() { + if (IsPlayer()) GameMessages::SendResurrect(this); +} + +void Entity::AddGroup(const std::string& group) { + if (std::find(m_Groups.begin(), m_Groups.end(), group) == m_Groups.end()) { + m_Groups.push_back(group); + } +} + +void Entity::RetroactiveVaultSize() { + auto* inventoryComponent = GetComponent(); + if (!inventoryComponent) return; + + auto itemsVault = inventoryComponent->GetInventory(eInventoryType::VAULT_ITEMS); + auto modelVault = inventoryComponent->GetInventory(eInventoryType::VAULT_MODELS); + + if (itemsVault->GetSize() == modelVault->GetSize()) return; + + modelVault->SetSize(itemsVault->GetSize()); +} diff --git a/dGame/dEntity/Entity.h b/dGame/dEntity/Entity.h new file mode 100644 index 000000000..2aae5f871 --- /dev/null +++ b/dGame/dEntity/Entity.h @@ -0,0 +1,387 @@ +#ifndef __ENTITY__H__ +#define __ENTITY__H__ + +#include +#include +#include +#include + +#include "dCommonVars.h" +#include "NiPoint3.h" +#include "NiQuaternion.h" +#include "eKillType.h" + +namespace Loot { + class Info; +}; + +namespace tinyxml2 { + class XMLDocument; +}; + +namespace CppScripts { + class Script; +}; + +class Player; +class EntityInfo; +class User; +class Spawner; +class ScriptComponent; +class dpEntity; +class EntityTimer; +class Component; +class Item; +class Character; +class EntityCallbackTimer; +class LDFBaseData; +class BoxDimensions; +enum class eTriggerEventType; +enum class eGameMasterLevel : uint8_t; +enum class eReplicaComponentType : uint32_t; +enum class eReplicaPacketType : uint8_t; +enum class eCinematicEvent : uint32_t; + +/** + * An entity in the world. + * Entities are composed of components which define their behavior. + */ + +using ComponentPtr = std::unique_ptr; +using ComponentWhitelist = std::vector; +using TemplateComponents = std::vector>; + +class Entity { +public: + explicit Entity(const LWOOBJID& objectID, EntityInfo info, Entity* parentEntity = nullptr); + virtual ~Entity(); + + void ApplyComponentWhitelist(TemplateComponents& components) const; + static const std::vector& GetComponentWhitelists() { return m_ComponentWhitelists; } + + /** + * Functions used for creating and setting up an Entity. + */ + void Initialize(); + + /** + * There are some very very edge cases we need to take care of with what components + * are kept and removed. Here is where we take care of those cases. + */ + void ApplyComponentBlacklist(TemplateComponents& components) const; + + // For adding and removing components based on LDF keys + void ApplyComponentConfig(TemplateComponents& components) const; + + // Paths have several components they could add. This function will add them. + void AddPathComponent(TemplateComponents& components) const; + + /** + * Determines if we should ghost an Entity or not. + * Ghosting means we no longer serialize it to a specific player because it is out of range. + */ + void IsGhosted(); + + bool operator==(const Entity& other) const; + bool operator!=(const Entity& other) const; + + // General Entity info + const LWOOBJID GetObjectID() const { return m_ObjectID; } + + const LOT GetLOT() const { return m_TemplateID; } + + Entity* GetParentEntity() const { return m_ParentEntity; } + + const bool GetIsGhostingCandidate() const { return m_IsGhostingCandidate; } + + const float GetDefaultScale() const { return m_Scale; } + + Entity* GetOwner() const; + void SetOwnerOverride(const LWOOBJID& value) { m_OwnerOverride = value; }; + Entity* GetScheduledKiller() { return m_ScheduleKiller; } + std::vector& GetTargetsInPhantom(); + const std::unordered_map& GetComponents() { return m_Components; } + + // Position related info + const NiPoint3& GetDefaultPosition() const { return m_DefaultPosition; }; + + const NiQuaternion& GetDefaultRotation() const { return m_DefaultRotation; }; + + const NiPoint3& GetPosition() const; + void SetPosition(const NiPoint3& position); + + const NiQuaternion& GetRotation() const; + void SetRotation(const NiQuaternion& rotation); + + virtual NiPoint3 GetRespawnPosition() const { return NiPoint3::ZERO; } + virtual NiQuaternion GetRespawnRotation() const { return NiQuaternion::IDENTITY; } + + // Spawner related info + Spawner* GetSpawner() const { return m_Spawner; } + + LWOOBJID GetSpawnerID() const { return m_SpawnerID; } + + const std::vector& GetGroups() { return m_Groups; }; + void SetGroups(const std::vector& value) { m_Groups = value; } + void AddGroup(const std::string& group); + + // LDF related into + const std::vector& GetSettings() const { return m_Settings; } + + const std::vector& GetNetworkSettings() const { return m_NetworkSettings; } + + // Networking related info + const int8_t GetObservers() const { return m_Observers; } + void SetObservers(const int8_t value); + + const uint16_t GetNetworkId() const { return m_NetworkID; } + void SetNetworkId(const uint16_t id) { m_NetworkID = id; } + + // Player extended info + virtual User* GetParentUser() const { return nullptr; }; + + virtual const SystemAddress GetSystemAddress() const { return UNASSIGNED_SYSTEM_ADDRESS; }; + + virtual void SetRespawnPosition(const NiPoint3& position) {}; + + virtual void SetRespawnRotation(const NiQuaternion& rotation) {}; + + virtual void SetSystemAddress(const SystemAddress& value) {}; + + eGameMasterLevel GetGMLevel() const { return m_GMLevel; } + void SetGMLevel(const eGameMasterLevel value); + + bool GetPlayerReadyForUpdates() const { return m_PlayerIsReadyForUpdates; } + void SetPlayerReadyForUpdates() { m_PlayerIsReadyForUpdates = true; } + + Character* GetCharacter() const { return m_Character; } + void SetCharacter(Character* value) { m_Character = value; } + // End info + + // If you are calling this, then calling GetComponent, just call GetComponent and check for nullptr. + bool HasComponent(const eReplicaComponentType componentId) const; + + // Event management + /** + * Call these when you want to observe events. Observed events should follow the following naming convention + * in scripts Notify. For example, if you want to observe the "OnDeath" event, you would call + * entity->Subscribe(script, "OnDeath"). Then in your script, you would have a function called NotifyOnDeath. + */ + void Subscribe(CppScripts::Script* scriptToAdd, const std::string& notificationName); + + /** + * Call this when you want to stop observing an event. The scriptToRemove will + * no longer be notified of notificationName events + */ + void Unsubscribe(CppScripts::Script* scriptToRemove, const std::string& notificationName); + + // Proximity radius management + void AddProximityRadius(const float proxRadius, const std::string& name); + void AddProximityRadius(const BoxDimensions& dimensions, const std::string& name); + + // Technically this is the live accrate API, however what it does is not setting the proximity radius, but rather + // adding a new one to the list of proximity radii. For that reason we will have the old API just call AddProximityRadius. + inline void SetProximityRadius(const float proxRadius, const std::string& name) { this->AddProximityRadius(proxRadius, name); } + inline void SetProximityRadius(const BoxDimensions& dimensions, const std::string& name) { this->AddProximityRadius(dimensions, name); } + + void AddRebuildCompleteCallback(const std::function& callback) const; + void AddCollisionPhantomCallback(const std::function& callback) { m_PhantomCollisionCallbacks.push_back(callback); }; + void AddDieCallback(const std::function& callback) { m_DieCallbacks.push_back(callback); }; + void TriggerEvent(const eTriggerEventType event, Entity* optionalTarget = nullptr); + void NotifyObject(Entity* sender, const std::u16string& name, const int32_t param1 = 0, const int32_t param2 = 0); + + // Parent Child management + void AddChild(Entity* child); + void RemoveChild(Entity* child); + void RemoveParent(); + + // Timer management + void AddTimer(const std::string& name, const float time); + void AddCallbackTimer(const float time, const std::function& callback); + bool HasTimer(const std::string& name); + void CancelCallbackTimers(); + void CancelAllTimers(); + void CancelTimer(const std::string& name); + + // Serialization + void WriteBaseReplicaData(RakNet::BitStream* outBitStream, const eReplicaPacketType packetType); + void WriteComponents(RakNet::BitStream* outBitStream, const eReplicaPacketType packetType); + + // Scripting + // Get the script attached to this entity. Will never return nullptr. + CppScripts::Script* GetScript() const; + void OnCollisionProximity(const LWOOBJID otherEntity, const std::string& proxName, const std::string& status); + void OnCollisionPhantom(const LWOOBJID otherEntity); + void OnCollisionLeavePhantom(const LWOOBJID otherEntity); + + void OnFireEventServerSide(Entity* sender, const std::string args, const int32_t param1 = -1, const int32_t param2 = -1, const 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, const eCinematicEvent event, const std::u16string& pathName, + const float pathTime, const float totalTime, const int32_t waypoint); + + void OnEmoteReceived(const int32_t emote, Entity* target); + + void OnUse(Entity* originator); + + void OnHitOrHealResult(Entity* attacker, const 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, const int32_t button, const std::u16string& identifier, const std::u16string& userData); + void OnChoiceBoxResponse(Entity* sender, const int32_t button, const std::u16string& buttonIdentifier, const std::u16string& identifier); + void RequestActivityExit(Entity* sender, const LWOOBJID& player, const bool canceled); + // End scripting + + // Cleanup functions + void Smash(const LWOOBJID source = LWOOBJID_EMPTY, const eKillType killType = eKillType::VIOLENT, const std::u16string& deathType = u""); + // Odds are you do not need to call this. Call Smash instead. + void Kill(Entity* murderer = nullptr); + + void ScheduleKillAfterUpdate(Entity* murderer = nullptr); + void ScheduleDestructionAfterUpdate() { m_ShouldDestroyAfterUpdate = true; } + + void Resurrect(); + + // Loot management (should be moved to Player. Not every Entity needs this.) + void AddLootItem(const Loot::Info& info); + void PickupItem(const LWOOBJID& objectID); + + bool CanPickupCoins(const uint64_t& count) const; + void PickupCoins(const uint64_t& count); + void RegisterCoinDrop(const uint64_t& count); + + // State checkers + void Sleep(); + void Wake(); + bool IsSleeping() const; + bool IsDead() const; + bool IsPlayer() const; + + // Update + void UpdateXMLDoc(tinyxml2::XMLDocument* doc); + void Update(float deltaTime); + + //Retroactively corrects the model vault size due to incorrect initialization in a previous patch. + void RetroactiveVaultSize(); + void ResetFlags(); + + // LDF Setting accessors + bool GetBoolean(const std::u16string& name) const { return GetVar(name); }; + int32_t GetI32(const std::u16string& name) const { return GetVar(name); }; + int64_t GetI64(const std::u16string& name) const { return GetVar(name); }; + bool HasVar(const std::u16string& name) const; + 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; + + // LDF Setting assignment shorthands + void SetBoolean(const std::u16string& name, bool value) { SetVar(name, value); } + void SetI32(const std::u16string& name, int32_t value) { SetVar(name, value); }; + void SetI64(const std::u16string& name, int64_t value) { SetVar(name, value); }; + + // Template declarations + template + const T& GetVar(const std::u16string& name) const; + + template + T GetVarAs(const std::u16string& name) const; + + template + void SetVar(const std::u16string& name, const T& value); + + template + T GetNetworkVar(const std::u16string& name); + + void SendNetworkVar(const std::string& data, const SystemAddress& sysAddr); + + template + void SetNetworkVar(const std::u16string& name, const T& value, const SystemAddress& sysAddr = UNASSIGNED_SYSTEM_ADDRESS); + + template + void SetNetworkVar(const std::u16string& name, const std::vector& value, const SystemAddress& sysAddr = UNASSIGNED_SYSTEM_ADDRESS); + /** + * @brief Get a non-owning reference to a component + * + * @tparam Cmpt The component to get a non-owning reference of + * @return Cmpt* The non-owning pointer to the component + */ + template + Cmpt* GetComponent() const; + + /** + * @brief Adds a component to this Entity. + * + * @tparam Cmpt The component to create + * @tparam ConstructorValues The constructor values to forward to the component + * @param arguments The constructor values to forward to the component + * @return Cmpt* A non-owning pointer to the created component, + * or a non-owning pointer to the existing component if the component already existed. + */ + template + Cmpt* AddComponent(ConstructorValues... arguments); + + /** + * @brief Removes a component from this Entity. + */ + template + void RemoveComponent(); + +protected: + LWOOBJID m_ObjectID; + LWOOBJID m_SpawnerID; + LWOOBJID m_OwnerOverride; + + uint32_t m_SpawnerNodeID; + LOT m_TemplateID; + + NiPoint3 m_DefaultPosition; + NiQuaternion m_DefaultRotation; + + float m_Scale; + int8_t m_Observers; + eGameMasterLevel m_GMLevel; + + Entity* m_ParentEntity; //For spawners and the like + Entity* m_ScheduleKiller; + Spawner* m_Spawner; + Character* m_Character; + + bool m_HasSpawnerNodeID; + + bool m_ShouldDestroyAfterUpdate; + bool m_PlayerIsReadyForUpdates; + bool m_IsGhostingCandidate; + bool m_IsParentChildDirty; + uint16_t m_NetworkID; + + std::unordered_map m_Components; + std::vector> m_Timers; + std::vector> m_PendingTimers; + std::vector> m_CallbackTimers; + std::vector m_Settings; + std::vector m_NetworkSettings; + std::vector m_ChildEntities; + std::vector m_Groups; + std::vector> m_DieCallbacks; + std::vector> m_PhantomCollisionCallbacks; + std::vector m_TargetsInPhantom; + + static const std::vector m_ComponentWhitelists; + static const std::array m_ComponentOrder; +}; + +#include "Entity.tcc" + +#endif //!__ENTITY__H__ diff --git a/dGame/dEntity/Entity.tcc b/dGame/dEntity/Entity.tcc new file mode 100644 index 000000000..a043a224a --- /dev/null +++ b/dGame/dEntity/Entity.tcc @@ -0,0 +1,161 @@ +// Template definitions for Entity. + +#ifndef __ENTITY__H__ +#error "Include Entity.h instead of Entity.tpp" +#endif + +#include "LDFFormat.h" + +// Access definitions +template +Cmpt* Entity::GetComponent() const { + const auto& componentItr = this->m_Components.find(Cmpt::ComponentType); + return componentItr == this->m_Components.end() ? nullptr : dynamic_cast(componentItr->second.get()); +} + +template +Cmpt* Entity::AddComponent(ConstructorValues...arguments) { + auto* component = GetComponent(); + if (component) return dynamic_cast(component); + + auto& insertedComponent = m_Components.insert_or_assign(Cmpt::ComponentType, + std::make_unique(this, std::forward(arguments)...)).first->second; + return dynamic_cast(insertedComponent.get()); +} + +template +void Entity::RemoveComponent() { + m_Components.erase(Cmpt::ComponentType); +} + +template +const T& Entity::GetVar(const std::u16string& name) const { + auto* data = GetVarData(name); + + if (data == nullptr) { + return LDFData::Default; + } + + auto* typed = dynamic_cast*>(data); + + if (typed == nullptr) { + return LDFData::Default; + } + + return typed->GetValue(); +} + +template +T Entity::GetVarAs(const std::u16string& name) const { + const auto data = GetVarAsString(name); + + T value; + + if (!GeneralUtils::TryParse(data, value)) { + return LDFData::Default; + } + + return value; +} + +template +void Entity::SetVar(const std::u16string& name, const T& value) { + auto* data = GetVarData(name); + + if (data == nullptr) { + auto* data = new LDFData(name, value); + + m_Settings.push_back(data); + + return; + } + + auto* typed = dynamic_cast*>(data); + + if (typed == nullptr) { + return; + } + + typed->SetValue(value); +} + +template +void Entity::SetNetworkVar(const std::u16string& name, const T& value, const SystemAddress& sysAddr) { + LDFData* newData = nullptr; + + for (auto* data : m_NetworkSettings) { + if (data->GetKey() != name) + continue; + + newData = dynamic_cast*>(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(name, value); + } + + m_NetworkSettings.push_back(newData); + SendNetworkVar(newData->GetString(true), sysAddr); +} + +template +void Entity::SetNetworkVar(const std::u16string& name, const std::vector& values, const SystemAddress& sysAddr) { + std::stringstream updates; + auto index = 1; + + for (const auto& value : values) { + LDFData* newData = nullptr; + const auto& indexedName = name + u"." + GeneralUtils::to_u16string(index); + + for (auto* data : m_NetworkSettings) { + if (data->GetKey() != indexedName) + continue; + + newData = dynamic_cast*>(data); + newData->SetValue(value); + break; + } + + if (newData == nullptr) { + newData = new LDFData(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 +T Entity::GetNetworkVar(const std::u16string& name) { + for (auto* data : m_NetworkSettings) { + if (data == nullptr || data->GetKey() != name) + continue; + + auto* typed = dynamic_cast*>(data); + if (typed == nullptr) + continue; + + return typed->GetValue(); + } + + return LDFData::Default; +} diff --git a/dGame/dEntity/EntityCallbackTimer.cpp b/dGame/dEntity/EntityCallbackTimer.cpp index e07c11893..95add4837 100644 --- a/dGame/dEntity/EntityCallbackTimer.cpp +++ b/dGame/dEntity/EntityCallbackTimer.cpp @@ -1,12 +1,12 @@ #include "EntityCallbackTimer.h" -EntityCallbackTimer::EntityCallbackTimer(float time, std::function callback) { +EntityCallbackTimer::EntityCallbackTimer(const float& time, const std::function& callback) { m_Time = time; m_Callback = callback; } -EntityCallbackTimer::~EntityCallbackTimer() { - +void EntityCallbackTimer::ExecuteCallback() { + m_Callback(); } std::function EntityCallbackTimer::GetCallback() { diff --git a/dGame/dEntity/EntityCallbackTimer.h b/dGame/dEntity/EntityCallbackTimer.h index 2a7e58f03..e097ca621 100644 --- a/dGame/dEntity/EntityCallbackTimer.h +++ b/dGame/dEntity/EntityCallbackTimer.h @@ -5,9 +5,9 @@ class EntityCallbackTimer { public: - EntityCallbackTimer(float time, std::function callback); - ~EntityCallbackTimer(); + EntityCallbackTimer(const float& time, const std::function& callback); + void ExecuteCallback(); std::function GetCallback(); float GetTime(); diff --git a/dGame/dEntity/EntityTimer.cpp b/dGame/dEntity/EntityTimer.cpp index 0363fc5b9..078d0da3d 100644 --- a/dGame/dEntity/EntityTimer.cpp +++ b/dGame/dEntity/EntityTimer.cpp @@ -1,14 +1,10 @@ #include "EntityTimer.h" -EntityTimer::EntityTimer(std::string name, float time) { +EntityTimer::EntityTimer(const std::string& name, const float& time) { m_Name = name; m_Time = time; } -EntityTimer::~EntityTimer() { - -} - std::string EntityTimer::GetName() { return m_Name; } diff --git a/dGame/dEntity/EntityTimer.h b/dGame/dEntity/EntityTimer.h index 9de0345dc..90fe3e468 100644 --- a/dGame/dEntity/EntityTimer.h +++ b/dGame/dEntity/EntityTimer.h @@ -4,8 +4,7 @@ class EntityTimer { public: - EntityTimer(std::string name, float time); - ~EntityTimer(); + EntityTimer(const std::string& name, const float& time); std::string GetName(); float GetTime(); diff --git a/dGame/dGameMessages/GameMessageHandler.cpp b/dGame/dGameMessages/GameMessageHandler.cpp index be7515989..eab9488d7 100644 --- a/dGame/dGameMessages/GameMessageHandler.cpp +++ b/dGame/dGameMessages/GameMessageHandler.cpp @@ -25,7 +25,7 @@ #include "CDClientManager.h" #include "CDSkillBehaviorTable.h" #include "SkillComponent.h" -#include "RacingControlComponent.h" +#include "RacingComponent.h" #include "RequestServerProjectileImpact.h" #include "SyncSkill.h" #include "StartSkill.h" @@ -110,7 +110,7 @@ void GameMessageHandler::HandleMessage(RakNet::BitStream* inStream, const System player->ConstructLimboEntities(); } - InventoryComponent* inv = entity->GetComponent(); + auto* inv = entity->GetComponent(); if (inv) { auto items = inv->GetEquippedItems(); for (auto pair : items) { @@ -126,28 +126,22 @@ void GameMessageHandler::HandleMessage(RakNet::BitStream* inStream, const System std::vector racingControllers = EntityManager::Instance()->GetEntitiesByComponent(eReplicaComponentType::RACING_CONTROL); for (Entity* racingController : racingControllers) { - auto* racingComponent = racingController->GetComponent(); - if (racingComponent != nullptr) { - racingComponent->OnPlayerLoaded(entity); - } + auto* racingComponent = racingController->GetComponent(); + if (racingComponent) racingComponent->OnPlayerLoaded(entity); } Entity* zoneControl = EntityManager::Instance()->GetZoneControlEntity(); - for (CppScripts::Script* script : CppScripts::GetEntityScripts(zoneControl)) { - script->OnPlayerLoaded(zoneControl, player); - } + if (zoneControl) zoneControl->GetScript()->OnPlayerLoaded(zoneControl, player); std::vector scriptedActs = EntityManager::Instance()->GetEntitiesByComponent(eReplicaComponentType::SCRIPT); 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->OnPlayerLoaded(scriptEntity, player); - } + scriptEntity->GetScript()->OnPlayerLoaded(scriptEntity, player); } } //Kill player if health == 0 - if (entity->GetIsDead()) { + if (entity->IsDead()) { entity->Smash(entity->GetObjectID()); } @@ -243,13 +237,6 @@ void GameMessageHandler::HandleMessage(RakNet::BitStream* inStream, const System case eGameMessageType::REQUEST_RESURRECT: { GameMessages::SendResurrect(entity); - /*auto* dest = static_cast(entity->GetComponent(eReplicaComponentType::DESTROYABLE)); - if (dest) { - dest->SetHealth(4); - dest->SetArmor(0); - dest->SetImagination(6); - EntityManager::Instance()->SerializeEntity(entity); - }*/ break; } case eGameMessageType::HANDLE_HOT_PROPERTY_DATA: { @@ -282,7 +269,7 @@ void GameMessageHandler::HandleMessage(RakNet::BitStream* inStream, const System if (startSkill.skillID == 1561 || startSkill.skillID == 1562 || startSkill.skillID == 1541) return; - MissionComponent* comp = entity->GetComponent(); + auto* comp = entity->GetComponent(); if (comp) { comp->Progress(eMissionTaskType::USE_SKILL, startSkill.skillID); } @@ -300,7 +287,7 @@ void GameMessageHandler::HandleMessage(RakNet::BitStream* inStream, const System success = skillComponent->CastPlayerSkill(behaviorId, startSkill.uiSkillHandle, bs, startSkill.optionalTargetID, startSkill.skillID); if (success && entity->GetCharacter()) { - DestroyableComponent* destComp = entity->GetComponent(); + auto* destComp = entity->GetComponent(); destComp->SetImagination(destComp->GetImagination() - skillTable->GetSkillByID(startSkill.skillID).imaginationcost); } diff --git a/dGame/dGameMessages/GameMessages.cpp b/dGame/dGameMessages/GameMessages.cpp index 0c3207d6c..404db64dd 100644 --- a/dGame/dGameMessages/GameMessages.cpp +++ b/dGame/dGameMessages/GameMessages.cpp @@ -24,7 +24,7 @@ #include "dConfig.h" #include "TeamManager.h" #include "ChatPackets.h" -#include "RocketLaunchLupComponent.h" +#include "MultiZoneEntranceComponent.h" #include "eUnequippableActiveType.h" #include "eMovementPlatformState.h" #include "LeaderboardManager.h" @@ -61,19 +61,19 @@ #include "MissionComponent.h" #include "DestroyableComponent.h" #include "ScriptComponent.h" -#include "RebuildComponent.h" +#include "QuickBuildComponent.h" #include "VendorComponent.h" #include "InventoryComponent.h" #include "RocketLaunchpadControlComponent.h" #include "PropertyEntranceComponent.h" +#include "CollectibleComponent.h" #include "MovingPlatformComponent.h" #include "PetComponent.h" #include "ModuleAssemblyComponent.h" -#include "VehiclePhysicsComponent.h" #include "RenderComponent.h" #include "PossessableComponent.h" -#include "PossessorComponent.h" -#include "RacingControlComponent.h" +#include "PossessionComponent.h" +#include "RacingComponent.h" #include "RailActivatorComponent.h" #include "LevelProgressionComponent.h" @@ -948,10 +948,10 @@ void GameMessages::SendResurrect(Entity* entity) { destroyableComponent->SetImagination(imaginationToRestore); } } - }); + }); - auto cont = static_cast(entity->GetComponent(eReplicaComponentType::CONTROLLABLE_PHYSICS)); + auto* cont = entity->GetComponent(); if (cont && entity->GetLOT() == 1) { cont->SetPosition(entity->GetRespawnPosition()); cont->SetRotation(entity->GetRespawnRotation()); @@ -1148,7 +1148,7 @@ void GameMessages::SendPlayerReachedRespawnCheckpoint(Entity* entity, const NiPo bitStream.Write(position.y); bitStream.Write(position.z); - auto con = static_cast(entity->GetComponent(eReplicaComponentType::CONTROLLABLE_PHYSICS)); + auto* con = entity->GetComponent(); if (con) { auto rot = con->GetRotation(); bitStream.Write(rot.x); @@ -1283,10 +1283,10 @@ void GameMessages::SendVendorStatusUpdate(Entity* entity, const SystemAddress& s CBITSTREAM; CMSGHEADER; - VendorComponent* vendor = static_cast(entity->GetComponent(eReplicaComponentType::VENDOR)); + auto* vendor = entity->GetComponent(); if (!vendor) return; - std::map vendorItems = vendor->GetInventory(); + auto vendorItems = vendor->GetInventory(); bitStream.Write(entity->GetObjectID()); bitStream.Write(eGameMessageType::VENDOR_STATUS_UPDATE); @@ -1294,9 +1294,9 @@ void GameMessages::SendVendorStatusUpdate(Entity* entity, const SystemAddress& s bitStream.Write(bUpdateOnly); bitStream.Write(static_cast(vendorItems.size())); - for (std::pair item : vendorItems) { - bitStream.Write(static_cast(item.first)); - bitStream.Write(static_cast(item.second)); + for (const auto&[lot, sortPriority] : vendorItems) { + bitStream.Write(lot); + bitStream.Write(sortPriority); } if (sysAddr == UNASSIGNED_SYSTEM_ADDRESS) SEND_PACKET_BROADCAST @@ -1405,7 +1405,7 @@ void GameMessages::SendMoveInventoryBatch(Entity* entity, uint32_t stackCount, i CBITSTREAM; CMSGHEADER; - InventoryComponent* inv = static_cast(entity->GetComponent(eReplicaComponentType::INVENTORY)); + auto* inv = entity->GetComponent(); if (!inv) return; Item* itemStack = inv->FindItemById(iObjID); @@ -1985,7 +1985,7 @@ void GameMessages::SendOpenPropertyManagment(const LWOOBJID objectId, const Syst CBITSTREAM; CMSGHEADER; - bitStream.Write(PropertyManagementComponent::Instance()->GetParent()->GetObjectID()); + bitStream.Write(PropertyManagementComponent::Instance()->GetParentEntity()->GetObjectID()); bitStream.Write(eGameMessageType::OPEN_PROPERTY_MANAGEMENT); if (sysAddr == UNASSIGNED_SYSTEM_ADDRESS) SEND_PACKET_BROADCAST; @@ -2243,7 +2243,7 @@ void GameMessages::HandleQueryPropertyData(RakNet::BitStream* inStream, Entity* entity = entites[0]; */ - auto* propertyVendorComponent = static_cast(entity->GetComponent(eReplicaComponentType::PROPERTY_VENDOR)); + auto* propertyVendorComponent = entity->GetComponent(); if (propertyVendorComponent != nullptr) { propertyVendorComponent->OnQueryPropertyData(entity, sysAddr); @@ -2255,7 +2255,7 @@ void GameMessages::HandleQueryPropertyData(RakNet::BitStream* inStream, Entity* entity = entites[0]; */ - auto* propertyManagerComponent = static_cast(entity->GetComponent(eReplicaComponentType::PROPERTY_MANAGEMENT)); + auto* propertyManagerComponent = entity->GetComponent(); if (propertyManagerComponent != nullptr) { propertyManagerComponent->OnQueryPropertyData(entity, sysAddr); @@ -2752,7 +2752,7 @@ void GameMessages::HandlePropertyEntranceSync(RakNet::BitStream* inStream, Entit bool updateUi{}; int32_t numResults{}; int32_t reputation{}; - int32_t sortMethod{}; + ePropertySortType sortMethod{}; int32_t startIndex{}; uint32_t filterTextLength{}; std::string filterText{}; @@ -2769,15 +2769,14 @@ void GameMessages::HandlePropertyEntranceSync(RakNet::BitStream* inStream, Entit for (auto i = 0u; i < filterTextLength; i++) { char c; - inStream->Read(c); - filterText.push_back(c); + if (inStream->Read(c)) filterText.push_back(c); } auto* player = Player::GetPlayer(sysAddr); auto* entranceComponent = entity->GetComponent(); - if (entranceComponent == nullptr) return; + if (!entranceComponent) return; entranceComponent->OnPropertyEntranceSync(player, includeNullAddress, @@ -2808,9 +2807,9 @@ void GameMessages::HandleEnterProperty(RakNet::BitStream* inStream, Entity* enti return; } - auto rocketLaunchLupComponent = entity->GetComponent(); - if (rocketLaunchLupComponent != nullptr) { - rocketLaunchLupComponent->OnSelectWorld(player, index); + auto* multiZoneEntranceComponent = entity->GetComponent(); + if (multiZoneEntranceComponent != nullptr) { + multiZoneEntranceComponent->OnSelectWorld(player, index); } } @@ -2983,14 +2982,14 @@ void GameMessages::SendSetStunned(LWOOBJID objectId, eStateChangeType stateChang } void GameMessages::SendSetStunImmunity(LWOOBJID target, eStateChangeType state, const SystemAddress& sysAddr, - LWOOBJID originator, - bool bImmuneToStunAttack, - bool bImmuneToStunEquip, - bool bImmuneToStunInteract, - bool bImmuneToStunJump, - bool bImmuneToStunMove, - bool bImmuneToStunTurn, - bool bImmuneToStunUseItem) { + LWOOBJID originator, + bool bImmuneToStunAttack, + bool bImmuneToStunEquip, + bool bImmuneToStunInteract, + bool bImmuneToStunJump, + bool bImmuneToStunMove, + bool bImmuneToStunTurn, + bool bImmuneToStunUseItem) { CBITSTREAM; CMSGHEADER; @@ -3015,15 +3014,15 @@ void GameMessages::SendSetStunImmunity(LWOOBJID target, eStateChangeType state, } void GameMessages::SendSetStatusImmunity(LWOOBJID objectId, eStateChangeType state, const SystemAddress& sysAddr, - bool bImmuneToBasicAttack, - bool bImmuneToDamageOverTime, - bool bImmuneToKnockback, - bool bImmuneToInterrupt, - bool bImmuneToSpeed, - bool bImmuneToImaginationGain, - bool bImmuneToImaginationLoss, - bool bImmuneToQuickbuildInterrupt, - bool bImmuneToPullToPoint) { + bool bImmuneToBasicAttack, + bool bImmuneToDamageOverTime, + bool bImmuneToKnockback, + bool bImmuneToInterrupt, + bool bImmuneToSpeed, + bool bImmuneToImaginationGain, + bool bImmuneToImaginationLoss, + bool bImmuneToQuickbuildInterrupt, + bool bImmuneToPullToPoint) { CBITSTREAM; CMSGHEADER; @@ -3718,7 +3717,7 @@ void GameMessages::SendPetNameChanged(LWOOBJID objectId, int32_t moderationStatu void GameMessages::HandleClientExitTamingMinigame(RakNet::BitStream* inStream, Entity* entity, const SystemAddress& sysAddr) { bool bVoluntaryExit = inStream->ReadBit(); - auto* petComponent = PetComponent::GetTamingPet(entity->GetObjectID()); + auto petComponent = PetComponent::GetTamingPet(entity->GetObjectID()); if (petComponent == nullptr) { return; @@ -3728,7 +3727,7 @@ void GameMessages::HandleClientExitTamingMinigame(RakNet::BitStream* inStream, E } void GameMessages::HandleStartServerPetMinigameTimer(RakNet::BitStream* inStream, Entity* entity, const SystemAddress& sysAddr) { - auto* petComponent = PetComponent::GetTamingPet(entity->GetObjectID()); + auto petComponent = PetComponent::GetTamingPet(entity->GetObjectID()); if (petComponent == nullptr) { return; @@ -3756,7 +3755,7 @@ void GameMessages::HandlePetTamingTryBuild(RakNet::BitStream* inStream, Entity* clientFailed = inStream->ReadBit(); - auto* petComponent = PetComponent::GetTamingPet(entity->GetObjectID()); + auto petComponent = PetComponent::GetTamingPet(entity->GetObjectID()); if (petComponent == nullptr) { return; @@ -3770,7 +3769,7 @@ void GameMessages::HandleNotifyTamingBuildSuccess(RakNet::BitStream* inStream, E inStream->Read(position); - auto* petComponent = PetComponent::GetTamingPet(entity->GetObjectID()); + auto petComponent = PetComponent::GetTamingPet(entity->GetObjectID()); if (petComponent == nullptr) { return; @@ -3791,7 +3790,7 @@ void GameMessages::HandleRequestSetPetName(RakNet::BitStream* inStream, Entity* name.push_back(character); } - auto* petComponent = PetComponent::GetTamingPet(entity->GetObjectID()); + auto petComponent = PetComponent::GetTamingPet(entity->GetObjectID()); if (petComponent == nullptr) { petComponent = PetComponent::GetActivePet(entity->GetObjectID()); @@ -3831,7 +3830,7 @@ void GameMessages::HandleDespawnPet(RakNet::BitStream* inStream, Entity* entity, bDeletePet = inStream->ReadBit(); - auto* petComponent = PetComponent::GetActivePet(entity->GetObjectID()); + auto petComponent = PetComponent::GetActivePet(entity->GetObjectID()); if (petComponent == nullptr) { return; @@ -3889,11 +3888,9 @@ void GameMessages::HandleMessageBoxResponse(RakNet::BitStream* inStream, Entity* scriptedActivityComponent->HandleMessageBoxResponse(userEntity, GeneralUtils::UTF16ToWTF8(identifier)); } - auto* racingControlComponent = entity->GetComponent(); + auto* racingComponent = entity->GetComponent(); - if (racingControlComponent != nullptr) { - racingControlComponent->HandleMessageBoxResponse(userEntity, iButton, GeneralUtils::UTF16ToWTF8(identifier)); - } + if (racingComponent) racingComponent->HandleMessageBoxResponse(userEntity, iButton, GeneralUtils::UTF16ToWTF8(identifier)); for (auto* shootingGallery : EntityManager::Instance()->GetEntitiesByComponent(eReplicaComponentType::SHOOTING_GALLERY)) { shootingGallery->OnMessageBoxResponse(userEntity, iButton, identifier, userData); @@ -4055,21 +4052,21 @@ void GameMessages::HandleDismountComplete(RakNet::BitStream* inStream, Entity* e // If we aren't possessing somethings, the don't do anything if (objectId != LWOOBJID_EMPTY) { - auto* possessorComponent = entity->GetComponent(); + auto* possessionComponent = entity->GetComponent(); auto* mount = EntityManager::Instance()->GetEntity(objectId); // make sure we have the things we need and they aren't null - if (possessorComponent && mount) { - if (!possessorComponent->GetIsDismounting()) return; - possessorComponent->SetIsDismounting(false); - possessorComponent->SetPossessable(LWOOBJID_EMPTY); - possessorComponent->SetPossessableType(ePossessionType::NO_POSSESSION); + if (possessionComponent && mount) { + if (!possessionComponent->GetIsDismounting()) return; + possessionComponent->SetIsDismounting(false); + possessionComponent->SetPossessable(LWOOBJID_EMPTY); + possessionComponent->SetPossessableType(ePossessionType::NO_POSSESSION); // character related things auto* character = entity->GetComponent(); if (character) { // If we had an active item turn it off - if (possessorComponent->GetMountItemID() != LWOOBJID_EMPTY) GameMessages::SendMarkInventoryItemAsActive(entity->GetObjectID(), false, eUnequippableActiveType::MOUNT, possessorComponent->GetMountItemID(), entity->GetSystemAddress()); - possessorComponent->SetMountItemID(LWOOBJID_EMPTY); + if (possessionComponent->GetMountItemID() != LWOOBJID_EMPTY) GameMessages::SendMarkInventoryItemAsActive(entity->GetObjectID(), false, eUnequippableActiveType::MOUNT, possessionComponent->GetMountItemID(), entity->GetSystemAddress()); + possessionComponent->SetMountItemID(LWOOBJID_EMPTY); } // Set that the controllabel phsyics comp is teleporting @@ -4137,13 +4134,11 @@ void GameMessages::HandleRacingClientReady(RakNet::BitStream* inStream, Entity* return; } - auto* racingControlComponent = dZoneManager::Instance()->GetZoneControlObject()->GetComponent(); + auto* racingComponent = dZoneManager::Instance()->GetZoneControlObject()->GetComponent(); - if (racingControlComponent == nullptr) { - return; - } + if (!racingComponent) return; - racingControlComponent->OnRacingClientReady(player); + racingComponent->OnRacingClientReady(player); } @@ -4187,23 +4182,20 @@ void GameMessages::HandleRequestDie(RakNet::BitStream* inStream, Entity* entity, auto* zoneController = dZoneManager::Instance()->GetZoneControlObject(); - auto* racingControlComponent = zoneController->GetComponent(); + auto* racingComponent = zoneController->GetComponent(); Game::logger->Log("HandleRequestDie", "Got die request: %i", entity->GetLOT()); - if (racingControlComponent != nullptr) { - auto* possessableComponent = entity->GetComponent(); - - if (possessableComponent != nullptr) { - entity = EntityManager::Instance()->GetEntity(possessableComponent->GetPossessor()); + if (!racingComponent) return; + auto* possessableComponent = entity->GetComponent(); - if (entity == nullptr) { - return; - } - } + if (possessableComponent) { + entity = EntityManager::Instance()->GetEntity(possessableComponent->GetPossessor()); - racingControlComponent->OnRequestDie(entity); + if (!entity) return; } + + racingComponent->OnRequestDie(entity); } @@ -4230,13 +4222,11 @@ void GameMessages::HandleRacingPlayerInfoResetFinished(RakNet::BitStream* inStre auto* zoneController = dZoneManager::Instance()->GetZoneControlObject(); - auto* racingControlComponent = zoneController->GetComponent(); + auto* racingComponent = zoneController->GetComponent(); Game::logger->Log("HandleRacingPlayerInfoResetFinished", "Got finished: %i", entity->GetLOT()); - if (racingControlComponent != nullptr) { - racingControlComponent->OnRacingPlayerInfoResetFinished(player); - } + if (racingComponent) racingComponent->OnRacingPlayerInfoResetFinished(player); } void GameMessages::SendUpdateReputation(const LWOOBJID objectId, const int64_t reputation, const SystemAddress& sysAddr) { @@ -4565,7 +4555,7 @@ void GameMessages::SendVehicleNotifyFinishedRace(LWOOBJID objectId, const System SEND_PACKET; } -void GameMessages::SendAddBuff(LWOOBJID& objectID, const LWOOBJID& casterID, uint32_t buffID, uint32_t msDuration, +void GameMessages::SendAddBuff(const LWOOBJID& objectID, const LWOOBJID& casterID, uint32_t buffID, uint32_t msDuration, bool addImmunity, bool cancelOnDamaged, bool cancelOnDeath, bool cancelOnLogout, bool cancelOnRemoveBuff, bool cancelOnUi, bool cancelOnUnequip, bool cancelOnZone, const SystemAddress& sysAddr) { @@ -4638,7 +4628,7 @@ void GameMessages::HandleRequestMoveItemBetweenInventoryTypes(RakNet::BitStream* if (!item) return; // Despawn the pet if we are moving that pet to the vault. - auto* petComponent = PetComponent::GetActivePet(entity->GetObjectID()); + auto petComponent = PetComponent::GetActivePet(entity->GetObjectID()); if (petComponent && petComponent->GetDatabaseId() == item->GetSubKey()) { inventoryComponent->DespawnPet(); } @@ -4722,7 +4712,7 @@ void GameMessages::HandleBuyFromVendor(RakNet::BitStream* inStream, Entity* enti Entity* player = EntityManager::Instance()->GetEntity(user->GetLoggedInChar()); if (!player) return; - auto* propertyVendorComponent = static_cast(entity->GetComponent(eReplicaComponentType::PROPERTY_VENDOR)); + auto* propertyVendorComponent = entity->GetComponent(); if (propertyVendorComponent != nullptr) { propertyVendorComponent->OnBuyFromVendor(player, bConfirmed, item, count); @@ -4826,10 +4816,10 @@ void GameMessages::HandleSellToVendor(RakNet::BitStream* inStream, Entity* entit if (!player) return; Character* character = player->GetCharacter(); if (!character) return; - InventoryComponent* inv = static_cast(player->GetComponent(eReplicaComponentType::INVENTORY)); + auto* inv = player->GetComponent(); if (!inv) return; - VendorComponent* vend = static_cast(entity->GetComponent(eReplicaComponentType::VENDOR)); + auto* vend = entity->GetComponent(); if (!vend) return; Item* item = inv->FindItemById(iObjID); @@ -4876,10 +4866,10 @@ void GameMessages::HandleBuybackFromVendor(RakNet::BitStream* inStream, Entity* if (!player) return; Character* character = player->GetCharacter(); if (!character) return; - InventoryComponent* inv = static_cast(player->GetComponent(eReplicaComponentType::INVENTORY)); + auto* inv = player->GetComponent(); if (!inv) return; - VendorComponent* vend = static_cast(entity->GetComponent(eReplicaComponentType::VENDOR)); + auto* vend = entity->GetComponent(); if (!vend) return; Item* item = inv->FindItemById(iObjID); @@ -5030,7 +5020,7 @@ void GameMessages::HandleRebuildCancel(RakNet::BitStream* inStream, Entity* enti inStream->Read(bEarlyRelease); inStream->Read(userID); - RebuildComponent* rebComp = static_cast(entity->GetComponent(eReplicaComponentType::QUICK_BUILD)); + auto* rebComp = entity->GetComponent(); if (!rebComp) return; rebComp->CancelRebuild(EntityManager::Instance()->GetEntity(userID), eQuickBuildFailReason::CANCELED_EARLY); @@ -5063,7 +5053,7 @@ void GameMessages::HandleRequestUse(RakNet::BitStream* inStream, Entity* entity, if (bIsMultiInteractUse) { if (multiInteractType == 0) { - auto* missionOfferComponent = static_cast(interactedObject->GetComponent(eReplicaComponentType::MISSION_OFFER)); + auto* missionOfferComponent = interactedObject->GetComponent(); if (missionOfferComponent != nullptr) { missionOfferComponent->OfferMissions(entity, multiInteractID); @@ -5076,7 +5066,7 @@ void GameMessages::HandleRequestUse(RakNet::BitStream* inStream, Entity* entity, } //Perform use task if possible: - auto missionComponent = static_cast(entity->GetComponent(eReplicaComponentType::MISSION)); + auto* missionComponent = entity->GetComponent(); if (missionComponent == nullptr) return; @@ -5098,7 +5088,7 @@ void GameMessages::HandlePlayEmote(RakNet::BitStream* inStream, Entity* entity) if (emoteID == 0) return; std::string sAnimationName = "deaded"; //Default name in case we fail to get the emote - MissionComponent* missionComponent = entity->GetComponent(); + auto* missionComponent = entity->GetComponent(); if (!missionComponent) return; if (targetID != LWOOBJID_EMPTY) { @@ -5142,7 +5132,7 @@ void GameMessages::HandleModularBuildConvertModel(RakNet::BitStream* inStream, E if (!user) return; Entity* character = EntityManager::Instance()->GetEntity(user->GetLoggedInChar()); if (!character) return; - InventoryComponent* inv = static_cast(character->GetComponent(eReplicaComponentType::INVENTORY)); + auto* inv = character->GetComponent(); if (!inv) return; auto* item = inv->FindItemById(modelID); @@ -5185,7 +5175,7 @@ void GameMessages::HandleRespondToMission(RakNet::BitStream* inStream, Entity* e inStream->Read(isDefaultReward); if (isDefaultReward) inStream->Read(reward); - MissionComponent* missionComponent = static_cast(entity->GetComponent(eReplicaComponentType::MISSION)); + auto* missionComponent = entity->GetComponent(); if (!missionComponent) { Game::logger->Log("GameMessages", "Unable to get mission component for entity %llu to handle RespondToMission", playerID); return; @@ -5205,9 +5195,7 @@ void GameMessages::HandleRespondToMission(RakNet::BitStream* inStream, Entity* e return; } - for (CppScripts::Script* script : CppScripts::GetEntityScripts(offerer)) { - script->OnRespondToMission(offerer, missionID, EntityManager::Instance()->GetEntity(playerID), reward); - } + offerer->GetScript()->OnRespondToMission(offerer, missionID, EntityManager::Instance()->GetEntity(playerID), reward); } void GameMessages::HandleMissionDialogOK(RakNet::BitStream* inStream, Entity* entity) { @@ -5223,12 +5211,10 @@ void GameMessages::HandleMissionDialogOK(RakNet::BitStream* inStream, Entity* en inStream->Read(responder); player = EntityManager::Instance()->GetEntity(responder); - for (CppScripts::Script* script : CppScripts::GetEntityScripts(entity)) { - script->OnMissionDialogueOK(entity, player, missionID, iMissionState); - } + entity->GetScript()->OnMissionDialogueOK(entity, player, missionID, iMissionState); // Get the player's mission component - MissionComponent* missionComponent = static_cast(player->GetComponent(eReplicaComponentType::MISSION)); + auto* missionComponent = player->GetComponent(); if (!missionComponent) { Game::logger->Log("GameMessages", "Unable to get mission component for entity %llu to handle MissionDialogueOK", player->GetObjectID()); return; @@ -5252,7 +5238,7 @@ void GameMessages::HandleRequestLinkedMission(RakNet::BitStream* inStream, Entit auto* player = EntityManager::Instance()->GetEntity(playerId); - auto* missionOfferComponent = static_cast(entity->GetComponent(eReplicaComponentType::MISSION_OFFER)); + auto* missionOfferComponent = entity->GetComponent(); if (missionOfferComponent != nullptr) { missionOfferComponent->OfferMissions(player, 0); @@ -5264,9 +5250,11 @@ void GameMessages::HandleHasBeenCollected(RakNet::BitStream* inStream, Entity* e inStream->Read(playerID); Entity* player = EntityManager::Instance()->GetEntity(playerID); - if (!player || !entity || entity->GetCollectibleID() == 0) return; + if (!player || !entity) return; + auto* collectibleComponent = entity->GetComponent(); + if (!collectibleComponent || collectibleComponent->GetCollectibleId() == 0) return; - MissionComponent* missionComponent = static_cast(player->GetComponent(eReplicaComponentType::MISSION)); + auto* missionComponent = player->GetComponent(); if (missionComponent) { missionComponent->Progress(eMissionTaskType::COLLECTION, entity->GetLOT(), entity->GetObjectID()); } @@ -5319,6 +5307,7 @@ void GameMessages::HandlePickupCurrency(RakNet::BitStream* inStream, Entity* ent auto* ch = entity->GetCharacter(); if (entity->CanPickupCoins(currency)) { + entity->PickupCoins(currency); ch->SetCoins(ch->GetCoins() + currency, eLootSourceType::PICKUP); } } @@ -5369,7 +5358,7 @@ void GameMessages::HandleEquipItem(RakNet::BitStream* inStream, Entity* entity) inStream->Read(immediate); //twice? inStream->Read(objectID); - InventoryComponent* inv = static_cast(entity->GetComponent(eReplicaComponentType::INVENTORY)); + auto* inv = entity->GetComponent(); if (!inv) return; Item* item = inv->FindItemById(objectID); @@ -5388,7 +5377,7 @@ void GameMessages::HandleUnequipItem(RakNet::BitStream* inStream, Entity* entity inStream->Read(immediate); inStream->Read(objectID); - InventoryComponent* inv = static_cast(entity->GetComponent(eReplicaComponentType::INVENTORY)); + auto* inv = entity->GetComponent(); if (!inv) return; auto* item = inv->FindItemById(objectID); @@ -5463,7 +5452,7 @@ void GameMessages::HandleRemoveItemFromInventory(RakNet::BitStream* inStream, En inStream->Read(iTradeIDIsDefault); if (iTradeIDIsDefault) inStream->Read(iTradeID); - InventoryComponent* inv = static_cast(entity->GetComponent(eReplicaComponentType::INVENTORY)); + auto* inv = entity->GetComponent(); if (!inv) return; auto* item = inv->FindItemById(iObjID); @@ -5506,7 +5495,7 @@ void GameMessages::HandleMoveItemInInventory(RakNet::BitStream* inStream, Entity inStream->Read(responseCode); inStream->Read(slot); - InventoryComponent* inv = static_cast(entity->GetComponent(eReplicaComponentType::INVENTORY)); + auto* inv = entity->GetComponent(); if (!inv) return; auto* item = inv->FindItemById(iObjID); @@ -5538,7 +5527,7 @@ void GameMessages::HandleMoveItemBetweenInventoryTypes(RakNet::BitStream* inStre inStream->Read(templateIDIsDefault); if (templateIDIsDefault) inStream->Read(templateID); - auto inv = entity->GetComponent(); + auto* inv = entity->GetComponent(); if (!inv) return; auto* item = inv->FindItemById(objectID); @@ -5583,7 +5572,7 @@ void GameMessages::HandleModularBuildFinish(RakNet::BitStream* inStream, Entity* if (!user) return; Entity* character = EntityManager::Instance()->GetEntity(user->GetLoggedInChar()); if (!character) return; - InventoryComponent* inv = static_cast(character->GetComponent(eReplicaComponentType::INVENTORY)); + auto* inv = character->GetComponent(); if (!inv) return; Game::logger->Log("GameMessages", "Build finished"); @@ -5648,11 +5637,9 @@ void GameMessages::HandleModularBuildFinish(RakNet::BitStream* inStream, Entity* } } - ScriptComponent* script = static_cast(entity->GetComponent(eReplicaComponentType::SCRIPT)); + auto* script = entity->GetComponent(); - for (CppScripts::Script* script : CppScripts::GetEntityScripts(entity)) { - script->OnModularBuildExit(entity, character, count >= 3, modList); - } + entity->GetScript()->OnModularBuildExit(entity, character, count >= 3, modList); // Move remaining temp models back to models std::vector items; @@ -5671,7 +5658,7 @@ void GameMessages::HandleDoneArrangingWithItem(RakNet::BitStream* inStream, Enti if (!user) return; Entity* character = EntityManager::Instance()->GetEntity(user->GetLoggedInChar()); if (!character) return; - InventoryComponent* inv = static_cast(character->GetComponent(eReplicaComponentType::INVENTORY)); + auto* inv = character->GetComponent(); if (!inv) return; /** @@ -5789,7 +5776,7 @@ void GameMessages::HandleModularBuildMoveAndEquip(RakNet::BitStream* inStream, E inStream->Read(templateID); - InventoryComponent* inv = static_cast(character->GetComponent(eReplicaComponentType::INVENTORY)); + auto* inv = character->GetComponent(); if (!inv) return; auto* item = inv->FindItemByLot(templateID, TEMP_MODELS); @@ -5826,28 +5813,24 @@ void GameMessages::HandleResurrect(RakNet::BitStream* inStream, Entity* entity) bool immediate = inStream->ReadBit(); Entity* zoneControl = EntityManager::Instance()->GetZoneControlEntity(); - for (CppScripts::Script* script : CppScripts::GetEntityScripts(zoneControl)) { - script->OnPlayerResurrected(zoneControl, entity); - } + zoneControl->GetScript()->OnPlayerResurrected(zoneControl, entity); std::vector 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->OnPlayerResurrected(scriptEntity, entity); - } + scriptEntity->GetScript()->OnPlayerResurrected(scriptEntity, entity); } } } void GameMessages::HandlePushEquippedItemsState(RakNet::BitStream* inStream, Entity* entity) { - InventoryComponent* inv = static_cast(entity->GetComponent(eReplicaComponentType::INVENTORY)); + auto* inv = entity->GetComponent(); if (!inv) return; inv->PushEquippedItems(); } void GameMessages::HandlePopEquippedItemsState(RakNet::BitStream* inStream, Entity* entity) { - InventoryComponent* inv = static_cast(entity->GetComponent(eReplicaComponentType::INVENTORY)); + auto* inv = entity->GetComponent(); if (!inv) return; inv->PopEquippedItems(); EntityManager::Instance()->SerializeEntity(entity); // so it updates on client side @@ -5859,7 +5842,7 @@ void GameMessages::HandleClientItemConsumed(RakNet::BitStream* inStream, Entity* inStream->Read(itemConsumed); - auto* inventory = static_cast(entity->GetComponent(eReplicaComponentType::INVENTORY)); + auto* inventory = entity->GetComponent(); if (inventory == nullptr) { return; @@ -5873,7 +5856,7 @@ void GameMessages::HandleClientItemConsumed(RakNet::BitStream* inStream, Entity* item->Consume(); - auto* missions = static_cast(entity->GetComponent(eReplicaComponentType::MISSION)); + auto* missions = entity->GetComponent(); if (missions != nullptr) { missions->Progress(eMissionTaskType::USE_ITEM, itemLot); } @@ -5885,7 +5868,7 @@ void GameMessages::HandleUseNonEquipmentItem(RakNet::BitStream* inStream, Entity inStream->Read(itemConsumed); - auto* inv = static_cast(entity->GetComponent(eReplicaComponentType::INVENTORY)); + auto* inv = entity->GetComponent(); if (!inv) return; @@ -5920,7 +5903,7 @@ void GameMessages::HandleMatchRequest(RakNet::BitStream* inStream, Entity* entit if (type == 0) { // join if (value != 0) { for (Entity* scriptedAct : scriptedActs) { - ScriptedActivityComponent* comp = static_cast(scriptedAct->GetComponent(eReplicaComponentType::SCRIPTED_ACTIVITY)); + auto* comp = scriptedAct->GetComponent(); if (!comp) continue; if (comp->GetActivityID() == value) { comp->PlayerJoin(entity); @@ -5931,7 +5914,7 @@ void GameMessages::HandleMatchRequest(RakNet::BitStream* inStream, Entity* entit } } else if (type == 1) { // ready/unready for (Entity* scriptedAct : scriptedActs) { - ScriptedActivityComponent* comp = static_cast(scriptedAct->GetComponent(eReplicaComponentType::SCRIPTED_ACTIVITY)); + auto* comp = scriptedAct->GetComponent(); if (!comp) continue; if (comp->PlayerIsInQueue(entity)) { comp->PlayerReady(entity, value); @@ -6087,9 +6070,7 @@ void GameMessages::HandlePlayerRailArrivedNotification(RakNet::BitStream* inStre const auto possibleRails = EntityManager::Instance()->GetEntitiesByComponent(eReplicaComponentType::RAIL_ACTIVATOR); for (auto* possibleRail : possibleRails) { - for (CppScripts::Script* script : CppScripts::GetEntityScripts(possibleRail)) { - script->OnPlayerRailArrived(possibleRail, entity, pathName, waypointNumber); - } + possibleRail->GetScript()->OnPlayerRailArrived(possibleRail, entity, pathName, waypointNumber); } } @@ -6136,7 +6117,7 @@ void GameMessages::HandleUpdatePlayerStatistic(RakNet::BitStream* inStream, Enti } void GameMessages::HandleDeactivateBubbleBuff(RakNet::BitStream* inStream, Entity* entity) { - auto controllablePhysicsComponent = entity->GetComponent(); + auto* controllablePhysicsComponent = entity->GetComponent(); if (controllablePhysicsComponent) controllablePhysicsComponent->DeactivateBubbleBuff(); } @@ -6149,7 +6130,7 @@ void GameMessages::HandleActivateBubbleBuff(RakNet::BitStream* inStream, Entity* if (type == u"skunk") bubbleType = eBubbleType::SKUNK; else if (type == u"energy") bubbleType = eBubbleType::ENERGY; - auto controllablePhysicsComponent = entity->GetComponent(); + auto* controllablePhysicsComponent = entity->GetComponent(); if (controllablePhysicsComponent) controllablePhysicsComponent->ActivateBubbleBuff(bubbleType, specialAnimations); } diff --git a/dGame/dGameMessages/GameMessages.h b/dGame/dGameMessages/GameMessages.h index 94bdd3ea4..acd75229e 100644 --- a/dGame/dGameMessages/GameMessages.h +++ b/dGame/dGameMessages/GameMessages.h @@ -200,7 +200,7 @@ namespace GameMessages { // The success or failure response sent back to the client will preserve the same value for localID. void SendBBBSaveResponse(const LWOOBJID& objectId, const LWOOBJID& localID, unsigned char* buffer, uint32_t bufferSize, const SystemAddress& sysAddr); - void SendAddBuff(LWOOBJID& objectID, const LWOOBJID& casterID, uint32_t buffID, uint32_t msDuration, + void SendAddBuff(const LWOOBJID& objectID, const LWOOBJID& casterID, uint32_t buffID, uint32_t msDuration, bool addImmunity = false, bool cancelOnDamaged = false, bool cancelOnDeath = true, bool cancelOnLogout = false, bool cancelOnRemoveBuff = true, bool cancelOnUi = false, bool cancelOnUnequip = false, bool cancelOnZone = false, const SystemAddress& sysAddr = UNASSIGNED_SYSTEM_ADDRESS); diff --git a/dGame/dGameMessages/PropertySelectQueryProperty.cpp b/dGame/dGameMessages/PropertySelectQueryProperty.cpp index 6e9758795..5f575a7c9 100644 --- a/dGame/dGameMessages/PropertySelectQueryProperty.cpp +++ b/dGame/dGameMessages/PropertySelectQueryProperty.cpp @@ -3,22 +3,19 @@ void PropertySelectQueryProperty::Serialize(RakNet::BitStream& stream) const { stream.Write(CloneId); - const auto& owner = GeneralUtils::UTF8ToUTF16(OwnerName); - stream.Write(uint32_t(owner.size())); - for (uint32_t i = 0; i < owner.size(); ++i) { - stream.Write(static_cast(owner[i])); + stream.Write(OwnerName.size()); + for (const auto& c : OwnerName) { + stream.Write(c); } - const auto& name = GeneralUtils::UTF8ToUTF16(Name); - stream.Write(uint32_t(name.size())); - for (uint32_t i = 0; i < name.size(); ++i) { - stream.Write(static_cast(name[i])); + stream.Write(Name.size()); + for (const auto& c : Name) { + stream.Write(c); } - const auto& description = GeneralUtils::UTF8ToUTF16(Description); - stream.Write(uint32_t(description.size())); - for (uint32_t i = 0; i < description.size(); ++i) { - stream.Write(static_cast(description[i])); + stream.Write(Description.size()); + for (const auto& c : Description) { + stream.Write(c); } stream.Write(Reputation); @@ -32,7 +29,3 @@ void PropertySelectQueryProperty::Serialize(RakNet::BitStream& stream) const { stream.Write(PerformanceIndex); stream.Write(PerformanceCost); } - -void PropertySelectQueryProperty::Deserialize(RakNet::BitStream& stream) const { - // Do we need this? -} diff --git a/dGame/dGameMessages/PropertySelectQueryProperty.h b/dGame/dGameMessages/PropertySelectQueryProperty.h index 47d795a7f..c3e5082ee 100644 --- a/dGame/dGameMessages/PropertySelectQueryProperty.h +++ b/dGame/dGameMessages/PropertySelectQueryProperty.h @@ -1,7 +1,7 @@ #pragma once -#ifndef PROPERTYSELECTQUERY_H -#define PROPERTYSELECTQUERY_H +#ifndef __PROPERTYSELECTQUERY_H__ +#define __PROPERTYSELECTQUERY_H__ #include "Entity.h" @@ -10,12 +10,10 @@ class PropertySelectQueryProperty final public: void Serialize(RakNet::BitStream& stream) const; - void Deserialize(RakNet::BitStream& stream) const; - LWOCLONEID CloneId = LWOCLONEID_INVALID; // The cloneID of the property - std::string OwnerName = ""; // The property owners name - std::string Name = ""; // The property name - std::string Description = ""; // The property description + std::string OwnerName; // The property owners name + std::string Name; // The property name + std::string Description; // The property description float Reputation = 0; // The reputation of the property bool IsBestFriend = false; // Whether or not the property belongs to a best friend bool IsFriend = false; // Whether or not the property belongs to a friend @@ -28,4 +26,4 @@ class PropertySelectQueryProperty final uint32_t PerformanceIndex = 0; // The performance index of the property? Always 0? }; -#endif +#endif // __PROPERTYSELECTQUERY_H__ diff --git a/dGame/dInventory/EquippedItem.h b/dGame/dInventory/EquippedItem.h index 78da9e8d9..bf7ffcc51 100644 --- a/dGame/dInventory/EquippedItem.h +++ b/dGame/dInventory/EquippedItem.h @@ -6,8 +6,16 @@ /** * An item that's equipped, generally as a smaller return type than the regular Item class */ -struct EquippedItem -{ +struct EquippedItem { + EquippedItem() {} + + EquippedItem(const LWOOBJID& id, const LOT lot, const uint32_t count, const uint32_t slot, const std::vector& config = {}) { + this->id = id; + this->lot = lot; + this->count = count; + this->slot = slot; + this->config = config; + } /** * The object ID of the equipped item */ diff --git a/dGame/dInventory/Inventory.cpp b/dGame/dInventory/Inventory.cpp index 3d2c82aeb..6cf2e1f0a 100644 --- a/dGame/dInventory/Inventory.cpp +++ b/dGame/dInventory/Inventory.cpp @@ -76,7 +76,7 @@ void Inventory::SetSize(const uint32_t value) { size = value; - GameMessages::SendSetInventorySize(component->GetParent(), type, static_cast(size)); + GameMessages::SendSetInventorySize(component->GetParentEntity(), type, static_cast(size)); } int32_t Inventory::FindEmptySlot() { diff --git a/dGame/dInventory/Item.cpp b/dGame/dInventory/Item.cpp index 83ac88699..ea6dfa05c 100644 --- a/dGame/dInventory/Item.cpp +++ b/dGame/dInventory/Item.cpp @@ -92,7 +92,7 @@ Item::Item( inventory->AddManagedItem(this); - auto* entity = inventory->GetComponent()->GetParent(); + auto entity = inventory->GetComponent()->GetParentEntity(); GameMessages::SendAddItemToInventoryClientSync(entity, entity->GetSystemAddress(), this, id, showFlyingLoot, static_cast(this->count), subKey, lootSourceType); if (isModMoveAndEquip) { @@ -100,7 +100,7 @@ Item::Item( Game::logger->Log("Item", "Move and equipped (%i) from (%i)", this->lot, this->inventory->GetType()); - EntityManager::Instance()->SerializeEntity(inventory->GetComponent()->GetParent()); + EntityManager::Instance()->SerializeEntity(inventory->GetComponent()->GetParentEntity()); } } @@ -136,7 +136,7 @@ Inventory* Item::GetInventory() const { return inventory; } -LWOOBJID Item::GetParent() const { +LWOOBJID Item::GetParentEntity() const { return parent; } @@ -166,7 +166,7 @@ void Item::SetCount(const uint32_t value, const bool silent, const bool disassem } if (!silent) { - auto* entity = inventory->GetComponent()->GetParent(); + auto entity = inventory->GetComponent()->GetParentEntity(); if (value > count) { GameMessages::SendAddItemToInventoryClientSync(entity, entity->GetSystemAddress(), this, id, showFlyingLoot, delta, LWOOBJID_EMPTY, lootSourceType); @@ -231,7 +231,7 @@ void Item::UnEquip() { } bool Item::IsEquipped() const { - auto* component = inventory->GetComponent(); + auto component = inventory->GetComponent(); for (const auto& pair : component->GetEquippedItems()) { const auto item = pair.second; @@ -262,7 +262,7 @@ bool Item::Consume() { Game::logger->LogDebug("Item", "Consumed LOT (%i) itemID (%llu). Success=(%d)", lot, id, success); - GameMessages::SendUseItemResult(inventory->GetComponent()->GetParent(), lot, success); + GameMessages::SendUseItemResult(inventory->GetComponent()->GetParentEntity(), lot, success); if (success) { inventory->GetComponent()->RemoveItem(lot, 1); @@ -278,13 +278,13 @@ void Item::UseNonEquip(Item* item) { return; } - auto* playerInventoryComponent = GetInventory()->GetComponent(); + auto playerInventoryComponent = GetInventory()->GetComponent(); if (!playerInventoryComponent) { Game::logger->LogDebug("Item", "no inventory component attached to item id %llu lot %i", this->GetId(), this->GetLot()); return; } - auto* playerEntity = playerInventoryComponent->GetParent(); + auto* playerEntity = playerInventoryComponent->GetParentEntity(); if (!playerEntity) { Game::logger->LogDebug("Item", "no player entity attached to inventory? item id is %llu", this->GetId()); return; @@ -314,8 +314,8 @@ void Item::UseNonEquip(Item* item) { auto success = !packages.empty(); if (success) { - if (this->GetPreconditionExpression()->Check(playerInventoryComponent->GetParent())) { - auto* entityParent = playerInventoryComponent->GetParent(); + if (this->GetPreconditionExpression()->Check(playerInventoryComponent->GetParentEntity())) { + auto* entityParent = playerInventoryComponent->GetParentEntity(); // Roll the loot for all the packages then see if it all fits. If it fits, give it to the player, otherwise don't. std::unordered_map rolledLoot{}; for (auto& pack : packages) { @@ -331,15 +331,15 @@ void Item::UseNonEquip(Item* item) { } } if (playerInventoryComponent->HasSpaceForLoot(rolledLoot)) { - LootGenerator::Instance().GiveLoot(playerInventoryComponent->GetParent(), rolledLoot, eLootSourceType::CONSUMPTION); + LootGenerator::Instance().GiveLoot(playerInventoryComponent->GetParentEntity(), rolledLoot, eLootSourceType::CONSUMPTION); item->SetCount(item->GetCount() - 1); } else { success = false; } } else { GameMessages::SendUseItemRequirementsResponse( - playerInventoryComponent->GetParent()->GetObjectID(), - playerInventoryComponent->GetParent()->GetSystemAddress(), + playerInventoryComponent->GetParentEntity()->GetObjectID(), + playerInventoryComponent->GetParentEntity()->GetSystemAddress(), eUseItemResponse::FailedPrecondition ); success = false; @@ -347,7 +347,7 @@ void Item::UseNonEquip(Item* item) { } } Game::logger->LogDebug("Item", "Player %llu %s used item %i", playerEntity->GetObjectID(), success ? "successfully" : "unsuccessfully", thisLot); - GameMessages::SendUseItemResult(playerInventoryComponent->GetParent(), thisLot, success); + GameMessages::SendUseItemResult(playerInventoryComponent->GetParentEntity(), thisLot, success); } } @@ -360,7 +360,7 @@ void Item::Disassemble(const eInventoryType inventoryType) { if (GetInventory()) { auto inventoryComponent = GetInventory()->GetComponent(); if (inventoryComponent) { - auto entity = inventoryComponent->GetParent(); + auto entity = inventoryComponent->GetParentEntity(); if (entity) entity->SetVar(u"currentModifiedBuild", modStr); } } diff --git a/dGame/dInventory/Item.h b/dGame/dInventory/Item.h index be2359eff..722aee494 100644 --- a/dGame/dInventory/Item.h +++ b/dGame/dInventory/Item.h @@ -150,7 +150,7 @@ class Item final * Returns the parent of this item, e.g. for proxy items * @return the parent of this item */ - LWOOBJID GetParent() const; + LWOOBJID GetParentEntity() const; /** * Sets the subkey for this item, e.g. for pets diff --git a/dGame/dInventory/ItemSet.cpp b/dGame/dInventory/ItemSet.cpp index a8e587399..245ede351 100644 --- a/dGame/dInventory/ItemSet.cpp +++ b/dGame/dInventory/ItemSet.cpp @@ -15,7 +15,7 @@ ItemSet::ItemSet(const uint32_t id, InventoryComponent* inventoryComponent) { this->m_ID = id; this->m_InventoryComponent = inventoryComponent; - this->m_PassiveAbilities = ItemSetPassiveAbility::FindAbilities(id, m_InventoryComponent->GetParent(), this); + this->m_PassiveAbilities = ItemSetPassiveAbility::FindAbilities(id, m_InventoryComponent->GetParentEntity(), this); auto query = CDClientDatabase::CreatePreppedStmt( "SELECT skillSetWith2, skillSetWith3, skillSetWith4, skillSetWith5, skillSetWith6, itemIDs FROM ItemSets WHERE setID = ?;"); @@ -125,8 +125,8 @@ void ItemSet::OnEquip(const LOT lot) { return; } - auto* skillComponent = m_InventoryComponent->GetParent()->GetComponent(); - auto* missionComponent = m_InventoryComponent->GetParent()->GetComponent(); + auto* skillComponent = m_InventoryComponent->GetParentEntity()->GetComponent(); + auto* missionComponent = m_InventoryComponent->GetParentEntity()->GetComponent(); for (const auto skill : skillSet) { auto* skillTable = CDClientManager::Instance().GetTable(); @@ -135,7 +135,7 @@ void ItemSet::OnEquip(const LOT lot) { missionComponent->Progress(eMissionTaskType::USE_SKILL, skill); - skillComponent->HandleUnmanaged(behaviorId, m_InventoryComponent->GetParent()->GetObjectID()); + skillComponent->HandleUnmanaged(behaviorId, m_InventoryComponent->GetParentEntity()->GetObjectID()); } } @@ -158,14 +158,14 @@ void ItemSet::OnUnEquip(const LOT lot) { return; } - const auto& skillComponent = m_InventoryComponent->GetParent()->GetComponent(); + const auto* skillComponent = m_InventoryComponent->GetParentEntity()->GetComponent(); for (const auto skill : skillSet) { auto* skillTable = CDClientManager::Instance().GetTable(); const auto behaviorId = skillTable->GetSkillByID(skill).behaviorID; - skillComponent->HandleUnCast(behaviorId, m_InventoryComponent->GetParent()->GetObjectID()); + skillComponent->HandleUnCast(behaviorId, m_InventoryComponent->GetParentEntity()->GetObjectID()); } } diff --git a/dGame/dMission/Mission.cpp b/dGame/dMission/Mission.cpp index 32a930e4d..7d84d9588 100644 --- a/dGame/dMission/Mission.cpp +++ b/dGame/dMission/Mission.cpp @@ -204,7 +204,7 @@ bool Mission::IsValidMission(const uint32_t missionId, CDMissions& info) { } Entity* Mission::GetAssociate() const { - return m_MissionComponent->GetParent(); + return m_MissionComponent->GetParentEntity(); } User* Mission::GetUser() const { @@ -372,7 +372,7 @@ void Mission::CheckCompletion() { void Mission::Catchup() { auto* entity = GetAssociate(); - auto* inventory = static_cast(entity->GetComponent(eReplicaComponentType::INVENTORY)); + auto* inventory = entity->GetComponent(); for (auto* task : m_Tasks) { const auto type = task->GetType(); @@ -532,7 +532,7 @@ void Mission::YieldRewards() { if (info->reward_reputation > 0) { missionComponent->Progress(eMissionTaskType::EARN_REPUTATION, 0, 0L, "", info->reward_reputation); - auto character = entity->GetComponent(); + auto* character = entity->GetComponent(); if (character) { character->SetReputation(character->GetReputation() + info->reward_reputation); GameMessages::SendUpdateReputation(entity->GetObjectID(), character->GetReputation(), entity->GetSystemAddress()); diff --git a/dGame/dMission/MissionTask.cpp b/dGame/dMission/MissionTask.cpp index 344427c6b..fff59cea4 100644 --- a/dGame/dMission/MissionTask.cpp +++ b/dGame/dMission/MissionTask.cpp @@ -15,6 +15,7 @@ #include "MissionComponent.h" #include "eMissionTaskType.h" #include "eReplicaComponentType.h" +#include "CollectibleComponent.h" MissionTask::MissionTask(Mission* mission, CDMissionTasks* info, uint32_t mask) { this->info = info; @@ -238,7 +239,7 @@ void MissionTask::Progress(int32_t value, LWOOBJID associate, const std::string& break; } - activity = static_cast(entity->GetComponent(eReplicaComponentType::QUICK_BUILD)); + activity = entity->GetComponent(); if (activity == nullptr) { break; } @@ -353,8 +354,9 @@ void MissionTask::Progress(int32_t value, LWOOBJID associate, const std::string& break; } - - collectionId = entity->GetCollectibleID(); + auto* collectibleComponent = entity->GetComponent(); + if (!collectibleComponent) break; + collectionId = collectibleComponent->GetCollectibleId(); collectionId = static_cast(collectionId) + static_cast(Game::server->GetZoneID() << 8); diff --git a/dGame/dPropertyBehaviors/ControlBehaviors.cpp b/dGame/dPropertyBehaviors/ControlBehaviors.cpp index d8a062ca5..52c097466 100644 --- a/dGame/dPropertyBehaviors/ControlBehaviors.cpp +++ b/dGame/dPropertyBehaviors/ControlBehaviors.cpp @@ -4,7 +4,7 @@ #include "Entity.h" #include "Game.h" #include "GameMessages.h" -#include "ModelComponent.h" +#include "ModelBehaviorComponent.h" #include "../../dWorldServer/ObjectIDManager.h" #include "dLogger.h" #include "BehaviorStates.h" @@ -30,7 +30,7 @@ #include "UpdateActionMessage.h" #include "UpdateStripUiMessage.h" -void ControlBehaviors::RequestUpdatedID(int32_t behaviorID, ModelComponent* modelComponent, Entity* modelOwner, const SystemAddress& sysAddr) { +void ControlBehaviors::RequestUpdatedID(int32_t behaviorID, ModelBehaviorComponent* modelComponent, Entity* modelOwner, const SystemAddress& sysAddr) { // auto behavior = modelComponent->FindBehavior(behaviorID); // if (behavior->GetBehaviorID() == -1 || behavior->GetShouldSetNewID()) { // ObjectIDManager::Instance()->RequestPersistentID( @@ -47,17 +47,17 @@ void ControlBehaviors::RequestUpdatedID(int32_t behaviorID, ModelComponent* mode // args.InsertValue("behaviorID", behaviorIDString); // AMFStringValue* objectIDAsString = new AMFStringValue(); - // objectIDAsString->SetValue(std::to_string(modelComponent->GetParent()->GetObjectID())); + // objectIDAsString->SetValue(std::to_string(modelComponent->GetParentEntity()->GetObjectID())); // args.InsertValue("objectID", objectIDAsString); // GameMessages::SendUIMessageServerToSingleClient(modelOwner, sysAddr, "UpdateBehaviorID", &args); - // ControlBehaviors::SendBehaviorListToClient(modelComponent->GetParent(), sysAddr, modelOwner); + // ControlBehaviors::SendBehaviorListToClient(modelComponent->GetParentEntity(), sysAddr, modelOwner); // }); // } } void ControlBehaviors::SendBehaviorListToClient(Entity* modelEntity, const SystemAddress& sysAddr, Entity* modelOwner) { - auto* modelComponent = modelEntity->GetComponent(); + auto* modelComponent = modelEntity->GetComponent(); if (!modelComponent) return; @@ -74,12 +74,12 @@ void ControlBehaviors::SendBehaviorListToClient(Entity* modelEntity, const Syste */ behaviorsToSerialize.Insert("behaviors"); - behaviorsToSerialize.Insert("objectID", std::to_string(modelComponent->GetParent()->GetObjectID())); + behaviorsToSerialize.Insert("objectID", std::to_string(modelComponent->GetParentEntity()->GetObjectID())); GameMessages::SendUIMessageServerToSingleClient(modelOwner, sysAddr, "UpdateBehaviorList", behaviorsToSerialize); } -void ControlBehaviors::ModelTypeChanged(AMFArrayValue* arguments, ModelComponent* ModelComponent) { +void ControlBehaviors::ModelTypeChanged(AMFArrayValue* arguments, ModelBehaviorComponent* ModelComponent) { auto* modelTypeAmf = arguments->Get("ModelType"); if (!modelTypeAmf) return; @@ -137,7 +137,7 @@ void ControlBehaviors::Rename(Entity* modelEntity, const SystemAddress& sysAddr, } // TODO This is also supposed to serialize the state of the behaviors in progress but those aren't implemented yet -void ControlBehaviors::SendBehaviorBlocksToClient(ModelComponent* modelComponent, const SystemAddress& sysAddr, Entity* modelOwner, AMFArrayValue* arguments) { +void ControlBehaviors::SendBehaviorBlocksToClient(ModelBehaviorComponent* modelComponent, const SystemAddress& sysAddr, Entity* modelOwner, AMFArrayValue* arguments) { // uint32_t behaviorID = ControlBehaviors::GetBehaviorIDFromArgument(arguments); // auto modelBehavior = modelComponent->FindBehavior(behaviorID); @@ -197,7 +197,7 @@ void ControlBehaviors::SendBehaviorBlocksToClient(ModelComponent* modelComponent // uiArray->InsertValue("x", xPosition); // thisStrip->InsertValue("ui", uiArray); - // targetObjectID = modelComponent->GetParent()->GetObjectID(); + // targetObjectID = modelComponent->GetParentEntity()->GetObjectID(); // behaviorID = modelBehavior->GetBehaviorID(); // AMFArrayValue* stripSerialize = new AMFArrayValue(); @@ -266,7 +266,7 @@ void ControlBehaviors::UpdateAction(AMFArrayValue* arguments) { } } -void ControlBehaviors::MoveToInventory(ModelComponent* modelComponent, const SystemAddress& sysAddr, Entity* modelOwner, AMFArrayValue* arguments) { +void ControlBehaviors::MoveToInventory(ModelBehaviorComponent* modelComponent, const SystemAddress& sysAddr, Entity* modelOwner, AMFArrayValue* arguments) { // This closes the UI menu should it be open while the player is removing behaviors AMFArrayValue args; @@ -276,12 +276,12 @@ void ControlBehaviors::MoveToInventory(ModelComponent* modelComponent, const Sys MoveToInventoryMessage moveToInventoryMessage(arguments); - SendBehaviorListToClient(modelComponent->GetParent(), sysAddr, modelOwner); + SendBehaviorListToClient(modelComponent->GetParentEntity(), sysAddr, modelOwner); } void ControlBehaviors::ProcessCommand(Entity* modelEntity, const SystemAddress& sysAddr, AMFArrayValue* arguments, std::string command, Entity* modelOwner) { if (!isInitialized || !modelEntity || !modelOwner || !arguments) return; - auto* modelComponent = modelEntity->GetComponent(); + auto* modelComponent = modelEntity->GetComponent(); if (!modelComponent) return; @@ -342,7 +342,7 @@ ControlBehaviors::ControlBehaviors() { std::string buffer{}; bool commentBlockStart = false; while (std::getline(blocksBuffer, read)) { - // tinyxml2 should handle comment blocks but the client has one that fails the processing. + // tinyxml2 should handle comment blocks but the client has one that fails the processing. // This preprocessing just removes all comments from the read file out of an abundance of caution. if (read.find("