From fc7a2742d0f893da6e0c98962057cc69bd7ab52d Mon Sep 17 00:00:00 2001 From: David Markowitz Date: Thu, 12 Dec 2024 02:00:21 -0800 Subject: [PATCH 1/3] brother --- dGame/Entity.cpp | 34 ++++++-- dGame/Entity.h | 2 + dGame/dComponents/BaseCombatAIComponent.cpp | 87 +++++++++++-------- dGame/dComponents/BaseCombatAIComponent.h | 8 ++ .../ControllablePhysicsComponent.cpp | 2 +- .../ControllablePhysicsComponent.h | 2 +- .../HavokVehiclePhysicsComponent.cpp | 2 +- .../HavokVehiclePhysicsComponent.h | 2 +- dGame/dComponents/PhantomPhysicsComponent.cpp | 2 +- dGame/dComponents/PhantomPhysicsComponent.h | 2 +- dGame/dComponents/PhysicsComponent.cpp | 17 +++- dGame/dComponents/PhysicsComponent.h | 7 +- .../RigidbodyPhantomPhysicsComponent.cpp | 2 +- .../RigidbodyPhantomPhysicsComponent.h | 2 +- dGame/dComponents/SimplePhysicsComponent.cpp | 2 +- dGame/dComponents/SimplePhysicsComponent.h | 2 +- dScripts/02_server/Map/General/CMakeLists.txt | 1 + .../Map/General/EnemyClearThreat.cpp | 25 ++++++ .../02_server/Map/General/EnemyClearThreat.h | 11 +++ dScripts/CppScripts.cpp | 24 +++-- 20 files changed, 173 insertions(+), 63 deletions(-) create mode 100644 dScripts/02_server/Map/General/EnemyClearThreat.cpp create mode 100644 dScripts/02_server/Map/General/EnemyClearThreat.h diff --git a/dGame/Entity.cpp b/dGame/Entity.cpp index 59f6e0e08..f5887996d 100644 --- a/dGame/Entity.cpp +++ b/dGame/Entity.cpp @@ -97,6 +97,8 @@ #include "CDSkillBehaviorTable.h" #include "CDZoneTableTable.h" +#include + Observable Entity::OnPlayerPositionUpdate; Entity::Entity(const LWOOBJID& objectID, EntityInfo info, User* parentUser, Entity* parentEntity) { @@ -286,8 +288,9 @@ void Entity::Initialize() { AddComponent(propertyEntranceComponentID); } - if (compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::CONTROLLABLE_PHYSICS) > 0) { - auto* controllablePhysics = AddComponent(); + const int32_t controllablePhysicsComponentID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::CONTROLLABLE_PHYSICS); + if (controllablePhysicsComponentID > 0) { + auto* controllablePhysics = AddComponent(controllablePhysicsComponentID); if (m_Character) { controllablePhysics->LoadFromXml(m_Character->GetXMLDoc()); @@ -330,16 +333,19 @@ void Entity::Initialize() { AddComponent(simplePhysicsComponentID); } - if (compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::RIGID_BODY_PHANTOM_PHYSICS) > 0) { - AddComponent(); + const int32_t rigidBodyPhantomPhysicsComponentID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::RIGID_BODY_PHANTOM_PHYSICS); + if (rigidBodyPhantomPhysicsComponentID > 0) { + AddComponent(rigidBodyPhantomPhysicsComponentID); } - if (markedAsPhantom || compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::PHANTOM_PHYSICS) > 0) { - AddComponent()->SetPhysicsEffectActive(false); + const int32_t phantomPhysicsComponentID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::PHANTOM_PHYSICS); + if (markedAsPhantom || phantomPhysicsComponentID > 0) { + AddComponent(phantomPhysicsComponentID)->SetPhysicsEffectActive(false); } - if (compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::HAVOK_VEHICLE_PHYSICS) > 0) { - auto* havokVehiclePhysicsComponent = AddComponent(); + const int32_t havokVehiclePhysicsComponentID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::HAVOK_VEHICLE_PHYSICS); + if (havokVehiclePhysicsComponentID > 0) { + auto* havokVehiclePhysicsComponent = AddComponent(havokVehiclePhysicsComponentID); havokVehiclePhysicsComponent->SetPosition(m_DefaultPosition); havokVehiclePhysicsComponent->SetRotation(m_DefaultRotation); } @@ -2161,7 +2167,19 @@ void Entity::SetRespawnPos(const NiPoint3& position) { auto* characterComponent = GetComponent(); if (characterComponent) characterComponent->SetRespawnPos(position); } + void Entity::SetRespawnRot(const NiQuaternion& rotation) { auto* characterComponent = GetComponent(); if (characterComponent) characterComponent->SetRespawnRot(rotation); } + +int32_t Entity::GetCollisionGroup() const { + for (const auto* component : m_Components | std::views::values) { + auto* compToCheck = dynamic_cast(component); + if (compToCheck) { + return compToCheck->GetCollisionGroup(); + } + } + + return 0; +} diff --git a/dGame/Entity.h b/dGame/Entity.h index 5d2b95272..bb32e50e2 100644 --- a/dGame/Entity.h +++ b/dGame/Entity.h @@ -107,6 +107,8 @@ class Entity { const SystemAddress& GetSystemAddress() const; + int32_t GetCollisionGroup() const; + /** * Setters */ diff --git a/dGame/dComponents/BaseCombatAIComponent.cpp b/dGame/dComponents/BaseCombatAIComponent.cpp index bfb0bbfa8..8d2db8b6e 100644 --- a/dGame/dComponents/BaseCombatAIComponent.cpp +++ b/dGame/dComponents/BaseCombatAIComponent.cpp @@ -27,7 +27,7 @@ #include "CDPhysicsComponentTable.h" #include "dNavMesh.h" -BaseCombatAIComponent::BaseCombatAIComponent(Entity* parent, const uint32_t id): Component(parent) { +BaseCombatAIComponent::BaseCombatAIComponent(Entity* parent, const uint32_t id) : Component(parent) { m_Target = LWOOBJID_EMPTY; m_DirtyStateOrTarget = true; m_State = AiState::spawn; @@ -37,6 +37,7 @@ BaseCombatAIComponent::BaseCombatAIComponent(Entity* parent, const uint32_t id): m_Disabled = false; m_SkillEntries = {}; m_SoftTimer = 5.0f; + m_ForcedTetherTime = 0.0f; //Grab the aggro information from BaseCombatAI: auto componentQuery = CDClientDatabase::CreatePreppedStmt( @@ -170,6 +171,8 @@ void BaseCombatAIComponent::Update(const float deltaTime) { GameMessages::SendStopFXEffect(m_Parent, true, "tether"); m_TetherEffectActive = false; } + m_ForcedTetherTime -= deltaTime; + if (m_ForcedTetherTime >= 0) return; } if (m_SoftTimer <= 0.0f) { @@ -287,40 +290,7 @@ void BaseCombatAIComponent::CalculateCombat(const float deltaTime) { } if (!m_TetherEffectActive && m_OutOfCombat && (m_OutOfCombatTime -= deltaTime) <= 0) { - auto* destroyableComponent = m_Parent->GetComponent(); - - if (destroyableComponent != nullptr && destroyableComponent->HasFaction(4)) { - auto serilizationRequired = false; - - if (destroyableComponent->GetHealth() != destroyableComponent->GetMaxHealth()) { - destroyableComponent->SetHealth(destroyableComponent->GetMaxHealth()); - - serilizationRequired = true; - } - - if (destroyableComponent->GetArmor() != destroyableComponent->GetMaxArmor()) { - destroyableComponent->SetArmor(destroyableComponent->GetMaxArmor()); - - serilizationRequired = true; - } - - if (serilizationRequired) { - Game::entityManager->SerializeEntity(m_Parent); - } - - GameMessages::SendPlayFXEffect(m_Parent->GetObjectID(), 6270, u"tether", "tether"); - - m_TetherEffectActive = true; - - m_TetherTime = 3.0f; - } - - // Speed towards start position - if (m_MovementAI != nullptr) { - m_MovementAI->SetHaltDistance(0); - m_MovementAI->SetMaxSpeed(m_PursuitSpeed); - m_MovementAI->SetDestination(m_StartPosition); - } + TetherLogic(); m_OutOfCombat = false; m_OutOfCombatTime = 0.0f; @@ -626,6 +596,7 @@ const NiPoint3& BaseCombatAIComponent::GetStartPosition() const { void BaseCombatAIComponent::ClearThreat() { m_ThreatEntries.clear(); + m_Target = LWOOBJID_EMPTY; m_DirtyThreat = true; } @@ -806,3 +777,49 @@ void BaseCombatAIComponent::Wake() { m_dpEntity->SetSleeping(false); m_dpEntityEnemy->SetSleeping(false); } + +void BaseCombatAIComponent::TetherLogic() { + auto* destroyableComponent = m_Parent->GetComponent(); + + if (destroyableComponent != nullptr && destroyableComponent->HasFaction(4)) { + auto serilizationRequired = false; + + if (destroyableComponent->GetHealth() != destroyableComponent->GetMaxHealth()) { + destroyableComponent->SetHealth(destroyableComponent->GetMaxHealth()); + + serilizationRequired = true; + } + + if (destroyableComponent->GetArmor() != destroyableComponent->GetMaxArmor()) { + destroyableComponent->SetArmor(destroyableComponent->GetMaxArmor()); + + serilizationRequired = true; + } + + if (serilizationRequired) { + Game::entityManager->SerializeEntity(m_Parent); + } + + GameMessages::SendPlayFXEffect(m_Parent->GetObjectID(), 6270, u"tether", "tether"); + + m_TetherEffectActive = true; + + m_TetherTime = 3.0f; + } + + // Speed towards start position + if (m_MovementAI != nullptr) { + m_MovementAI->SetHaltDistance(0); + m_MovementAI->SetMaxSpeed(m_PursuitSpeed); + m_MovementAI->SetDestination(m_StartPosition); + } +} + +void BaseCombatAIComponent::ForceTether() { + SetTarget(LWOOBJID_EMPTY); + m_ThreatEntries.clear(); + TetherLogic(); + m_ForcedTetherTime = m_TetherTime; + + SetAiState(AiState::aggro); +} diff --git a/dGame/dComponents/BaseCombatAIComponent.h b/dGame/dComponents/BaseCombatAIComponent.h index 89985d647..ae2350c59 100644 --- a/dGame/dComponents/BaseCombatAIComponent.h +++ b/dGame/dComponents/BaseCombatAIComponent.h @@ -224,6 +224,12 @@ class BaseCombatAIComponent final : public Component { */ void Wake(); + void ForceTether(); + + // heals the entity to full health and armor + // and tethers them to their spawn point + void TetherLogic(); + private: /** * Returns the current target or the target that currently is the largest threat to this entity @@ -382,6 +388,8 @@ class BaseCombatAIComponent final : public Component { */ bool m_DirtyStateOrTarget = false; + float m_ForcedTetherTime = 0.0f; + /** * Whether the current entity is a mech enemy, needed as mechs tether radius works differently * @return whether this entity is a mech diff --git a/dGame/dComponents/ControllablePhysicsComponent.cpp b/dGame/dComponents/ControllablePhysicsComponent.cpp index 18e4b19d4..1a05020b5 100644 --- a/dGame/dComponents/ControllablePhysicsComponent.cpp +++ b/dGame/dComponents/ControllablePhysicsComponent.cpp @@ -15,7 +15,7 @@ #include "LevelProgressionComponent.h" #include "eStateChangeType.h" -ControllablePhysicsComponent::ControllablePhysicsComponent(Entity* entity) : PhysicsComponent(entity) { +ControllablePhysicsComponent::ControllablePhysicsComponent(Entity* entity, int32_t componentId) : PhysicsComponent(entity, componentId) { m_Velocity = {}; m_AngularVelocity = {}; m_InJetpackMode = false; diff --git a/dGame/dComponents/ControllablePhysicsComponent.h b/dGame/dComponents/ControllablePhysicsComponent.h index 6309b8fc3..31a6bb301 100644 --- a/dGame/dComponents/ControllablePhysicsComponent.h +++ b/dGame/dComponents/ControllablePhysicsComponent.h @@ -23,7 +23,7 @@ class ControllablePhysicsComponent : public PhysicsComponent { public: static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::CONTROLLABLE_PHYSICS; - ControllablePhysicsComponent(Entity* entity); + ControllablePhysicsComponent(Entity* entity, int32_t componentId); ~ControllablePhysicsComponent() override; void Update(float deltaTime) override; diff --git a/dGame/dComponents/HavokVehiclePhysicsComponent.cpp b/dGame/dComponents/HavokVehiclePhysicsComponent.cpp index 635830cc5..e977f952d 100644 --- a/dGame/dComponents/HavokVehiclePhysicsComponent.cpp +++ b/dGame/dComponents/HavokVehiclePhysicsComponent.cpp @@ -1,7 +1,7 @@ #include "HavokVehiclePhysicsComponent.h" #include "EntityManager.h" -HavokVehiclePhysicsComponent::HavokVehiclePhysicsComponent(Entity* parent) : PhysicsComponent(parent) { +HavokVehiclePhysicsComponent::HavokVehiclePhysicsComponent(Entity* parent, int32_t componentId) : PhysicsComponent(parent, componentId) { m_Velocity = NiPoint3Constant::ZERO; m_AngularVelocity = NiPoint3Constant::ZERO; m_IsOnGround = true; diff --git a/dGame/dComponents/HavokVehiclePhysicsComponent.h b/dGame/dComponents/HavokVehiclePhysicsComponent.h index 83eb82fe6..ad6087a7d 100644 --- a/dGame/dComponents/HavokVehiclePhysicsComponent.h +++ b/dGame/dComponents/HavokVehiclePhysicsComponent.h @@ -13,7 +13,7 @@ class HavokVehiclePhysicsComponent : public PhysicsComponent { public: static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::HAVOK_VEHICLE_PHYSICS; - HavokVehiclePhysicsComponent(Entity* parentEntity); + HavokVehiclePhysicsComponent(Entity* parentEntity, int32_t componentId); void Serialize(RakNet::BitStream& outBitStream, bool bIsInitialUpdate) override; diff --git a/dGame/dComponents/PhantomPhysicsComponent.cpp b/dGame/dComponents/PhantomPhysicsComponent.cpp index 95fed36e6..be7fe7746 100644 --- a/dGame/dComponents/PhantomPhysicsComponent.cpp +++ b/dGame/dComponents/PhantomPhysicsComponent.cpp @@ -27,7 +27,7 @@ #include "dpShapeBox.h" #include "dpShapeSphere.h" -PhantomPhysicsComponent::PhantomPhysicsComponent(Entity* parent) : PhysicsComponent(parent) { +PhantomPhysicsComponent::PhantomPhysicsComponent(Entity* parent, int32_t componentId) : PhysicsComponent(parent, componentId) { m_Position = m_Parent->GetDefaultPosition(); m_Rotation = m_Parent->GetDefaultRotation(); m_Scale = m_Parent->GetDefaultScale(); diff --git a/dGame/dComponents/PhantomPhysicsComponent.h b/dGame/dComponents/PhantomPhysicsComponent.h index 89cfb857c..cd54587be 100644 --- a/dGame/dComponents/PhantomPhysicsComponent.h +++ b/dGame/dComponents/PhantomPhysicsComponent.h @@ -30,7 +30,7 @@ class PhantomPhysicsComponent final : public PhysicsComponent { public: static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::PHANTOM_PHYSICS; - PhantomPhysicsComponent(Entity* parent); + PhantomPhysicsComponent(Entity* parent, int32_t componentId); ~PhantomPhysicsComponent() override; void Update(float deltaTime) override; void Serialize(RakNet::BitStream& outBitStream, bool bIsInitialUpdate) override; diff --git a/dGame/dComponents/PhysicsComponent.cpp b/dGame/dComponents/PhysicsComponent.cpp index 4a250a6a9..3fc13fe7f 100644 --- a/dGame/dComponents/PhysicsComponent.cpp +++ b/dGame/dComponents/PhysicsComponent.cpp @@ -14,10 +14,25 @@ #include "EntityInfo.h" -PhysicsComponent::PhysicsComponent(Entity* parent) : Component(parent) { +PhysicsComponent::PhysicsComponent(Entity* parent, int32_t componentId) : Component(parent) { m_Position = NiPoint3Constant::ZERO; m_Rotation = NiQuaternionConstant::IDENTITY; m_DirtyPosition = false; + + CDPhysicsComponentTable* physicsComponentTable = CDClientManager::GetTable(); + + if (physicsComponentTable) { + auto* info = physicsComponentTable->GetByID(componentId); + if (info) { + m_CollisionGroup = info->collisionGroup; + } + } + + if (m_Parent->HasVar(u"CollisionGroupID")) m_CollisionGroup = m_Parent->GetVar(u"CollisionGroupID"); + if (m_Parent->GetLOT() == 8419) { + auto [x, y, z] = m_Parent->GetDefaultPosition(); + LOG("PhysicsComponent: %f %f %f", x, y, z); + } } void PhysicsComponent::Serialize(RakNet::BitStream& outBitStream, bool bIsInitialUpdate) { diff --git a/dGame/dComponents/PhysicsComponent.h b/dGame/dComponents/PhysicsComponent.h index 4bf0828a0..41a4b9d15 100644 --- a/dGame/dComponents/PhysicsComponent.h +++ b/dGame/dComponents/PhysicsComponent.h @@ -15,7 +15,7 @@ class dpEntity; class PhysicsComponent : public Component { public: - PhysicsComponent(Entity* parent); + PhysicsComponent(Entity* parent, int32_t componentId); virtual ~PhysicsComponent() = default; void Serialize(RakNet::BitStream& outBitStream, bool bIsInitialUpdate) override; @@ -25,6 +25,9 @@ class PhysicsComponent : public Component { const NiQuaternion& GetRotation() const { return m_Rotation; } virtual void SetRotation(const NiQuaternion& rot) { if (m_Rotation == rot) return; m_Rotation = rot; m_DirtyPosition = true; } + + int32_t GetCollisionGroup() const noexcept { return m_CollisionGroup; } + void SetCollisionGroup(int32_t group) noexcept { m_CollisionGroup = group; } protected: dpEntity* CreatePhysicsEntity(eReplicaComponentType type); @@ -37,6 +40,8 @@ class PhysicsComponent : public Component { NiQuaternion m_Rotation; bool m_DirtyPosition; + + int32_t m_CollisionGroup{}; }; #endif //!__PHYSICSCOMPONENT__H__ diff --git a/dGame/dComponents/RigidbodyPhantomPhysicsComponent.cpp b/dGame/dComponents/RigidbodyPhantomPhysicsComponent.cpp index df81aab36..d0f8caeb2 100644 --- a/dGame/dComponents/RigidbodyPhantomPhysicsComponent.cpp +++ b/dGame/dComponents/RigidbodyPhantomPhysicsComponent.cpp @@ -12,7 +12,7 @@ #include "dpShapeSphere.h" #include"EntityInfo.h" -RigidbodyPhantomPhysicsComponent::RigidbodyPhantomPhysicsComponent(Entity* parent) : PhysicsComponent(parent) { +RigidbodyPhantomPhysicsComponent::RigidbodyPhantomPhysicsComponent(Entity* parent, int32_t componentId) : PhysicsComponent(parent, componentId) { m_Position = m_Parent->GetDefaultPosition(); m_Rotation = m_Parent->GetDefaultRotation(); m_Scale = m_Parent->GetDefaultScale(); diff --git a/dGame/dComponents/RigidbodyPhantomPhysicsComponent.h b/dGame/dComponents/RigidbodyPhantomPhysicsComponent.h index 11595ec03..e7ce93d64 100644 --- a/dGame/dComponents/RigidbodyPhantomPhysicsComponent.h +++ b/dGame/dComponents/RigidbodyPhantomPhysicsComponent.h @@ -21,7 +21,7 @@ class RigidbodyPhantomPhysicsComponent : public PhysicsComponent { public: static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::RIGID_BODY_PHANTOM_PHYSICS; - RigidbodyPhantomPhysicsComponent(Entity* parent); + RigidbodyPhantomPhysicsComponent(Entity* parent, int32_t componentId); void Update(const float deltaTime) override; diff --git a/dGame/dComponents/SimplePhysicsComponent.cpp b/dGame/dComponents/SimplePhysicsComponent.cpp index 6bc2e2bc9..825570f2d 100644 --- a/dGame/dComponents/SimplePhysicsComponent.cpp +++ b/dGame/dComponents/SimplePhysicsComponent.cpp @@ -13,7 +13,7 @@ #include "Entity.h" -SimplePhysicsComponent::SimplePhysicsComponent(Entity* parent, uint32_t componentID) : PhysicsComponent(parent) { +SimplePhysicsComponent::SimplePhysicsComponent(Entity* parent, int32_t componentID) : PhysicsComponent(parent, componentID) { m_Position = m_Parent->GetDefaultPosition(); m_Rotation = m_Parent->GetDefaultRotation(); diff --git a/dGame/dComponents/SimplePhysicsComponent.h b/dGame/dComponents/SimplePhysicsComponent.h index b4491e127..61362712d 100644 --- a/dGame/dComponents/SimplePhysicsComponent.h +++ b/dGame/dComponents/SimplePhysicsComponent.h @@ -30,7 +30,7 @@ class SimplePhysicsComponent : public PhysicsComponent { public: static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::SIMPLE_PHYSICS; - SimplePhysicsComponent(Entity* parent, uint32_t componentID); + SimplePhysicsComponent(Entity* parent, int32_t componentID); ~SimplePhysicsComponent() override; void Serialize(RakNet::BitStream& outBitStream, bool bIsInitialUpdate) override; diff --git a/dScripts/02_server/Map/General/CMakeLists.txt b/dScripts/02_server/Map/General/CMakeLists.txt index 3379e5b00..dc32d53c5 100644 --- a/dScripts/02_server/Map/General/CMakeLists.txt +++ b/dScripts/02_server/Map/General/CMakeLists.txt @@ -2,6 +2,7 @@ set(DSCRIPTS_SOURCES_02_SERVER_MAP_GENERAL "BankInteractServer.cpp" "BaseInteractDropLootServer.cpp" "Binoculars.cpp" + "EnemyClearThreat.cpp" "ExplodingAsset.cpp" "FrictionVolumeServer.cpp" "ForceVolumeServer.cpp" diff --git a/dScripts/02_server/Map/General/EnemyClearThreat.cpp b/dScripts/02_server/Map/General/EnemyClearThreat.cpp new file mode 100644 index 000000000..06aa80629 --- /dev/null +++ b/dScripts/02_server/Map/General/EnemyClearThreat.cpp @@ -0,0 +1,25 @@ +#include "EnemyClearThreat.h" + +#include "BaseCombatAIComponent.h" +#include "PhysicsComponent.h" + +void EnemyClearThreat::OnCollisionPhantom(Entity* self, Entity* target) { + if (!target) return; + + const auto colGroup = target->GetCollisionGroup(); + if (colGroup == 12) { // enemy + auto* const baseCombatAiComponent = target->GetComponent(); + if (!baseCombatAiComponent) return; + + baseCombatAiComponent->ClearThreat(); + baseCombatAiComponent->ForceTether(); + } else if (colGroup == 10) { // player + const auto enemies = Game::entityManager->GetEntitiesByComponent(eReplicaComponentType::BASE_COMBAT_AI); + for (const auto& enemy : enemies) { + auto* const baseCombatAiComponent = enemy->GetComponent(); + if (!baseCombatAiComponent) continue; + + baseCombatAiComponent->SetThreat(target->GetObjectID(), 0.0f); + } + } +} diff --git a/dScripts/02_server/Map/General/EnemyClearThreat.h b/dScripts/02_server/Map/General/EnemyClearThreat.h new file mode 100644 index 000000000..97cce4268 --- /dev/null +++ b/dScripts/02_server/Map/General/EnemyClearThreat.h @@ -0,0 +1,11 @@ +#ifndef ENEMYCLEARTHREAT_H +#define ENEMYCLEARTHREAT_H + +#include "CppScripts.h" + +class EnemyClearThreat : public CppScripts::Script { +public: + void OnCollisionPhantom(Entity* self, Entity* target) override; +}; + +#endif //!ENEMYCLEARTHREAT_H diff --git a/dScripts/CppScripts.cpp b/dScripts/CppScripts.cpp index ed0de2ba3..e3b3acdbd 100644 --- a/dScripts/CppScripts.cpp +++ b/dScripts/CppScripts.cpp @@ -327,6 +327,7 @@ #include "VisToggleNotifierServer.h" #include "LupGenericInteract.h" #include "WblRobotCitizen.h" +#include "EnemyClearThreat.h" #include #include @@ -686,8 +687,21 @@ namespace { {"scripts\\zone\\LUPs\\RobotCity Intro\\WBL_RCIntro_RobotCitizenOrange.lua", []() {return new WblRobotCitizen();}}, {"scripts\\zone\\LUPs\\RobotCity Intro\\WBL_RCIntro_RobotCitizenRed.lua", []() {return new WblRobotCitizen();}}, {"scripts\\zone\\LUPs\\RobotCity Intro\\WBL_RCIntro_RobotCitizenYellow.lua", []() {return new WblRobotCitizen();}}, + {"scripts\\02_server\\Map\\General\\L_ENEMY_CLEAR_THREAT.lua", []() {return new EnemyClearThreat();}}, }; + + std::set g_ExcludedScripts = { + "scripts\\02_server\\Enemy\\General\\L_SUSPEND_LUA_AI.lua", + "scripts\\02_server\\Enemy\\General\\L_BASE_ENEMY_SPIDERLING.lua", + "scripts\\ai\\AG\\L_AG_SENTINEL_GUARD.lua", + "scripts\\ai\\FV\\L_ACT_NINJA_STUDENT.lua", + "scripts\\ai\\WILD\\L_WILD_GF_FROG.lua", + "scripts\\empty.lua", + "scripts\\zone\\AG\\L_ZONE_AG.lua", + "scripts\\zone\\NS\\L_ZONE_NS.lua", + "scripts\\zone\\GF\\L_ZONE_GF.lua", + }; }; CppScripts::Script* const CppScripts::GetScript(Entity* parent, const std::string& scriptName) { @@ -699,14 +713,8 @@ CppScripts::Script* const CppScripts::GetScript(Entity* parent, const std::strin const auto itrTernary = scriptLoader.find(scriptName); Script* script = itrTernary != scriptLoader.cend() ? itrTernary->second() : &InvalidToReturn; - if (script == &InvalidToReturn) { - if ((scriptName.length() > 0) && !((scriptName == "scripts\\02_server\\Enemy\\General\\L_SUSPEND_LUA_AI.lua") || - (scriptName == "scripts\\02_server\\Enemy\\General\\L_BASE_ENEMY_SPIDERLING.lua") || - (scriptName == "scripts\\ai\\FV\\L_ACT_NINJA_STUDENT.lua") || - (scriptName == "scripts\\ai\\WILD\\L_WILD_GF_FROG.lua") || - (scriptName == "scripts\\empty.lua") || - (scriptName == "scripts\\ai\\AG\\L_AG_SENTINEL_GUARD.lua") - )) LOG_DEBUG("LOT %i attempted to load CppScript for '%s', but returned InvalidScript.", parent->GetLOT(), scriptName.c_str()); + if (script == &InvalidToReturn && !scriptName.empty() && !g_ExcludedScripts.contains(scriptName)) { + LOG_DEBUG("LOT %i attempted to load CppScript for '%s', but returned InvalidScript.", parent->GetLOT(), scriptName.c_str()); } g_Scripts[scriptName] = script; From 7930f29990990eea671afe22b61e424596cf1cde Mon Sep 17 00:00:00 2001 From: David Markowitz Date: Thu, 12 Dec 2024 18:52:59 -0800 Subject: [PATCH 2/3] use some better logic --- dGame/Entity.h | 3 +++ dGame/dComponents/BaseCombatAIComponent.cpp | 18 +++++++++++++++++- dGame/dComponents/BaseCombatAIComponent.h | 8 ++++++++ dGame/dComponents/PhysicsComponent.cpp | 4 ---- .../02_server/Map/General/EnemyClearThreat.cpp | 2 +- 5 files changed, 29 insertions(+), 6 deletions(-) diff --git a/dGame/Entity.h b/dGame/Entity.h index bb32e50e2..2ed7aa536 100644 --- a/dGame/Entity.h +++ b/dGame/Entity.h @@ -107,6 +107,9 @@ class Entity { const SystemAddress& GetSystemAddress() const; + // Returns the collision group for this entity. + // Because the collision group is stored on a base component, this will look for a physics component + // then return the collision group from that. int32_t GetCollisionGroup() const; /** diff --git a/dGame/dComponents/BaseCombatAIComponent.cpp b/dGame/dComponents/BaseCombatAIComponent.cpp index 8d2db8b6e..fbe5a3825 100644 --- a/dGame/dComponents/BaseCombatAIComponent.cpp +++ b/dGame/dComponents/BaseCombatAIComponent.cpp @@ -16,6 +16,7 @@ #include "DestroyableComponent.h" #include +#include #include #include @@ -175,6 +176,15 @@ void BaseCombatAIComponent::Update(const float deltaTime) { if (m_ForcedTetherTime >= 0) return; } + for (auto entry = m_RemovedThreatList.begin(); entry != m_RemovedThreatList.end();) { + entry->second -= deltaTime; + if (entry->second <= 0.0f) { + entry = m_RemovedThreatList.erase(entry); + } else { + ++entry; + } + } + if (m_SoftTimer <= 0.0f) { Game::entityManager->SerializeEntity(m_Parent); @@ -469,7 +479,7 @@ std::vector BaseCombatAIComponent::GetTargetWithinAggroRange() const { const auto distance = Vector3::DistanceSquared(m_Parent->GetPosition(), other->GetPosition()); - if (distance > m_AggroRadius * m_AggroRadius) continue; + if (distance > m_AggroRadius * m_AggroRadius || m_RemovedThreatList.contains(id)) continue; targets.push_back(id); } @@ -823,3 +833,9 @@ void BaseCombatAIComponent::ForceTether() { SetAiState(AiState::aggro); } + +void BaseCombatAIComponent::IgnoreThreat(const LWOOBJID threat, const float value) { + m_RemovedThreatList[threat] = value; + SetThreat(threat, 0.0f); + m_Target = LWOOBJID_EMPTY; +} diff --git a/dGame/dComponents/BaseCombatAIComponent.h b/dGame/dComponents/BaseCombatAIComponent.h index ae2350c59..52adb429e 100644 --- a/dGame/dComponents/BaseCombatAIComponent.h +++ b/dGame/dComponents/BaseCombatAIComponent.h @@ -224,12 +224,16 @@ class BaseCombatAIComponent final : public Component { */ void Wake(); + // Force this entity to tether and ignore all other actions void ForceTether(); // heals the entity to full health and armor // and tethers them to their spawn point void TetherLogic(); + // Ignore a threat for a certain amount of time + void IgnoreThreat(const LWOOBJID target, const float time); + private: /** * Returns the current target or the target that currently is the largest threat to this entity @@ -388,8 +392,12 @@ class BaseCombatAIComponent final : public Component { */ bool m_DirtyStateOrTarget = false; + // The amount of time the entity will be forced to tether for float m_ForcedTetherTime = 0.0f; + // The amount of time a removed threat will be ignored for. + std::map m_RemovedThreatList; + /** * Whether the current entity is a mech enemy, needed as mechs tether radius works differently * @return whether this entity is a mech diff --git a/dGame/dComponents/PhysicsComponent.cpp b/dGame/dComponents/PhysicsComponent.cpp index 3fc13fe7f..67fca8d9a 100644 --- a/dGame/dComponents/PhysicsComponent.cpp +++ b/dGame/dComponents/PhysicsComponent.cpp @@ -29,10 +29,6 @@ PhysicsComponent::PhysicsComponent(Entity* parent, int32_t componentId) : Compon } if (m_Parent->HasVar(u"CollisionGroupID")) m_CollisionGroup = m_Parent->GetVar(u"CollisionGroupID"); - if (m_Parent->GetLOT() == 8419) { - auto [x, y, z] = m_Parent->GetDefaultPosition(); - LOG("PhysicsComponent: %f %f %f", x, y, z); - } } void PhysicsComponent::Serialize(RakNet::BitStream& outBitStream, bool bIsInitialUpdate) { diff --git a/dScripts/02_server/Map/General/EnemyClearThreat.cpp b/dScripts/02_server/Map/General/EnemyClearThreat.cpp index 06aa80629..b026899c3 100644 --- a/dScripts/02_server/Map/General/EnemyClearThreat.cpp +++ b/dScripts/02_server/Map/General/EnemyClearThreat.cpp @@ -19,7 +19,7 @@ void EnemyClearThreat::OnCollisionPhantom(Entity* self, Entity* target) { auto* const baseCombatAiComponent = enemy->GetComponent(); if (!baseCombatAiComponent) continue; - baseCombatAiComponent->SetThreat(target->GetObjectID(), 0.0f); + baseCombatAiComponent->IgnoreThreat(target->GetObjectID(), 3.0f); } } } From a1bf3842371848a9652b9d7d2b23ff6f9707c05b Mon Sep 17 00:00:00 2001 From: David Markowitz Date: Thu, 12 Dec 2024 22:59:57 -0800 Subject: [PATCH 3/3] Implement spider boss msg script tested that the message now shows up when hitting the survival spider entrance area --- dGame/dGameMessages/GameMessages.cpp | 36 +++++++++++- dGame/dGameMessages/GameMessages.h | 27 +++++++++ dScripts/CppScripts.cpp | 2 + dScripts/ai/AG/AgSpiderBossMessage.cpp | 81 ++++++++++++++++++++++++++ dScripts/ai/AG/AgSpiderBossMessage.h | 37 ++++++++++++ dScripts/ai/AG/CMakeLists.txt | 1 + 6 files changed, 183 insertions(+), 1 deletion(-) create mode 100644 dScripts/ai/AG/AgSpiderBossMessage.cpp create mode 100644 dScripts/ai/AG/AgSpiderBossMessage.h diff --git a/dGame/dGameMessages/GameMessages.cpp b/dGame/dGameMessages/GameMessages.cpp index b42e7d010..eea76fba3 100644 --- a/dGame/dGameMessages/GameMessages.cpp +++ b/dGame/dGameMessages/GameMessages.cpp @@ -6338,6 +6338,40 @@ void GameMessages::SendUpdateInventoryUi(LWOOBJID objectId, const SystemAddress& bitStream.Write(objectId); bitStream.Write(MessageType::Game::UPDATE_INVENTORY_UI); - + + SEND_PACKET; +} + +void GameMessages::DisplayTooltip::Send() const { + CBITSTREAM; + CMSGHEADER; + + bitStream.Write(target); + bitStream.Write(msgId); + + bitStream.Write(doOrDie); + bitStream.Write(noRepeat); + bitStream.Write(noRevive); + bitStream.Write(isPropertyTooltip); + bitStream.Write(show); + bitStream.Write(translate); + bitStream.Write(time); + bitStream.Write(id.size()); + bitStream.Write(id); + + std::string toWrite; + for (const auto* item : localizeParams) { + toWrite += item->GetString() + "\n"; + } + if (!toWrite.empty()) toWrite.pop_back(); + bitStream.Write(toWrite.size()); + bitStream.Write(GeneralUtils::ASCIIToUTF16(toWrite)); + if (!toWrite.empty()) bitStream.Write(0x00); // Null Terminator + + bitStream.Write(imageName.size()); + bitStream.Write(imageName); + bitStream.Write(text.size()); + bitStream.Write(text); + SEND_PACKET; } diff --git a/dGame/dGameMessages/GameMessages.h b/dGame/dGameMessages/GameMessages.h index 1dbe5d815..62d59a1de 100644 --- a/dGame/dGameMessages/GameMessages.h +++ b/dGame/dGameMessages/GameMessages.h @@ -11,6 +11,7 @@ #include "eCyclingMode.h" #include "eLootSourceType.h" #include "Brick.h" +#include "MessageType/Game.h" class AMFBaseValue; class Entity; @@ -20,6 +21,7 @@ class User; class Leaderboard; class PropertySelectQueryProperty; class TradeItem; +class LDFBaseData; enum class eAnimationFlags : uint32_t; @@ -47,6 +49,15 @@ enum class eCameraTargetCyclingMode : int32_t { }; namespace GameMessages { + struct GameMsg { + GameMsg(MessageType::Game gmId) : msgId{ gmId } {} + virtual ~GameMsg() = default; + virtual void Send() const {} + MessageType::Game msgId; + LWOOBJID target{ LWOOBJID_EMPTY }; + SystemAddress sysAddr{ UNASSIGNED_SYSTEM_ADDRESS }; + }; + class PropertyDataMessage; void SendFireEventClientSide(const LWOOBJID& objectID, const SystemAddress& sysAddr, std::u16string args, const LWOOBJID& object, int64_t param1, int param2, const LWOOBJID& sender); void SendTeleport(const LWOOBJID& objectID, const NiPoint3& pos, const NiQuaternion& rot, const SystemAddress& sysAddr, bool bSetRotation = false); @@ -680,6 +691,22 @@ namespace GameMessages { // This is a client gm however its default values are exactly what we need to get around the invisible inventory item issues. void SendUpdateInventoryUi(LWOOBJID objectId, const SystemAddress& sysAddr); + + struct DisplayTooltip : public GameMsg { + DisplayTooltip() : GameMsg(MessageType::Game::DISPLAY_TOOLTIP) {} + bool doOrDie{}; + bool noRepeat{}; + bool noRevive{}; + bool isPropertyTooltip{}; + bool show{}; + bool translate{}; + int32_t time{}; + std::u16string id{}; + std::vector localizeParams{}; + std::u16string imageName{}; + std::u16string text{}; + void Send() const override; + }; }; #endif // GAMEMESSAGES_H diff --git a/dScripts/CppScripts.cpp b/dScripts/CppScripts.cpp index e3b3acdbd..543284516 100644 --- a/dScripts/CppScripts.cpp +++ b/dScripts/CppScripts.cpp @@ -328,6 +328,7 @@ #include "LupGenericInteract.h" #include "WblRobotCitizen.h" #include "EnemyClearThreat.h" +#include "AgSpiderBossMessage.h" #include #include @@ -688,6 +689,7 @@ namespace { {"scripts\\zone\\LUPs\\RobotCity Intro\\WBL_RCIntro_RobotCitizenRed.lua", []() {return new WblRobotCitizen();}}, {"scripts\\zone\\LUPs\\RobotCity Intro\\WBL_RCIntro_RobotCitizenYellow.lua", []() {return new WblRobotCitizen();}}, {"scripts\\02_server\\Map\\General\\L_ENEMY_CLEAR_THREAT.lua", []() {return new EnemyClearThreat();}}, + {"scripts\\ai\\AG\\L_AG_SPIDER_BOSS_MESSAGE.lua", []() {return new AgSpiderBossMessage();}}, }; diff --git a/dScripts/ai/AG/AgSpiderBossMessage.cpp b/dScripts/ai/AG/AgSpiderBossMessage.cpp new file mode 100644 index 000000000..6e6cc1b9d --- /dev/null +++ b/dScripts/ai/AG/AgSpiderBossMessage.cpp @@ -0,0 +1,81 @@ +#include "AgSpiderBossMessage.h" + +#include "Entity.h" +#include "GameMessages.h" + +#include "RenderComponent.h" + +Box AgSpiderBossMessage::GetBox(Entity* self) const { + return self->GetVar(u"box"); +} + +void AgSpiderBossMessage::SetBox(Entity* self, const Box& box) const { + self->SetVar(u"box", box); +} + +void AgSpiderBossMessage::MakeBox(Entity* self) const { + auto box = GetBox(self); + if (box.boxTarget == LWOOBJID_EMPTY || box.isDisplayed || box.boxSelf == LWOOBJID_EMPTY) return; + + box.isDisplayed = true; + SetBox(self, box); + self->AddTimer("BoxTimer", box.boxTime); + + const auto* const tgt = Game::entityManager->GetEntity(box.boxTarget); + if (!tgt) return; + GameMessages::DisplayTooltip tooltip; + tooltip.target = tgt->GetObjectID(); + tooltip.sysAddr = tgt->GetSystemAddress(); + tooltip.show = true; + tooltip.text = box.boxText; + tooltip.time = box.boxTime * 1000; // to ms + tooltip.Send(); +} + +void AgSpiderBossMessage::OnCollisionPhantom(Entity* self, Entity* target) { + if (!target || !target->IsPlayer()) return; + + auto box = GetBox(self); + // knockback the target + auto forward = target->GetRotation().GetForwardVector(); + box.boxTarget = target->GetObjectID(); + GameMessages::SendPlayFXEffect(target->GetObjectID(), 1378, u"create", "pushBack"); + RenderComponent::PlayAnimation(target, "knockback-recovery"); + forward.y += 15; + forward.x *= 100; + forward.z *= 100; + GameMessages::SendKnockback(target->GetObjectID(), self->GetObjectID(), self->GetObjectID(), 0, forward); + + if (box.isTouch || box.isDisplayed) return; + box.boxSelf = self->GetObjectID(); + box.isTouch = true; + box.boxText = u"%[SPIDER_CAVE_MESSAGE]"; + SetBox(self, box); + self->AddTimer("EventTimer", 0.1f); +} + +void AgSpiderBossMessage::OnOffCollisionPhantom(Entity* self, Entity* target) { + if (!target) return; + auto box = GetBox(self); + box.isTouch = false; + box.Reset(); + SetBox(self, box); +} + +void AgSpiderBossMessage::OnTimerDone(Entity* self, std::string timerName) { + if (timerName == "BoxTimer") { + auto box = GetBox(self); + box.isDisplayed = false; + SetBox(self, box); + ResetBox(self); + } else if (timerName == "EventTimer") { + auto box = GetBox(self); + MakeBox(self); + } +} + +void AgSpiderBossMessage::ResetBox(Entity* self) const { + auto box = GetBox(self); + box.Reset(); + SetBox(self, box); +} diff --git a/dScripts/ai/AG/AgSpiderBossMessage.h b/dScripts/ai/AG/AgSpiderBossMessage.h new file mode 100644 index 000000000..70a2fbee3 --- /dev/null +++ b/dScripts/ai/AG/AgSpiderBossMessage.h @@ -0,0 +1,37 @@ +#ifndef AGSPIDERBOSSMESSAGE_H +#define AGSPIDERBOSSMESSAGE_H + +#include "CppScripts.h" + +struct Box { + LWOOBJID boxTarget{}; + bool isDisplayed{}; + bool isTouch{}; + bool isFirst{}; + LWOOBJID boxSelf{}; + std::u16string boxText{}; + int32_t boxTime{ 1 }; + + void Reset() { + boxTarget = LWOOBJID_EMPTY; + isDisplayed = false; + isTouch = false; + isFirst = false; + boxSelf = LWOOBJID_EMPTY; + boxText.clear(); + boxTime = 1; + } +}; + +class AgSpiderBossMessage : public CppScripts::Script { +public: + Box GetBox(Entity* self) const; + void SetBox(Entity* self, const Box& box) const; + void MakeBox(Entity* self) const; + void OnCollisionPhantom(Entity* self, Entity* target) override; + void OnOffCollisionPhantom(Entity* self, Entity* target) override; + void OnTimerDone(Entity* self, std::string timerName) override; + void ResetBox(Entity* self) const; +}; + +#endif //!AGSPIDERBOSSMESSAGE_H diff --git a/dScripts/ai/AG/CMakeLists.txt b/dScripts/ai/AG/CMakeLists.txt index 101f86fd5..237e266a5 100644 --- a/dScripts/ai/AG/CMakeLists.txt +++ b/dScripts/ai/AG/CMakeLists.txt @@ -1,6 +1,7 @@ set(DSCRIPTS_SOURCES_AI_AG "AgShipPlayerDeathTrigger.cpp" "AgSpaceStuff.cpp" + "AgSpiderBossMessage.cpp" "AgShipShake.cpp" "AgShipPlayerShockServer.cpp" "AgImagSmashable.cpp"