From 833ed8a40d35e0c30b2228f3fe6b9bb1f71a3adb Mon Sep 17 00:00:00 2001 From: Mick Vermeulen Date: Sat, 11 Dec 2021 11:59:29 +0100 Subject: [PATCH] Implement Buccaneer Valiant special ability Adds the ability for the buccaneer valiant to spawn a ship that rams enemies and smashes them. Next to a script that triggers the ship skill a few other changes had to be made: - Force movement behavior server side calculation and sync - The ship has no physics volume so the FindValidTargets for behaviors had to be altered to allow ControllablePhysics entities to find entities within their area. The "target_self" AOE flag has been used to replicate the old behavior. --- dGame/dBehaviors/AreaOfEffectBehavior.cpp | 4 +- dGame/dBehaviors/AreaOfEffectBehavior.h | 2 + dGame/dBehaviors/BehaviorContext.cpp | 11 ++- dGame/dBehaviors/BehaviorContext.h | 2 +- dGame/dBehaviors/ForceMovementBehavior.cpp | 85 ++++++++++++++++------ dGame/dBehaviors/ForceMovementBehavior.h | 9 +++ dScripts/BuccaneerValiantShip.cpp | 24 ++++++ dScripts/BuccaneerValiantShip.h | 6 ++ dScripts/CppScripts.cpp | 3 + 9 files changed, 114 insertions(+), 32 deletions(-) create mode 100644 dScripts/BuccaneerValiantShip.cpp create mode 100644 dScripts/BuccaneerValiantShip.h diff --git a/dGame/dBehaviors/AreaOfEffectBehavior.cpp b/dGame/dBehaviors/AreaOfEffectBehavior.cpp index a5341348..b87c7c17 100644 --- a/dGame/dBehaviors/AreaOfEffectBehavior.cpp +++ b/dGame/dBehaviors/AreaOfEffectBehavior.cpp @@ -74,7 +74,7 @@ void AreaOfEffectBehavior::Calculate(BehaviorContext* context, RakNet::BitStream includeFaction = 1; } - for (auto validTarget : context->GetValidTargets(m_ignoreFaction , includeFaction)) + for (auto validTarget : context->GetValidTargets(m_ignoreFaction , includeFaction, m_TargetSelf == 1)) { auto* entity = EntityManager::Instance()->GetEntity(validTarget); @@ -155,4 +155,6 @@ void AreaOfEffectBehavior::Load() this->m_ignoreFaction = GetInt("ignore_faction"); this->m_includeFaction = GetInt("include_faction"); + + this->m_TargetSelf = GetInt("target_self"); } diff --git a/dGame/dBehaviors/AreaOfEffectBehavior.h b/dGame/dBehaviors/AreaOfEffectBehavior.h index c2b6679d..37313499 100644 --- a/dGame/dBehaviors/AreaOfEffectBehavior.h +++ b/dGame/dBehaviors/AreaOfEffectBehavior.h @@ -13,6 +13,8 @@ public: int32_t m_ignoreFaction; int32_t m_includeFaction; + + int32_t m_TargetSelf; /* * Inherited diff --git a/dGame/dBehaviors/BehaviorContext.cpp b/dGame/dBehaviors/BehaviorContext.cpp index fd3117a4..2637aa61 100644 --- a/dGame/dBehaviors/BehaviorContext.cpp +++ b/dGame/dBehaviors/BehaviorContext.cpp @@ -149,7 +149,7 @@ void BehaviorContext::SyncBehavior(const uint32_t syncId, RakNet::BitStream* bit return; } - + behavior->Sync(this, bitStream, branch); } @@ -325,7 +325,7 @@ void BehaviorContext::Reset() this->scheduledUpdates.clear(); } -std::vector BehaviorContext::GetValidTargets(int32_t ignoreFaction, int32_t includeFaction) const +std::vector BehaviorContext::GetValidTargets(int32_t ignoreFaction, int32_t includeFaction, bool targetSelf) const { auto* entity = EntityManager::Instance()->GetEntity(this->caster); @@ -353,21 +353,20 @@ std::vector BehaviorContext::GetValidTargets(int32_t ignoreFaction, in } } - if (ignoreFaction || includeFaction || (!entity->HasComponent(COMPONENT_TYPE_PHANTOM_PHYSICS) && !entity->HasComponent(COMPONENT_TYPE_CONTROLLABLE_PHYSICS) && targets.empty())) + if (ignoreFaction || includeFaction || (!entity->HasComponent(COMPONENT_TYPE_PHANTOM_PHYSICS) && targets.empty())) { - DestroyableComponent* destroyableComponent; + DestroyableComponent* destroyableComponent; if (!entity->TryGetComponent(COMPONENT_TYPE_DESTROYABLE, destroyableComponent)) { return targets; } auto entities = EntityManager::Instance()->GetEntitiesByComponent(COMPONENT_TYPE_CONTROLLABLE_PHYSICS); - for (auto* candidate : entities) { const auto id = candidate->GetObjectID(); - if (destroyableComponent->CheckValidity(id, ignoreFaction || includeFaction)) + if ((id != entity->GetObjectID() || targetSelf) && destroyableComponent->CheckValidity(id, ignoreFaction || includeFaction)) { targets.push_back(id); } diff --git a/dGame/dBehaviors/BehaviorContext.h b/dGame/dBehaviors/BehaviorContext.h index 58154512..21b2e6e9 100644 --- a/dGame/dBehaviors/BehaviorContext.h +++ b/dGame/dBehaviors/BehaviorContext.h @@ -102,7 +102,7 @@ struct BehaviorContext void Reset(); - std::vector GetValidTargets(int32_t ignoreFaction = 0, int32_t includeFaction = 0) const; + std::vector GetValidTargets(int32_t ignoreFaction = 0, int32_t includeFaction = 0, const bool targetSelf = false) const; explicit BehaviorContext(LWOOBJID originator, bool calculation = false); diff --git a/dGame/dBehaviors/ForceMovementBehavior.cpp b/dGame/dBehaviors/ForceMovementBehavior.cpp index 332cfed6..ea8c04a7 100644 --- a/dGame/dBehaviors/ForceMovementBehavior.cpp +++ b/dGame/dBehaviors/ForceMovementBehavior.cpp @@ -1,44 +1,81 @@ #include "ForceMovementBehavior.h" - #include "BehaviorBranchContext.h" #include "BehaviorContext.h" +#include "ControllablePhysicsComponent.h" +#include "EntityManager.h" -void ForceMovementBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bitStream, const BehaviorBranchContext branch) -{ - if (this->m_hitAction->m_templateId == BehaviorTemplates::BEHAVIOR_EMPTY && this->m_hitEnemyAction->m_templateId == BehaviorTemplates::BEHAVIOR_EMPTY && this->m_hitFactionAction->m_templateId == BehaviorTemplates::BEHAVIOR_EMPTY) - { - return; - } +void ForceMovementBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bitStream, const BehaviorBranchContext branch) { + if (this->m_hitAction->m_templateId == BehaviorTemplates::BEHAVIOR_EMPTY && this->m_hitEnemyAction->m_templateId == BehaviorTemplates::BEHAVIOR_EMPTY && this->m_hitFactionAction->m_templateId == BehaviorTemplates::BEHAVIOR_EMPTY) { + return; + } - uint32_t handle; - - bitStream->Read(handle); - - context->RegisterSyncBehavior(handle, this, branch); + uint32_t handle; + bitStream->Read(handle); + context->RegisterSyncBehavior(handle, this, branch); } void ForceMovementBehavior::Sync(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) { - uint32_t next; + uint32_t next; + bitStream->Read(next); - bitStream->Read(next); + LWOOBJID target; + bitStream->Read(target); - LWOOBJID target; + branch.target = target; + auto* behavior = CreateBehavior(next); + behavior->Handle(context, bitStream, branch); +} - bitStream->Read(target); +void ForceMovementBehavior::Calculate(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) { + if (this->m_hitAction->m_templateId == BehaviorTemplates::BEHAVIOR_EMPTY && this->m_hitEnemyAction->m_templateId == BehaviorTemplates::BEHAVIOR_EMPTY && this->m_hitFactionAction->m_templateId == BehaviorTemplates::BEHAVIOR_EMPTY) { + return; + } - auto* behavior = CreateBehavior(next); + auto* casterEntity = EntityManager::Instance()->GetEntity(context->caster); + if (casterEntity != nullptr) { + auto* controllablePhysicsComponent = casterEntity->GetComponent(); + if (controllablePhysicsComponent != nullptr) { - branch.target = target; + if (m_Forward == 1) { + controllablePhysicsComponent->SetVelocity(controllablePhysicsComponent->GetRotation().GetForwardVector() * 25); + } - behavior->Handle(context, bitStream, branch); + EntityManager::Instance()->SerializeEntity(casterEntity); + } + } + + const auto skillHandle = context->GetUniqueSkillId(); + bitStream->Write(skillHandle); + + context->SyncCalculation(skillHandle, this->m_Duration, this, branch); } void ForceMovementBehavior::Load() { - this->m_hitAction = GetAction("hit_action"); - - this->m_hitEnemyAction = GetAction("hit_action_enemy"); - - this->m_hitFactionAction = GetAction("hit_action_faction"); + this->m_hitAction = GetAction("hit_action"); + this->m_hitEnemyAction = GetAction("hit_action_enemy"); + this->m_hitFactionAction = GetAction("hit_action_faction"); + this->m_Duration = GetFloat("duration"); + this->m_Forward = GetFloat("forward"); + this->m_Left = GetFloat("left"); + this->m_Yaw = GetFloat("yaw"); +} + +void ForceMovementBehavior::SyncCalculation(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) { + auto* casterEntity = EntityManager::Instance()->GetEntity(context->caster); + if (casterEntity != nullptr) { + auto* controllablePhysicsComponent = casterEntity->GetComponent(); + if (controllablePhysicsComponent != nullptr) { + + controllablePhysicsComponent->SetPosition(controllablePhysicsComponent->GetPosition() + controllablePhysicsComponent->GetVelocity() * m_Duration); + controllablePhysicsComponent->SetVelocity({}); + + EntityManager::Instance()->SerializeEntity(casterEntity); + } + } + + this->m_hitAction->Calculate(context, bitStream, branch); + this->m_hitEnemyAction->Calculate(context, bitStream, branch); + this->m_hitEnemyAction->Calculate(context, bitStream, branch); } diff --git a/dGame/dBehaviors/ForceMovementBehavior.h b/dGame/dBehaviors/ForceMovementBehavior.h index 5b77e4b7..50b0aa26 100644 --- a/dGame/dBehaviors/ForceMovementBehavior.h +++ b/dGame/dBehaviors/ForceMovementBehavior.h @@ -9,6 +9,11 @@ public: Behavior* m_hitEnemyAction; Behavior* m_hitFactionAction; + + float_t m_Duration; + float_t m_Forward; + float_t m_Left; + float_t m_Yaw; /* * Inherited @@ -18,8 +23,12 @@ public: { } + void Calculate(BehaviorContext *context, RakNet::BitStream *bitStream, BehaviorBranchContext branch) override; + void Handle(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) override; + void SyncCalculation(BehaviorContext *context, RakNet::BitStream *bitStream, BehaviorBranchContext branch) override; + void Sync(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) override; void Load() override; diff --git a/dScripts/BuccaneerValiantShip.cpp b/dScripts/BuccaneerValiantShip.cpp new file mode 100644 index 00000000..2c24b7d4 --- /dev/null +++ b/dScripts/BuccaneerValiantShip.cpp @@ -0,0 +1,24 @@ +#include "BuccaneerValiantShip.h" +#include "SkillComponent.h" +#include "dLogger.h" + +void BuccaneerValiantShip::OnStartup(Entity* self) { + const auto skill = 982; + const auto behavior = 20577; + const auto skillCastTimer = 1.0F; + + self->AddCallbackTimer(skillCastTimer, [self]() { + auto* skillComponent = self->GetComponent(); + auto* owner = self->GetOwner(); + + if (skillComponent != nullptr && owner != nullptr) { + skillComponent->CalculateBehavior(skill, behavior, LWOOBJID_EMPTY, true, false, owner->GetObjectID()); + + // Kill self if missed + const auto selfSmashTimer = 1.1F; + self->AddCallbackTimer(selfSmashTimer, [self]() { + self->Kill(); + }); + } + }); +} diff --git a/dScripts/BuccaneerValiantShip.h b/dScripts/BuccaneerValiantShip.h new file mode 100644 index 00000000..f501d1b9 --- /dev/null +++ b/dScripts/BuccaneerValiantShip.h @@ -0,0 +1,6 @@ +#pragma once +#include "CppScripts.h" + +class BuccaneerValiantShip : public CppScripts::Script { + void OnStartup(Entity *self) override; +}; diff --git a/dScripts/CppScripts.cpp b/dScripts/CppScripts.cpp index ed59c5ba..87a2c668 100644 --- a/dScripts/CppScripts.cpp +++ b/dScripts/CppScripts.cpp @@ -261,6 +261,7 @@ #include "PersonalFortress.h" #include "PropertyDevice.h" #include "ImaginationBackpackHealServer.h" +#include "BuccaneerValiantShip.h" // Survival scripts #include "AgSurvivalStromling.h" @@ -774,6 +775,8 @@ CppScripts::Script* CppScripts::GetScript(Entity* parent, const std::string& scr script = new PropertyDevice(); else if (scriptName == "scripts\\02_server\\Map\\General\\L_IMAG_BACKPACK_HEALS_SERVER.lua") script = new ImaginationBackpackHealServer(); + else if (scriptName == "scripts\\EquipmentScripts\\BuccaneerValiantShip.lua") + script = new BuccaneerValiantShip(); //Ignore these scripts: else if (scriptName == "scripts\\02_server\\Enemy\\General\\L_SUSPEND_LUA_AI.lua")