Public release of the DLU server code!

Have fun!
This commit is contained in:
Unknown
2021-12-05 18:54:36 +01:00
parent 5f7270e4ad
commit 0545adfac3
1146 changed files with 368646 additions and 1 deletions

View File

@@ -0,0 +1,44 @@
#include "AirMovementBehavior.h"
#include "BehaviorBranchContext.h"
#include "BehaviorContext.h"
#include "EntityManager.h"
void AirMovementBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch)
{
uint32_t handle;
bitStream->Read(handle);
context->RegisterSyncBehavior(handle, this, branch);
}
void AirMovementBehavior::Calculate(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch)
{
const auto handle = context->GetUniqueSkillId();
bitStream->Write(handle);
}
void AirMovementBehavior::Sync(BehaviorContext* context, RakNet::BitStream* bit_stream, BehaviorBranchContext branch)
{
uint32_t behaviorId;
bit_stream->Read(behaviorId);
LWOOBJID target;
bit_stream->Read(target);
auto* behavior = CreateBehavior(behaviorId);
if (EntityManager::Instance()->GetEntity(target) != nullptr)
{
branch.target = target;
}
behavior->Handle(context, bit_stream, branch);
}
void AirMovementBehavior::Load()
{
}

View File

@@ -0,0 +1,23 @@
#pragma once
#include "Behavior.h"
class AirMovementBehavior final : public Behavior
{
public:
/*
* Inherited
*/
explicit AirMovementBehavior(const uint32_t behavior_id) : Behavior(behavior_id)
{
}
void Handle(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) override;
void Calculate(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) override;
void Sync(BehaviorContext* context, RakNet::BitStream* bit_stream, BehaviorBranchContext branch) override;
void Load() override;
};

View File

@@ -0,0 +1,35 @@
#include "AndBehavior.h"
#include "BehaviorBranchContext.h"
#include "Game.h"
#include "dLogger.h"
void AndBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bitStream, const BehaviorBranchContext branch)
{
for (auto* behavior : this->m_behaviors)
{
behavior->Handle(context, bitStream, branch);
}
}
void AndBehavior::Calculate(BehaviorContext* context, RakNet::BitStream* bitStream, const BehaviorBranchContext branch)
{
for (auto* behavior : this->m_behaviors)
{
behavior->Calculate(context, bitStream, branch);
}
}
void AndBehavior::Load()
{
const auto parameters = GetParameterNames();
for (const auto& parameter : parameters)
{
if (parameter.first.rfind("behavior", 0) == 0)
{
auto* action = GetAction(parameter.second);
this->m_behaviors.push_back(action);
}
}
}

View File

@@ -0,0 +1,24 @@
#pragma once
#include <vector>
#include "Behavior.h"
class AndBehavior final : public Behavior
{
public:
std::vector<Behavior*> m_behaviors;
/*
* Inherited
*/
explicit AndBehavior(const uint32_t behaviorId) : Behavior(behaviorId)
{
}
void Handle(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) override;
void Calculate(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) override;
void Load() override;
};

View File

@@ -0,0 +1,52 @@
#include "ApplyBuffBehavior.h"
#include "EntityManager.h"
#include "BehaviorContext.h"
#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);
if (entity == nullptr) return;
auto* buffComponent = entity->GetComponent<BuffComponent>();
if (buffComponent == nullptr) return;
buffComponent->ApplyBuff(m_BuffId, m_Duration, context->originator, addImmunity, cancelOnDamaged, cancelOnDeath,
cancelOnLogout, cancelonRemoveBuff, cancelOnUi, cancelOnUnequip, cancelOnZone);
}
void ApplyBuffBehavior::UnCast(BehaviorContext* context, BehaviorBranchContext branch)
{
auto* entity = EntityManager::Instance()->GetEntity(branch.target);
if (entity == nullptr) return;
auto* buffComponent = entity->GetComponent<BuffComponent>();
if (buffComponent == nullptr) return;
buffComponent->RemoveBuff(m_BuffId);
}
void ApplyBuffBehavior::Calculate(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch)
{
Handle(context, bitStream, branch);
}
void ApplyBuffBehavior::Load()
{
m_BuffId = GetInt("buff_id");
m_Duration = GetFloat("duration_secs");
addImmunity = GetBoolean("add_immunity");
cancelOnDamaged = GetBoolean("cancel_on_damaged");
cancelOnDeath = GetBoolean("cancel_on_death");
cancelOnLogout = GetBoolean("cancel_on_logout");
cancelonRemoveBuff = GetBoolean("cancel_on_remove_buff");
cancelOnUi = GetBoolean("cancel_on_ui");
cancelOnUnequip = GetBoolean("cancel_on_unequip");
cancelOnZone = GetBoolean("cancel_on_zone");
}

View File

@@ -0,0 +1,35 @@
#pragma once
#include <vector>
#include "Behavior.h"
class ApplyBuffBehavior final : public Behavior
{
public:
int32_t m_BuffId;
float m_Duration;
bool addImmunity;
bool cancelOnDamaged;
bool cancelOnDeath;
bool cancelOnLogout;
bool cancelonRemoveBuff;
bool cancelOnUi;
bool cancelOnUnequip;
bool cancelOnZone;
/*
* Inherited
*/
explicit ApplyBuffBehavior(const uint32_t behaviorId) : Behavior(behaviorId)
{
}
void Handle(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) override;
void UnCast(BehaviorContext* context, BehaviorBranchContext branch) override;
void Calculate(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) override;
void Load() override;
};

View File

@@ -0,0 +1,158 @@
#include "AreaOfEffectBehavior.h"
#include <vector>
#include "EntityManager.h"
#include "Game.h"
#include "dLogger.h"
#include "BehaviorBranchContext.h"
#include "BehaviorContext.h"
#include "RebuildComponent.h"
#include "DestroyableComponent.h"
void AreaOfEffectBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch)
{
uint32_t targetCount;
bitStream->Read(targetCount);
if (targetCount > this->m_maxTargets)
{
return;
}
std::vector<LWOOBJID> targets;
targets.reserve(targetCount);
for (auto i = 0u; i < targetCount; ++i)
{
LWOOBJID target;
bitStream->Read(target);
targets.push_back(target);
}
for (auto target : targets)
{
branch.target = target;
this->m_action->Handle(context, bitStream, branch);
}
}
void AreaOfEffectBehavior::Calculate(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch)
{
auto* self = EntityManager::Instance()->GetEntity(context->caster);
if (self == nullptr)
{
Game::logger->Log("TacArcBehavior", "Invalid self for (%llu)!\n", context->originator);
return;
}
auto reference = branch.isProjectile ? branch.referencePosition : self->GetPosition();
std::vector<Entity*> targets;
auto* presetTarget = EntityManager::Instance()->GetEntity(branch.target);
if (presetTarget != nullptr)
{
if (this->m_radius * this->m_radius >= Vector3::DistanceSquared(reference, presetTarget->GetPosition()))
{
targets.push_back(presetTarget);
}
}
int32_t includeFaction = m_includeFaction;
if (self->GetLOT() == 14466) // TODO: Fix edge case
{
includeFaction = 1;
}
for (auto validTarget : context->GetValidTargets(m_ignoreFaction , includeFaction))
{
auto* entity = EntityManager::Instance()->GetEntity(validTarget);
if (entity == nullptr)
{
Game::logger->Log("TacArcBehavior", "Invalid target (%llu) for (%llu)!\n", validTarget, context->originator);
continue;
}
if (std::find(targets.begin(), targets.end(), entity) != targets.end())
{
continue;
}
auto* destroyableComponent = entity->GetComponent<DestroyableComponent>();
if (destroyableComponent == nullptr)
{
continue;
}
if (destroyableComponent->HasFaction(m_ignoreFaction))
{
continue;
}
const auto distance = Vector3::DistanceSquared(reference, entity->GetPosition());
if (this->m_radius * this->m_radius >= distance && (this->m_maxTargets == 0 || targets.size() < this->m_maxTargets))
{
targets.push_back(entity);
}
}
std::sort(targets.begin(), targets.end(), [reference](Entity* a, Entity* b)
{
const auto aDistance = Vector3::DistanceSquared(a->GetPosition(), reference);
const auto bDistance = Vector3::DistanceSquared(b->GetPosition(), reference);
return aDistance > bDistance;
});
const uint32_t size = targets.size();
bitStream->Write(size);
if (size == 0)
{
return;
}
context->foundTarget = true;
for (auto* target : targets)
{
bitStream->Write(target->GetObjectID());
PlayFx(u"cast", context->originator, target->GetObjectID());
}
for (auto* target : targets)
{
branch.target = target->GetObjectID();
this->m_action->Calculate(context, bitStream, branch);
}
}
void AreaOfEffectBehavior::Load()
{
this->m_action = GetAction("action");
this->m_radius = GetFloat("radius");
this->m_maxTargets = GetInt("max targets");
this->m_ignoreFaction = GetInt("ignore_faction");
this->m_includeFaction = GetInt("include_faction");
}

View File

@@ -0,0 +1,29 @@
#pragma once
#include "Behavior.h"
class AreaOfEffectBehavior final : public Behavior
{
public:
Behavior* m_action;
uint32_t m_maxTargets;
float m_radius;
int32_t m_ignoreFaction;
int32_t m_includeFaction;
/*
* Inherited
*/
explicit AreaOfEffectBehavior(const uint32_t behaviorId) : Behavior(behaviorId)
{
}
void Handle(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) override;
void Calculate(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) override;
void Load() override;
};

View File

@@ -0,0 +1,61 @@
#include "AttackDelayBehavior.h"
#include "BehaviorBranchContext.h"
#include "BehaviorContext.h"
#include "Game.h"
#include "dLogger.h"
void AttackDelayBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bitStream, const BehaviorBranchContext branch)
{
uint32_t handle;
bitStream->Read(handle);
for (auto i = 0u; i < this->m_numIntervals; ++i)
{
context->RegisterSyncBehavior(handle, this, branch);
}
}
void AttackDelayBehavior::Calculate(BehaviorContext* context, RakNet::BitStream* bitStream, const BehaviorBranchContext branch)
{
const auto handle = context->GetUniqueSkillId();
bitStream->Write(handle);
context->foundTarget = true;
for (auto i = 0u; i < this->m_numIntervals; ++i)
{
const auto multiple = static_cast<float>(i + 1);
context->SyncCalculation(handle, this->m_delay * multiple, this, branch, m_ignoreInterrupts);
}
}
void AttackDelayBehavior::Sync(BehaviorContext* context, RakNet::BitStream* bitStream, const BehaviorBranchContext branch)
{
this->m_action->Handle(context, bitStream, branch);
}
void AttackDelayBehavior::SyncCalculation(BehaviorContext* context, RakNet::BitStream* bitStream, const BehaviorBranchContext branch)
{
PlayFx(u"cast", context->originator);
this->m_action->Calculate(context, bitStream, branch);
}
void AttackDelayBehavior::Load()
{
this->m_numIntervals = GetInt("num_intervals");
this->m_action = GetAction("action");
this->m_delay = GetFloat("delay");
this->m_ignoreInterrupts = GetBoolean("ignore_interrupts");
if (this->m_numIntervals == 0)
{
this->m_numIntervals = 1;
}
}

View File

@@ -0,0 +1,32 @@
#pragma once
#include "Behavior.h"
class AttackDelayBehavior final : public Behavior
{
public:
Behavior* m_action;
float m_delay;
uint32_t m_numIntervals;
bool m_ignoreInterrupts;
/*
* Inherited
*/
explicit AttackDelayBehavior(const uint32_t behaviorId) : Behavior(behaviorId)
{
}
void Handle(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) override;
void Calculate(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) override;
void Sync(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) override;
void SyncCalculation(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) override;
void Load() override;
};

View File

@@ -0,0 +1,150 @@
#include "BasicAttackBehavior.h"
#include "BehaviorBranchContext.h"
#include "Game.h"
#include "dLogger.h"
#include "EntityManager.h"
#include "DestroyableComponent.h"
#include "BehaviorContext.h"
void BasicAttackBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) {
if (context->unmanaged) {
auto* entity = EntityManager::Instance()->GetEntity(branch.target);
auto* destroyableComponent = entity->GetComponent<DestroyableComponent>();
if (destroyableComponent != nullptr) {
PlayFx(u"onhit", entity->GetObjectID());
destroyableComponent->Damage(this->m_maxDamage, context->originator);
}
this->m_onSuccess->Handle(context, bitStream, branch);
return;
}
bitStream->AlignReadToByteBoundary();
uint16_t allocatedBits;
bitStream->Read(allocatedBits);
const auto baseAddress = bitStream->GetReadOffset();
if (bitStream->ReadBit()) { // Blocked
return;
}
if (bitStream->ReadBit()) { // Immune
return;
}
if (bitStream->ReadBit()) { // Success
uint32_t unknown;
bitStream->Read(unknown);
uint32_t damageDealt;
bitStream->Read(damageDealt);
// A value that's too large may be a cheating attempt, so we set it to MIN too
if (damageDealt > this->m_maxDamage || damageDealt < this->m_minDamage) {
damageDealt = this->m_minDamage;
}
auto* entity = EntityManager::Instance()->GetEntity(branch.target);
bool died;
bitStream->Read(died);
if (entity != nullptr) {
auto* destroyableComponent = entity->GetComponent<DestroyableComponent>();
if (destroyableComponent != nullptr) {
PlayFx(u"onhit", entity->GetObjectID());
destroyableComponent->Damage(damageDealt, context->originator);
}
}
}
uint8_t successState;
bitStream->Read(successState);
switch (successState) {
case 1:
this->m_onSuccess->Handle(context, bitStream, branch);
break;
default:
Game::logger->Log("BasicAttackBehavior", "Unknown success state (%i)!\n", successState);
break;
}
bitStream->SetReadOffset(baseAddress + allocatedBits);
}
void BasicAttackBehavior::Calculate(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) {
auto* self = EntityManager::Instance()->GetEntity(context->originator);
if (self == nullptr) {
Game::logger->Log("BasicAttackBehavior", "Invalid self entity (%llu)!\n", context->originator);
return;
}
bitStream->AlignWriteToByteBoundary();
const auto allocatedAddress = bitStream->GetWriteOffset();
bitStream->Write(uint16_t(0));
const auto startAddress = bitStream->GetWriteOffset();
bitStream->Write0(); // Blocked
bitStream->Write0(); // Immune
bitStream->Write1(); // Success
if (true) {
uint32_t unknown3 = 0;
bitStream->Write(unknown3);
auto damage = this->m_minDamage;
auto* entity = EntityManager::Instance()->GetEntity(branch.target);
if (entity == nullptr) {
damage = 0;
bitStream->Write(damage);
bitStream->Write(false);
} else {
bitStream->Write(damage);
bitStream->Write(true);
auto* destroyableComponent = entity->GetComponent<DestroyableComponent>();
if (damage != 0 && destroyableComponent != nullptr) {
PlayFx(u"onhit", entity->GetObjectID(), 1);
destroyableComponent->Damage(damage, context->originator, false);
context->ScheduleUpdate(branch.target);
}
}
}
uint8_t successState = 1;
bitStream->Write(successState);
switch (successState) {
case 1:
this->m_onSuccess->Calculate(context, bitStream, branch);
break;
default:
Game::logger->Log("BasicAttackBehavior", "Unknown success state (%i)!\n", successState);
break;
}
const auto endAddress = bitStream->GetWriteOffset();
const uint16_t allocate = endAddress - startAddress + 1;
bitStream->SetWriteOffset(allocatedAddress);
bitStream->Write(allocate);
bitStream->SetWriteOffset(startAddress + allocate);
}
void BasicAttackBehavior::Load() {
this->m_minDamage = GetInt("min damage");
if (this->m_minDamage == 0) this->m_minDamage = 1;
this->m_maxDamage = GetInt("max damage");
if (this->m_maxDamage == 0) this->m_maxDamage = 1;
this->m_onSuccess = GetAction("on_success");
}

View File

@@ -0,0 +1,22 @@
#pragma once
#include "Behavior.h"
class BasicAttackBehavior final : public Behavior
{
public:
uint32_t m_minDamage;
uint32_t m_maxDamage;
Behavior* m_onSuccess;
explicit BasicAttackBehavior(const uint32_t behaviorId) : Behavior(behaviorId)
{
}
void Handle(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) override;
void Calculate(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) override;
void Load() override;
};

View File

@@ -0,0 +1,547 @@
#include <sstream>
#include <algorithm>
#include "Behavior.h"
#include "CDActivitiesTable.h"
#include "Game.h"
#include "dLogger.h"
#include "BehaviorTemplates.h"
#include "BehaviorBranchContext.h"
/*
* Behavior includes
*/
#include "AirMovementBehavior.h"
#include "EmptyBehavior.h"
#include "MovementSwitchBehavior.h"
#include "AndBehavior.h"
#include "AreaOfEffectBehavior.h"
#include "DurationBehavior.h"
#include "TacArcBehavior.h"
#include "AttackDelayBehavior.h"
#include "BasicAttackBehavior.h"
#include "ChainBehavior.h"
#include "ChargeUpBehavior.h"
#include "GameMessages.h"
#include "HealBehavior.h"
#include "ImaginationBehavior.h"
#include "KnockbackBehavior.h"
#include "NpcCombatSkillBehavior.h"
#include "StartBehavior.h"
#include "StunBehavior.h"
#include "ProjectileAttackBehavior.h"
#include "RepairBehavior.h"
#include "SwitchBehavior.h"
#include "SwitchMultipleBehavior.h"
#include "TargetCasterBehavior.h"
#include "VerifyBehavior.h"
#include "BuffBehavior.h"
#include "TauntBehavior.h"
#include "SkillCastFailedBehavior.h"
#include "SpawnBehavior.h"
#include "ForceMovementBehavior.h"
#include "ImmunityBehavior.h"
#include "InterruptBehavior.h"
#include "PlayEffectBehavior.h"
#include "DamageAbsorptionBehavior.h"
#include "BlockBehavior.h"
#include "ClearTargetBehavior.h"
#include "PullToPointBehavior.h"
#include "EndBehavior.h"
#include "ChangeOrientationBehavior.h"
#include "OverTimeBehavior.h"
#include "ApplyBuffBehavior.h"
#include "CarBoostBehavior.h"
#include "SkillEventBehavior.h"
#include "SpeedBehavior.h"
#include "DamageReductionBehavior.h"
//CDClient includes
#include "CDBehaviorParameterTable.h"
#include "CDClientDatabase.h"
#include "CDClientManager.h"
//Other includes
#include "EntityManager.h"
#include "RenderComponent.h"
#include "DestroyableComponent.h"
std::map<uint32_t, Behavior*> Behavior::Cache = {};
CDBehaviorParameterTable* Behavior::BehaviorParameterTable = nullptr;
Behavior* Behavior::GetBehavior(const uint32_t behaviorId)
{
if (BehaviorParameterTable == nullptr)
{
BehaviorParameterTable = CDClientManager::Instance()->GetTable<CDBehaviorParameterTable>("BehaviorParameter");
}
const auto pair = Cache.find(behaviorId);
if (pair == Cache.end())
{
return nullptr;
}
return static_cast<Behavior*>(pair->second);
}
Behavior* Behavior::CreateBehavior(const uint32_t behaviorId)
{
auto* cached = GetBehavior(behaviorId);
if (cached != nullptr)
{
return cached;
}
if (behaviorId == 0)
{
return new EmptyBehavior(0);
}
const auto templateId = GetBehaviorTemplate(behaviorId);
Behavior* behavior = nullptr;
switch (templateId)
{
case BehaviorTemplates::BEHAVIOR_EMPTY: break;
case BehaviorTemplates::BEHAVIOR_BASIC_ATTACK:
behavior = new BasicAttackBehavior(behaviorId);
break;
case BehaviorTemplates::BEHAVIOR_TAC_ARC:
behavior = new TacArcBehavior(behaviorId);
break;
case BehaviorTemplates::BEHAVIOR_AND:
behavior = new AndBehavior(behaviorId);
break;
case BehaviorTemplates::BEHAVIOR_PROJECTILE_ATTACK:
behavior = new ProjectileAttackBehavior(behaviorId);
break;
case BehaviorTemplates::BEHAVIOR_HEAL:
behavior = new HealBehavior(behaviorId);
break;
case BehaviorTemplates::BEHAVIOR_MOVEMENT_SWITCH:
behavior = new MovementSwitchBehavior(behaviorId);
break;
case BehaviorTemplates::BEHAVIOR_AREA_OF_EFFECT:
behavior = new AreaOfEffectBehavior(behaviorId);
break;
case BehaviorTemplates::BEHAVIOR_PLAY_EFFECT:
behavior = new PlayEffectBehavior(behaviorId);
break;
case BehaviorTemplates::BEHAVIOR_IMMUNITY:
behavior = new ImmunityBehavior(behaviorId);
break;
case BehaviorTemplates::BEHAVIOR_DAMAGE_BUFF: break;
case BehaviorTemplates::BEHAVIOR_DAMAGE_ABSORBTION:
behavior = new DamageAbsorptionBehavior(behaviorId);
break;
case BehaviorTemplates::BEHAVIOR_OVER_TIME:
behavior = new OverTimeBehavior(behaviorId);
break;
case BehaviorTemplates::BEHAVIOR_IMAGINATION:
behavior = new ImaginationBehavior(behaviorId);
break;
case BehaviorTemplates::BEHAVIOR_TARGET_CASTER:
behavior = new TargetCasterBehavior(behaviorId);
break;
case BehaviorTemplates::BEHAVIOR_STUN:
behavior = new StunBehavior(behaviorId);
break;
case BehaviorTemplates::BEHAVIOR_DURATION:
behavior = new DurationBehavior(behaviorId);
break;
case BehaviorTemplates::BEHAVIOR_KNOCKBACK:
behavior = new KnockbackBehavior(behaviorId);
break;
case BehaviorTemplates::BEHAVIOR_ATTACK_DELAY:
behavior = new AttackDelayBehavior(behaviorId);
break;
case BehaviorTemplates::BEHAVIOR_CAR_BOOST:
behavior = new CarBoostBehavior(behaviorId);
break;
case BehaviorTemplates::BEHAVIOR_FALL_SPEED: break;
case BehaviorTemplates::BEHAVIOR_SHIELD: break;
case BehaviorTemplates::BEHAVIOR_REPAIR_ARMOR:
behavior = new RepairBehavior(behaviorId);
break;
case BehaviorTemplates::BEHAVIOR_SPEED:
behavior = new SpeedBehavior(behaviorId);
break;
case BehaviorTemplates::BEHAVIOR_DARK_INSPIRATION: break;
case BehaviorTemplates::BEHAVIOR_LOOT_BUFF: break;
case BehaviorTemplates::BEHAVIOR_VENTURE_VISION: break;
case BehaviorTemplates::BEHAVIOR_SPAWN_OBJECT:
behavior = new SpawnBehavior(behaviorId);
break;
case BehaviorTemplates::BEHAVIOR_LAY_BRICK: break;
case BehaviorTemplates::BEHAVIOR_SWITCH:
behavior = new SwitchBehavior(behaviorId);
break;
case BehaviorTemplates::BEHAVIOR_BUFF:
behavior = new BuffBehavior(behaviorId);
break;
case BehaviorTemplates::BEHAVIOR_JETPACK: break;
case BehaviorTemplates::BEHAVIOR_SKILL_EVENT:
behavior = new SkillEventBehavior(behaviorId);
break;
case BehaviorTemplates::BEHAVIOR_CONSUME_ITEM: break;
case BehaviorTemplates::BEHAVIOR_SKILL_CAST_FAILED:
behavior = new SkillCastFailedBehavior(behaviorId);
break;
case BehaviorTemplates::BEHAVIOR_IMITATION_SKUNK_STINK: break;
case BehaviorTemplates::BEHAVIOR_CHANGE_IDLE_FLAGS: break;
case BehaviorTemplates::BEHAVIOR_APPLY_BUFF:
behavior = new ApplyBuffBehavior(behaviorId);
break;
case BehaviorTemplates::BEHAVIOR_CHAIN:
behavior = new ChainBehavior(behaviorId);
break;
case BehaviorTemplates::BEHAVIOR_CHANGE_ORIENTATION:
behavior = new ChangeOrientationBehavior(behaviorId);
break;
case BehaviorTemplates::BEHAVIOR_FORCE_MOVEMENT:
behavior = new ForceMovementBehavior(behaviorId);
break;
case BehaviorTemplates::BEHAVIOR_INTERRUPT:
behavior = new InterruptBehavior(behaviorId);
break;
case BehaviorTemplates::BEHAVIOR_ALTER_COOLDOWN: break;
case BehaviorTemplates::BEHAVIOR_CHARGE_UP:
behavior = new ChargeUpBehavior(behaviorId);
break;
case BehaviorTemplates::BEHAVIOR_SWITCH_MULTIPLE:
behavior = new SwitchMultipleBehavior(behaviorId);
break;
case BehaviorTemplates::BEHAVIOR_START:
behavior = new StartBehavior(behaviorId);
break;
case BehaviorTemplates::BEHAVIOR_END:
behavior = new EndBehavior(behaviorId);
break;
case BehaviorTemplates::BEHAVIOR_ALTER_CHAIN_DELAY: break;
case BehaviorTemplates::BEHAVIOR_CAMERA: break;
case BehaviorTemplates::BEHAVIOR_REMOVE_BUFF: break;
case BehaviorTemplates::BEHAVIOR_GRAB: break;
case BehaviorTemplates::BEHAVIOR_MODULAR_BUILD: break;
case BehaviorTemplates::BEHAVIOR_NPC_COMBAT_SKILL:
behavior = new NpcCombatSkillBehavior(behaviorId);
break;
case BehaviorTemplates::BEHAVIOR_BLOCK:
behavior = new BlockBehavior(behaviorId);
break;
case BehaviorTemplates::BEHAVIOR_VERIFY:
behavior = new VerifyBehavior(behaviorId);
break;
case BehaviorTemplates::BEHAVIOR_TAUNT:
behavior = new TauntBehavior(behaviorId);
break;
case BehaviorTemplates::BEHAVIOR_AIR_MOVEMENT:
behavior = new AirMovementBehavior(behaviorId);
break;
case BehaviorTemplates::BEHAVIOR_SPAWN_QUICKBUILD:
behavior = new SpawnBehavior(behaviorId);
break;
case BehaviorTemplates::BEHAVIOR_PULL_TO_POINT:
behavior = new PullToPointBehavior(behaviorId);
break;
case BehaviorTemplates::BEHAVIOR_PROPERTY_ROTATE: break;
case BehaviorTemplates::BEHAVIOR_DAMAGE_REDUCTION:
behavior = new DamageReductionBehavior(behaviorId);
break;
case BehaviorTemplates::BEHAVIOR_PROPERTY_TELEPORT: break;
case BehaviorTemplates::BEHAVIOR_PROPERTY_CLEAR_TARGET:
behavior = new ClearTargetBehavior(behaviorId);
break;
case BehaviorTemplates::BEHAVIOR_TAKE_PICTURE: break;
case BehaviorTemplates::BEHAVIOR_MOUNT: break;
case BehaviorTemplates::BEHAVIOR_SKILL_SET: break;
default:
//Game::logger->Log("Behavior", "Failed to load behavior with invalid template id (%i)!\n", templateId);
break;
}
if (behavior == nullptr)
{
//Game::logger->Log("Behavior", "Failed to load unimplemented template id (%i)!\n", templateId);
behavior = new EmptyBehavior(behaviorId);
}
behavior->Load();
return behavior;
}
BehaviorTemplates Behavior::GetBehaviorTemplate(const uint32_t behaviorId)
{
std::stringstream query;
query << "SELECT templateID FROM BehaviorTemplate WHERE behaviorID = " << std::to_string(behaviorId);
auto result = CDClientDatabase::ExecuteQuery(query.str());
// Make sure we do not proceed if we are trying to load an invalid behavior
if (result.eof())
{
if (behaviorId != 0)
{
Game::logger->Log("Behavior::GetBehaviorTemplate", "Failed to load behavior template with id (%i)!\n", behaviorId);
}
return BehaviorTemplates::BEHAVIOR_EMPTY;
}
const auto id = static_cast<BehaviorTemplates>(result.getIntField(0));
result.finalize();
return id;
}
// For use with enemies, to display the correct damage animations on the players
void Behavior::PlayFx(std::u16string type, const LWOOBJID target, const LWOOBJID secondary)
{
auto* targetEntity = EntityManager::Instance()->GetEntity(target);
if (targetEntity == nullptr)
{
return;
}
const auto effectId = this->m_effectId;
if (effectId == 0)
{
GameMessages::SendPlayFXEffect(targetEntity, -1, type, "", secondary, 1, 1, true);
return;
}
auto* renderComponent = targetEntity->GetComponent<RenderComponent>();
const auto typeString = GeneralUtils::UTF16ToWTF8(type);
if (m_effectNames == nullptr)
{
m_effectNames = new std::unordered_map<std::string, std::string>();
}
else
{
const auto pair = m_effectNames->find(typeString);
if (type.empty())
{
type = GeneralUtils::ASCIIToUTF16(*m_effectType);
}
if (pair != m_effectNames->end())
{
if (renderComponent == nullptr)
{
GameMessages::SendPlayFXEffect(targetEntity, effectId, type, pair->second, secondary, 1, 1, true);
return;
}
renderComponent->PlayEffect(effectId, type, pair->second, secondary);
return;
}
}
std::stringstream query;
if (!type.empty())
{
query << "SELECT effectName FROM BehaviorEffect WHERE effectType = '" << typeString << "' AND effectID = " << std::to_string(effectId) << ";";
}
else
{
query << "SELECT effectName, effectType FROM BehaviorEffect WHERE effectID = " << std::to_string(effectId) << ";";
}
auto result = CDClientDatabase::ExecuteQuery(query.str());
if (result.eof() || result.fieldIsNull(0))
{
return;
}
const auto name = std::string(result.getStringField(0));
if (type.empty())
{
const auto typeResult = result.getStringField(1);
type = GeneralUtils::ASCIIToUTF16(typeResult);
m_effectType = new std::string(typeResult);
}
result.finalize();
m_effectNames->insert_or_assign(typeString, name);
if (renderComponent == nullptr)
{
GameMessages::SendPlayFXEffect(targetEntity, effectId, type, name, secondary, 1, 1, true);
return;
}
renderComponent->PlayEffect(effectId, type, name, secondary);
}
Behavior::Behavior(const uint32_t behaviorId)
{
this->m_behaviorId = behaviorId;
// Add to cache
Cache.insert_or_assign(behaviorId, this);
if (behaviorId == 0) {
this->m_effectId = 0;
this->m_effectHandle = nullptr;
this->m_templateId = BehaviorTemplates::BEHAVIOR_EMPTY;
}
/*
* Get standard info
*/
std::stringstream query;
query << "SELECT templateID, effectID, effectHandle FROM BehaviorTemplate WHERE behaviorID = " << std::to_string(behaviorId);
auto result = CDClientDatabase::ExecuteQuery(query.str());
// Make sure we do not proceed if we are trying to load an invalid behavior
if (result.eof())
{
Game::logger->Log("Behavior", "Failed to load behavior with id (%i)!\n", behaviorId);
this->m_effectId = 0;
this->m_effectHandle = nullptr;
this->m_templateId = BehaviorTemplates::BEHAVIOR_EMPTY;
return;
}
this->m_templateId = static_cast<BehaviorTemplates>(result.getIntField(0));
this->m_effectId = result.getIntField(1);
if (!result.fieldIsNull(2))
{
const std::string effectHandle = result.getStringField(2);
if (effectHandle == "")
{
this->m_effectHandle = nullptr;
}
else
{
this->m_effectHandle = new std::string(effectHandle);
}
}
else
{
this->m_effectHandle = nullptr;
}
result.finalize();
}
float Behavior::GetFloat(const std::string& name) const
{
return BehaviorParameterTable->GetEntry(this->m_behaviorId, name);
}
bool Behavior::GetBoolean(const std::string& name) const
{
return GetFloat(name) > 0;
}
int32_t Behavior::GetInt(const std::string& name) const
{
return static_cast<int32_t>(GetFloat(name));
}
Behavior* Behavior::GetAction(const std::string& name) const
{
const auto id = GetInt(name);
return CreateBehavior(id);
}
Behavior* Behavior::GetAction(float value) const
{
return CreateBehavior(static_cast<int32_t>(value));
}
std::map<std::string, float> Behavior::GetParameterNames() const
{
std::map<std::string, float> parameters;
std::stringstream query;
query << "SELECT parameterID, value FROM BehaviorParameter WHERE behaviorID = " << std::to_string(this->m_behaviorId);
auto tableData = CDClientDatabase::ExecuteQuery(query.str());
while (!tableData.eof())
{
parameters.insert_or_assign(tableData.getStringField(0, ""), tableData.getFloatField(1, 0));
tableData.nextRow();
}
tableData.finalize();
return parameters;
}
void Behavior::Load()
{
}
void Behavior::Handle(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch)
{
}
void Behavior::Sync(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch)
{
}
void Behavior::UnCast(BehaviorContext* context, BehaviorBranchContext branch)
{
}
void Behavior::Timer(BehaviorContext* context, BehaviorBranchContext branch, LWOOBJID second)
{
}
void Behavior::End(BehaviorContext* context, BehaviorBranchContext branch, LWOOBJID second)
{
}
void Behavior::Calculate(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch)
{
}
void Behavior::SyncCalculation(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch)
{
}
Behavior::~Behavior()
{
delete m_effectNames;
delete m_effectType;
delete m_effectHandle;
}

View File

@@ -0,0 +1,92 @@
#pragma once
#include <map>
#include <string>
#include <vector>
#include <unordered_map>
#include "BitStream.h"
#include "BehaviorTemplates.h"
#include "dCommonVars.h"
struct BehaviorContext;
struct BehaviorBranchContext;
class CDBehaviorParameterTable;
class Behavior
{
public:
/*
* Static
*/
static std::map<uint32_t, Behavior*> Cache;
static CDBehaviorParameterTable* BehaviorParameterTable;
static Behavior* GetBehavior(uint32_t behaviorId);
static Behavior* CreateBehavior(uint32_t behaviorId);
static BehaviorTemplates GetBehaviorTemplate(uint32_t behaviorId);
/*
* Utilities
*/
void PlayFx(std::u16string type, LWOOBJID target, LWOOBJID secondary = LWOOBJID_EMPTY);
/*
* Members
*/
uint32_t m_behaviorId;
BehaviorTemplates m_templateId;
uint32_t m_effectId;
std::string* m_effectHandle = nullptr;
std::unordered_map<std::string, std::string>* m_effectNames = nullptr;
std::string* m_effectType = nullptr;
/*
* Behavior parameters
*/
float GetFloat(const std::string& name) const;
bool GetBoolean(const std::string& name) const;
int32_t GetInt(const std::string& name) const;
Behavior* GetAction(const std::string& name) const;
Behavior* GetAction(float value) const;
std::map<std::string, float> GetParameterNames() const;
/*
* Virtual
*/
virtual void Load();
// Player side
virtual void Handle(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch);
virtual void Sync(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch);
virtual void UnCast(BehaviorContext* context, BehaviorBranchContext branch);
virtual void Timer(BehaviorContext* context, BehaviorBranchContext branch, LWOOBJID second);
virtual void End(BehaviorContext* context, BehaviorBranchContext branch, LWOOBJID second);
// Npc side
virtual void Calculate(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch);
virtual void SyncCalculation(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch);
/*
* Creations/destruction
*/
explicit Behavior(uint32_t behaviorId);
virtual ~Behavior();
};

View File

@@ -0,0 +1,15 @@
#include "BehaviorBranchContext.h"
BehaviorBranchContext::BehaviorBranchContext()
{
this->isProjectile = false;
}
BehaviorBranchContext::BehaviorBranchContext(const LWOOBJID target, const float duration, const NiPoint3& referencePosition)
{
this->target = target;
this->duration = duration;
this->referencePosition = referencePosition;
this->isProjectile = false;
}

View File

@@ -0,0 +1,21 @@
#pragma once
#include "dCommonVars.h"
#include "NiPoint3.h"
struct BehaviorBranchContext
{
LWOOBJID target = LWOOBJID_EMPTY;
float duration = 0;
NiPoint3 referencePosition = {};
bool isProjectile = false;
uint32_t start = 0;
BehaviorBranchContext();
BehaviorBranchContext(LWOOBJID target, float duration = 0, const NiPoint3& referencePosition = NiPoint3(0, 0, 0));
};

View File

@@ -0,0 +1,400 @@
#include "BehaviorContext.h"
#include "Behavior.h"
#include "BehaviorBranchContext.h"
#include "EntityManager.h"
#include "SkillComponent.h"
#include "Game.h"
#include "dLogger.h"
#include "dServer.h"
#include "PacketUtils.h"
#include <sstream>
#include "DestroyableComponent.h"
#include "PhantomPhysicsComponent.h"
#include "RebuildComponent.h"
BehaviorSyncEntry::BehaviorSyncEntry()
{
}
BehaviorTimerEntry::BehaviorTimerEntry()
{
}
BehaviorEndEntry::BehaviorEndEntry()
{
}
uint32_t BehaviorContext::GetUniqueSkillId() const
{
auto* entity = EntityManager::Instance()->GetEntity(this->originator);
if (entity == nullptr)
{
Game::logger->Log("BehaviorContext", "Invalid entity for (%llu)!\n", this->originator);
return 0;
}
auto* component = entity->GetComponent<SkillComponent>();
if (component == nullptr)
{
Game::logger->Log("BehaviorContext", "No skill component attached to (%llu)!\n", this->originator);;
return 0;
}
return component->GetUniqueSkillId();
}
void BehaviorContext::RegisterSyncBehavior(const uint32_t syncId, Behavior* behavior, const BehaviorBranchContext& branchContext)
{
auto entry = BehaviorSyncEntry();
entry.handle = syncId;
entry.behavior = behavior;
entry.branchContext = branchContext;
this->syncEntries.push_back(entry);
}
void BehaviorContext::RegisterTimerBehavior(Behavior* behavior, const BehaviorBranchContext& branchContext, const LWOOBJID second)
{
BehaviorTimerEntry entry
;
entry.time = branchContext.duration;
entry.behavior = behavior;
entry.branchContext = branchContext;
entry.second = second;
this->timerEntries.push_back(entry);
}
void BehaviorContext::RegisterEndBehavior(Behavior* behavior, const BehaviorBranchContext& branchContext, const LWOOBJID second)
{
BehaviorEndEntry entry;
entry.behavior = behavior;
entry.branchContext = branchContext;
entry.second = second;
entry.start = branchContext.start;
this->endEntries.push_back(entry);
}
void BehaviorContext::ScheduleUpdate(const LWOOBJID id)
{
if (std::find(this->scheduledUpdates.begin(), this->scheduledUpdates.end(), id) != this->scheduledUpdates.end())
{
return;
}
this->scheduledUpdates.push_back(id);
}
void BehaviorContext::ExecuteUpdates()
{
for (const auto& id : this->scheduledUpdates)
{
auto* entity = EntityManager::Instance()->GetEntity(id);
if (entity == nullptr) continue;
EntityManager::Instance()->SerializeEntity(entity);
}
this->scheduledUpdates.clear();
}
void BehaviorContext::SyncBehavior(const uint32_t syncId, RakNet::BitStream* bitStream)
{
BehaviorSyncEntry entry;
auto found = false;
/*
* There may be more than one of each handle
*/
for (auto i = 0u; i < this->syncEntries.size(); ++i)
{
const auto syncEntry = this->syncEntries.at(i);
if (syncEntry.handle == syncId)
{
found = true;
entry = syncEntry;
this->syncEntries.erase(this->syncEntries.begin() + i);
break;
}
}
if (!found)
{
Game::logger->Log("BehaviorContext", "Failed to find behavior sync entry with sync id (%i)!\n", syncId);
return;
}
auto* behavior = entry.behavior;
const auto branch = entry.branchContext;
if (behavior == nullptr)
{
Game::logger->Log("BehaviorContext", "Invalid behavior for sync id (%i)!\n", syncId);
return;
}
behavior->Sync(this, bitStream, branch);
}
void BehaviorContext::Update(const float deltaTime)
{
for (auto i = 0u; i < this->timerEntries.size(); ++i)
{
auto entry = this->timerEntries.at(i);
if (entry.time > 0)
{
entry.time -= deltaTime;
this->timerEntries[i] = entry;
}
if (entry.time > 0)
{
continue;
}
entry.behavior->Timer(this, entry.branchContext, entry.second);
}
std::vector<BehaviorTimerEntry> valid;
for (const auto& entry : this->timerEntries)
{
if (entry.time <= 0)
{
continue;
}
valid.push_back(entry);
}
this->timerEntries = valid;
}
void BehaviorContext::SyncCalculation(const uint32_t syncId, const float time, Behavior* behavior, const BehaviorBranchContext& branch, const bool ignoreInterrupts)
{
BehaviorSyncEntry entry;
entry.behavior = behavior;
entry.time = time;
entry.branchContext = branch;
entry.handle = syncId;
entry.ignoreInterrupts = ignoreInterrupts;
this->syncEntries.push_back(entry);
}
void BehaviorContext::InvokeEnd(const uint32_t id)
{
std::vector<BehaviorEndEntry> entries;
for (const auto& entry : this->endEntries)
{
if (entry.start == id)
{
entry.behavior->End(this, entry.branchContext, entry.second);
continue;
}
entries.push_back(entry);
}
this->endEntries = entries;
}
bool BehaviorContext::CalculateUpdate(const float deltaTime)
{
auto any = false;
for (auto i = 0u; i < this->syncEntries.size(); ++i)
{
auto entry = this->syncEntries.at(i);
if (entry.time > 0)
{
entry.time -= deltaTime;
this->syncEntries[i] = entry;
}
if (entry.time > 0)
{
any = true;
continue;
}
// Echo sync
GameMessages::EchoSyncSkill echo;
echo.bDone = true;
echo.uiBehaviorHandle = entry.handle;
echo.uiSkillHandle = this->skillUId;
auto* bitStream = new RakNet::BitStream();
// Calculate sync
entry.behavior->SyncCalculation(this, bitStream, entry.branchContext);
if (!clientInitalized)
{
echo.sBitStream.assign((char*) bitStream->GetData(), bitStream->GetNumberOfBytesUsed());
// Write message
RakNet::BitStream message;
PacketUtils::WriteHeader(message, CLIENT, MSG_CLIENT_GAME_MSG);
message.Write(this->originator);
echo.Serialize(&message);
Game::server->Send(&message, UNASSIGNED_SYSTEM_ADDRESS, true);
}
ExecuteUpdates();
delete bitStream;
}
std::vector<BehaviorSyncEntry> valid;
for (const auto& entry : this->syncEntries)
{
if (entry.time <= 0)
{
continue;
}
valid.push_back(entry);
}
this->syncEntries = valid;
return any;
}
void BehaviorContext::Interrupt()
{
std::vector<BehaviorSyncEntry> keptSync {};
for (const auto& entry : this->syncEntries)
{
if (!entry.ignoreInterrupts) continue;
keptSync.push_back(entry);
}
this->syncEntries = keptSync;
}
void BehaviorContext::Reset()
{
for (const auto& entry : this->timerEntries)
{
entry.behavior->Timer(this, entry.branchContext, entry.second);
}
for (const auto& entry : this->endEntries)
{
entry.behavior->End(this, entry.branchContext, entry.second);
}
this->endEntries.clear();
this->timerEntries.clear();
this->syncEntries.clear();
this->scheduledUpdates.clear();
}
std::vector<LWOOBJID> BehaviorContext::GetValidTargets(int32_t ignoreFaction, int32_t includeFaction) const
{
auto* entity = EntityManager::Instance()->GetEntity(this->caster);
std::vector<LWOOBJID> targets;
if (entity == nullptr)
{
Game::logger->Log("BehaviorContext", "Invalid entity for (%llu)!\n", this->originator);
return targets;
}
if (!ignoreFaction && !includeFaction)
{
for (auto entry : entity->GetTargetsInPhantom())
{
auto* instance = EntityManager::Instance()->GetEntity(entry);
if (instance == nullptr)
{
continue;
}
targets.push_back(entry);
}
}
if (ignoreFaction || includeFaction || (!entity->HasComponent(COMPONENT_TYPE_PHANTOM_PHYSICS) && !entity->HasComponent(COMPONENT_TYPE_CONTROLLABLE_PHYSICS) && targets.empty()))
{
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))
{
targets.push_back(id);
}
}
}
return targets;
}
BehaviorContext::BehaviorContext(const LWOOBJID originator, const bool calculation)
{
this->originator = originator;
this->syncEntries = {};
this->timerEntries = {};
if (calculation)
{
this->skillUId = GetUniqueSkillId();
}
else
{
this->skillUId = 0;
}
}
BehaviorContext::~BehaviorContext()
{
Reset();
}

View File

@@ -0,0 +1,110 @@
#pragma once
#include "RakPeerInterface.h"
#include "dCommonVars.h"
#include "BehaviorBranchContext.h"
#include "GameMessages.h"
#include <vector>
class Behavior;
struct BehaviorSyncEntry
{
uint32_t handle = 0;
float time = 0;
bool ignoreInterrupts = false;
Behavior* behavior = nullptr;
BehaviorBranchContext branchContext;
BehaviorSyncEntry();
};
struct BehaviorTimerEntry
{
float time = 0;
Behavior* behavior = nullptr;
BehaviorBranchContext branchContext;
LWOOBJID second = LWOOBJID_EMPTY;
BehaviorTimerEntry();
};
struct BehaviorEndEntry
{
Behavior* behavior = nullptr;
uint32_t start = 0;
BehaviorBranchContext branchContext;
LWOOBJID second = LWOOBJID_EMPTY;
BehaviorEndEntry();
};
struct BehaviorContext
{
LWOOBJID originator = LWOOBJID_EMPTY;
bool foundTarget = false;
float skillTime = 0;
uint32_t skillUId = 0;
bool failed = false;
bool clientInitalized = false;
std::vector<BehaviorSyncEntry> syncEntries;
std::vector<BehaviorTimerEntry> timerEntries;
std::vector<BehaviorEndEntry> endEntries;
std::vector<LWOOBJID> scheduledUpdates;
bool unmanaged = false;
LWOOBJID caster = LWOOBJID_EMPTY;
uint32_t GetUniqueSkillId() const;
void RegisterSyncBehavior(uint32_t syncId, Behavior* behavior, const BehaviorBranchContext& branchContext);
void RegisterTimerBehavior(Behavior* behavior, const BehaviorBranchContext& branchContext, LWOOBJID second = LWOOBJID_EMPTY);
void RegisterEndBehavior(Behavior* behavior, const BehaviorBranchContext& branchContext, LWOOBJID second = LWOOBJID_EMPTY);
void ScheduleUpdate(LWOOBJID id);
void ExecuteUpdates();
void SyncBehavior(uint32_t syncId, RakNet::BitStream* bitStream);
void Update(float deltaTime);
void SyncCalculation(uint32_t syncId, float time, Behavior* behavior, const BehaviorBranchContext& branch, bool ignoreInterrupts = false);
void InvokeEnd(uint32_t id);
bool CalculateUpdate(float deltaTime);
void Interrupt();
void Reset();
std::vector<LWOOBJID> GetValidTargets(int32_t ignoreFaction = 0, int32_t includeFaction = 0) const;
explicit BehaviorContext(LWOOBJID originator, bool calculation = false);
~BehaviorContext();
};

View File

@@ -0,0 +1,11 @@
#pragma once
enum class BehaviorSlot
{
Invalid = -1,
Primary,
Offhand,
Neck,
Head,
Consumable
};

View File

@@ -0,0 +1 @@
#include "BehaviorTemplates.h"

View File

@@ -0,0 +1,70 @@
#pragma once
enum class BehaviorTemplates : unsigned int {
BEHAVIOR_EMPTY, // Not a real behavior, indicates invalid behaviors
BEHAVIOR_BASIC_ATTACK,
BEHAVIOR_TAC_ARC,
BEHAVIOR_AND,
BEHAVIOR_PROJECTILE_ATTACK,
BEHAVIOR_HEAL,
BEHAVIOR_MOVEMENT_SWITCH,
BEHAVIOR_AREA_OF_EFFECT,
BEHAVIOR_PLAY_EFFECT,
BEHAVIOR_IMMUNITY,
BEHAVIOR_DAMAGE_BUFF,
BEHAVIOR_DAMAGE_ABSORBTION,
BEHAVIOR_OVER_TIME,
BEHAVIOR_IMAGINATION,
BEHAVIOR_TARGET_CASTER,
BEHAVIOR_STUN,
BEHAVIOR_DURATION,
BEHAVIOR_KNOCKBACK,
BEHAVIOR_ATTACK_DELAY,
BEHAVIOR_CAR_BOOST,
BEHAVIOR_FALL_SPEED,
BEHAVIOR_SHIELD,
BEHAVIOR_REPAIR_ARMOR,
BEHAVIOR_SPEED,
BEHAVIOR_DARK_INSPIRATION,
BEHAVIOR_LOOT_BUFF,
BEHAVIOR_VENTURE_VISION,
BEHAVIOR_SPAWN_OBJECT,
BEHAVIOR_LAY_BRICK,
BEHAVIOR_SWITCH,
BEHAVIOR_BUFF,
BEHAVIOR_JETPACK,
BEHAVIOR_SKILL_EVENT,
BEHAVIOR_CONSUME_ITEM,
BEHAVIOR_SKILL_CAST_FAILED,
BEHAVIOR_IMITATION_SKUNK_STINK,
BEHAVIOR_CHANGE_IDLE_FLAGS,
BEHAVIOR_APPLY_BUFF,
BEHAVIOR_CHAIN,
BEHAVIOR_CHANGE_ORIENTATION,
BEHAVIOR_FORCE_MOVEMENT,
BEHAVIOR_INTERRUPT,
BEHAVIOR_ALTER_COOLDOWN,
BEHAVIOR_CHARGE_UP,
BEHAVIOR_SWITCH_MULTIPLE,
BEHAVIOR_START,
BEHAVIOR_END,
BEHAVIOR_ALTER_CHAIN_DELAY,
BEHAVIOR_CAMERA,
BEHAVIOR_REMOVE_BUFF,
BEHAVIOR_GRAB,
BEHAVIOR_MODULAR_BUILD,
BEHAVIOR_NPC_COMBAT_SKILL,
BEHAVIOR_BLOCK,
BEHAVIOR_VERIFY,
BEHAVIOR_TAUNT,
BEHAVIOR_AIR_MOVEMENT,
BEHAVIOR_SPAWN_QUICKBUILD,
BEHAVIOR_PULL_TO_POINT,
BEHAVIOR_PROPERTY_ROTATE,
BEHAVIOR_DAMAGE_REDUCTION,
BEHAVIOR_PROPERTY_TELEPORT,
BEHAVIOR_PROPERTY_CLEAR_TARGET,
BEHAVIOR_TAKE_PICTURE,
BEHAVIOR_MOUNT,
BEHAVIOR_SKILL_SET
};

View File

@@ -0,0 +1,85 @@
#include "BlockBehavior.h"
#include "BehaviorContext.h"
#include "BehaviorBranchContext.h"
#include "EntityManager.h"
#include "Game.h"
#include "dLogger.h"
#include "DestroyableComponent.h"
void BlockBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch)
{
const auto target = context->originator;
auto* entity = EntityManager::Instance()->GetEntity(target);
if (entity == nullptr)
{
Game::logger->Log("DamageAbsorptionBehavior", "Failed to find target (%llu)!\n", branch.target);
return;
}
auto* destroyableComponent = entity->GetComponent<DestroyableComponent>();
if (destroyableComponent == nullptr)
{
return;
}
destroyableComponent->SetAttacksToBlock(this->m_numAttacksCanBlock);
if (branch.start > 0)
{
context->RegisterEndBehavior(this, branch);
}
else if (branch.duration > 0)
{
context->RegisterTimerBehavior(this, branch);
}
}
void BlockBehavior::Calculate(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch)
{
Handle(context, bitStream, branch);
}
void BlockBehavior::UnCast(BehaviorContext* context, BehaviorBranchContext branch)
{
const auto target = context->originator;
auto* entity = EntityManager::Instance()->GetEntity(target);
if (entity == nullptr)
{
Game::logger->Log("DamageAbsorptionBehavior", "Failed to find target (%llu)!\n", branch.target);
return;
}
auto* destroyableComponent = entity->GetComponent<DestroyableComponent>();
destroyableComponent->SetAttacksToBlock(this->m_numAttacksCanBlock);
if (destroyableComponent == nullptr)
{
return;
}
destroyableComponent->SetAttacksToBlock(0);
}
void BlockBehavior::Timer(BehaviorContext* context, BehaviorBranchContext branch, LWOOBJID second)
{
UnCast(context, branch);
}
void BlockBehavior::End(BehaviorContext* context, BehaviorBranchContext branch, LWOOBJID second)
{
UnCast(context, branch);
}
void BlockBehavior::Load()
{
this->m_numAttacksCanBlock = GetInt("num_attacks_can_block");
}

View File

@@ -0,0 +1,28 @@
#pragma once
#include "Behavior.h"
class BlockBehavior final : public Behavior
{
public:
int32_t m_numAttacksCanBlock;
/*
* Inherited
*/
explicit BlockBehavior(const uint32_t behaviorId) : Behavior(behaviorId)
{
}
void Handle(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) override;
void Calculate(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) override;
void UnCast(BehaviorContext* context, BehaviorBranchContext branch) override;
void Timer(BehaviorContext* context, BehaviorBranchContext branch, LWOOBJID second) override;
void End(BehaviorContext* context, BehaviorBranchContext branch, LWOOBJID second) override;
void Load() override;
};

View File

@@ -0,0 +1,97 @@
#include "BuffBehavior.h"
#include "BehaviorContext.h"
#include "BehaviorBranchContext.h"
#include "EntityManager.h"
#include "Game.h"
#include "dLogger.h"
#include "DestroyableComponent.h"
void BuffBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch)
{
const auto target = branch.target != LWOOBJID_EMPTY ? branch.target : context->originator;
auto* entity = EntityManager::Instance()->GetEntity(target);
if (entity == nullptr)
{
Game::logger->Log("BuffBehavior", "Invalid target (%llu)!\n", target);
return;
}
auto* component = entity->GetComponent<DestroyableComponent>();
if (component == nullptr)
{
Game::logger->Log("BuffBehavior", "Invalid target, no destroyable component (%llu)!\n", target);
return;
}
component->SetMaxHealth(component->GetMaxHealth() + this->m_health);
component->SetMaxArmor(component->GetMaxArmor() + this->m_armor);
component->SetMaxImagination(component->GetMaxImagination() + this->m_imagination);
EntityManager::Instance()->SerializeEntity(entity);
if (!context->unmanaged)
{
if (branch.duration > 0)
{
context->RegisterTimerBehavior(this, branch);
}
else if (branch.start > 0)
{
context->RegisterEndBehavior(this, branch);
}
}
}
void BuffBehavior::UnCast(BehaviorContext* context, BehaviorBranchContext branch)
{
const auto target = branch.target != LWOOBJID_EMPTY ? branch.target : context->originator;
auto* entity = EntityManager::Instance()->GetEntity(target);
if (entity == nullptr)
{
Game::logger->Log("BuffBehavior", "Invalid target (%llu)!\n", target);
return;
}
auto* component = entity->GetComponent<DestroyableComponent>();
if (component == nullptr)
{
Game::logger->Log("BuffBehavior", "Invalid target, no destroyable component (%llu)!\n", target);
return;
}
component->SetMaxHealth(component->GetMaxHealth() - this->m_health);
component->SetMaxArmor(component->GetMaxArmor() - this->m_armor);
component->SetMaxImagination(component->GetMaxImagination() - this->m_imagination);
EntityManager::Instance()->SerializeEntity(entity);
}
void BuffBehavior::Timer(BehaviorContext* context, const BehaviorBranchContext branch, LWOOBJID second)
{
UnCast(context, branch);
}
void BuffBehavior::End(BehaviorContext* context, const BehaviorBranchContext branch, LWOOBJID second)
{
UnCast(context, branch);
}
void BuffBehavior::Load()
{
this->m_health = GetInt("life");
this->m_armor = GetInt("armor");
this->m_imagination = GetInt("imag");
}

View File

@@ -0,0 +1,29 @@
#pragma once
#include "Behavior.h"
class BuffBehavior final : public Behavior
{
public:
uint32_t m_health;
uint32_t m_armor;
uint32_t m_imagination;
/*
* Inherited
*/
explicit BuffBehavior(const uint32_t behaviorId) : Behavior(behaviorId)
{
}
void Handle(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) override;
void UnCast(BehaviorContext* context, BehaviorBranchContext branch) override;
void Timer(BehaviorContext* context, BehaviorBranchContext branch, LWOOBJID second) override;
void End(BehaviorContext* context, BehaviorBranchContext branch, LWOOBJID second) override;
void Load() override;
};

View File

@@ -0,0 +1,51 @@
#include "CarBoostBehavior.h"
#include "BehaviorBranchContext.h"
#include "GameMessages.h"
#include "EntityManager.h"
#include "BehaviorContext.h"
#include "CharacterComponent.h"
#include "Game.h"
#include "dLogger.h"
#include "PossessableComponent.h"
void CarBoostBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch)
{
GameMessages::SendVehicleAddPassiveBoostAction(branch.target, UNASSIGNED_SYSTEM_ADDRESS);
auto* entity = EntityManager::Instance()->GetEntity(context->originator);
if (entity == nullptr)
{
return;
}
Game::logger->Log("Car boost", "Activating car boost!\n");
auto* possessableComponent = entity->GetComponent<PossessableComponent>();
if (possessableComponent != nullptr) {
auto* possessor = EntityManager::Instance()->GetEntity(possessableComponent->GetPossessor());
if (possessor != nullptr) {
auto* characterComponent = possessor->GetComponent<CharacterComponent>();
if (characterComponent != nullptr) {
Game::logger->Log("Car boost", "Tracking car boost!\n");
characterComponent->UpdatePlayerStatistic(RacingCarBoostsActivated);
}
}
}
m_Action->Handle(context, bitStream, branch);
entity->AddCallbackTimer(m_Time, [entity] () {
GameMessages::SendVehicleRemovePassiveBoostAction(entity->GetObjectID(), UNASSIGNED_SYSTEM_ADDRESS);
});
}
void CarBoostBehavior::Load()
{
m_Action = GetAction("action");
m_Time = GetFloat("time");
}

View File

@@ -0,0 +1,24 @@
#pragma once
#include "Behavior.h"
#include <vector>
class CarBoostBehavior final : public Behavior
{
public:
Behavior* m_Action;
float m_Time;
/*
* Inherited
*/
explicit CarBoostBehavior(const uint32_t behaviorId) : Behavior(behaviorId)
{
}
void Handle(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) override;
void Load() override;
};

View File

@@ -0,0 +1,40 @@
#include "ChainBehavior.h"
#include "BehaviorBranchContext.h"
#include "Game.h"
#include "dLogger.h"
void ChainBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bitStream, const BehaviorBranchContext branch)
{
uint32_t chain_index;
bitStream->Read(chain_index);
chain_index--;
if (chain_index < this->m_behaviors.size())
{
this->m_behaviors.at(chain_index)->Handle(context, bitStream, branch);
}
}
void ChainBehavior::Calculate(BehaviorContext* context, RakNet::BitStream* bitStream, const BehaviorBranchContext branch)
{
bitStream->Write(1);
this->m_behaviors.at(0)->Calculate(context, bitStream, branch);
}
void ChainBehavior::Load()
{
const auto parameters = GetParameterNames();
for (const auto& parameter : parameters)
{
if (parameter.first.rfind("behavior", 0) == 0)
{
auto* action = GetAction(parameter.second);
this->m_behaviors.push_back(action);
}
}
}

View File

@@ -0,0 +1,25 @@
#pragma once
#include "Behavior.h"
#include <vector>
class ChainBehavior final : public Behavior
{
public:
std::vector<Behavior*> m_behaviors;
/*
* Inherited
*/
explicit ChainBehavior(const uint32_t behaviorId) : Behavior(behaviorId)
{
}
void Handle(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) override;
void Calculate(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) override;
void Load() override;
};

View File

@@ -0,0 +1,50 @@
#include "ChangeOrientationBehavior.h"
#include "BehaviorBranchContext.h"
#include "BehaviorContext.h"
#include "EntityManager.h"
#include "BaseCombatAIComponent.h"
void ChangeOrientationBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch)
{
}
void ChangeOrientationBehavior::Calculate(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch)
{
if (!m_ToTarget) return; // TODO: Add the other arguments to this behavior
auto* self = EntityManager::Instance()->GetEntity(context->originator);
auto* other = EntityManager::Instance()->GetEntity(branch.target);
if (self == nullptr || other == nullptr) return;
const auto source = self->GetPosition();
const auto destination = self->GetPosition();
if (m_OrientCaster)
{
auto* baseCombatAIComponent = self->GetComponent<BaseCombatAIComponent>();
/*if (baseCombatAIComponent != nullptr)
{
baseCombatAIComponent->LookAt(destination);
}
else*/
{
self->SetRotation(NiQuaternion::LookAt(source, destination));
}
EntityManager::Instance()->SerializeEntity(self);
}
else
{
other->SetRotation(NiQuaternion::LookAt(destination, source));
EntityManager::Instance()->SerializeEntity(other);
}
}
void ChangeOrientationBehavior::Load()
{
m_OrientCaster = GetBoolean("orient_caster");
m_ToTarget = GetBoolean("to_target");
}

View File

@@ -0,0 +1,26 @@
#pragma once
#include "Behavior.h"
#include <vector>
class ChangeOrientationBehavior final : public Behavior
{
public:
bool m_OrientCaster;
bool m_ToTarget;
/*
* Inherited
*/
explicit ChangeOrientationBehavior(const uint32_t behaviorId) : Behavior(behaviorId)
{
}
void Handle(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) override;
void Calculate(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) override;
void Load() override;
};

View File

@@ -0,0 +1,27 @@
#include "ChargeUpBehavior.h"
#include "BehaviorBranchContext.h"
#include "BehaviorContext.h"
#include "dLogger.h"
void ChargeUpBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bitStream, const BehaviorBranchContext branch)
{
uint32_t handle;
bitStream->Read(handle);
context->RegisterSyncBehavior(handle, this, branch);
}
void ChargeUpBehavior::Calculate(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch)
{
}
void ChargeUpBehavior::Sync(BehaviorContext* context, RakNet::BitStream* bitStream, const BehaviorBranchContext branch)
{
this->m_action->Handle(context, bitStream, branch);
}
void ChargeUpBehavior::Load()
{
this->m_action = GetAction("action");
}

View File

@@ -0,0 +1,24 @@
#pragma once
#include "Behavior.h"
class ChargeUpBehavior final : public Behavior
{
public:
Behavior* m_action;
/*
* Inherited
*/
explicit ChargeUpBehavior(const uint32_t behaviorId) : Behavior(behaviorId)
{
}
void Handle(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) override;
void Calculate(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) override;
void Sync(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) override;
void Load() override;
};

View File

@@ -0,0 +1,25 @@
#include "ClearTargetBehavior.h"
#include "BehaviorBranchContext.h"
#include "BehaviorContext.h"
void ClearTargetBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch)
{
branch.target = LWOOBJID_EMPTY;
this->m_action->Handle(context, bitStream, branch);
}
void ClearTargetBehavior::Calculate(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch)
{
branch.target = LWOOBJID_EMPTY;
this->m_action->Calculate(context, bitStream, branch);
}
void ClearTargetBehavior::Load()
{
this->m_action = GetAction("action");
this->m_clearIfCaster = GetBoolean("clear_if_caster");
}

View File

@@ -0,0 +1,23 @@
#pragma once
#include "Behavior.h"
class ClearTargetBehavior final : public Behavior
{
public:
Behavior* m_action;
bool m_clearIfCaster;
/*
* Inherited
*/
explicit ClearTargetBehavior(const uint32_t behaviorId) : Behavior(behaviorId)
{
}
void Handle(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) override;
void Calculate(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) override;
void Load() override;
};

View File

@@ -0,0 +1,68 @@
#include "DamageAbsorptionBehavior.h"
#include "BehaviorBranchContext.h"
#include "BehaviorContext.h"
#include "EntityManager.h"
#include "Game.h"
#include "dLogger.h"
#include "DestroyableComponent.h"
void DamageAbsorptionBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bitStream, const BehaviorBranchContext branch)
{
auto* target = EntityManager::Instance()->GetEntity(branch.target);
if (target == nullptr)
{
Game::logger->Log("DamageAbsorptionBehavior", "Failed to find target (%llu)!\n", branch.target);
return;
}
auto* destroyable = target->GetComponent<DestroyableComponent>();
if (destroyable == nullptr)
{
return;
}
destroyable->SetDamageToAbsorb(static_cast<uint32_t>(destroyable->GetDamageToAbsorb()) + this->m_absorbAmount);
destroyable->SetIsShielded(true);
context->RegisterTimerBehavior(this, branch, target->GetObjectID());
}
void DamageAbsorptionBehavior::Calculate(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch)
{
Handle(context, bitStream, branch);
}
void DamageAbsorptionBehavior::Timer(BehaviorContext* context, BehaviorBranchContext branch, const LWOOBJID second)
{
auto* target = EntityManager::Instance()->GetEntity(second);
if (target == nullptr)
{
Game::logger->Log("DamageAbsorptionBehavior", "Failed to find target (%llu)!\n", second);
return;
}
auto* destroyable = target->GetComponent<DestroyableComponent>();
if (destroyable == nullptr)
{
return;
}
const auto present = static_cast<uint32_t>(destroyable->GetDamageToAbsorb());
const auto toRemove = std::min(present, this->m_absorbAmount);
destroyable->SetDamageToAbsorb(present - toRemove);
}
void DamageAbsorptionBehavior::Load()
{
this->m_absorbAmount = GetInt("absorb_amount");
}

View File

@@ -0,0 +1,24 @@
#pragma once
#include "Behavior.h"
class DamageAbsorptionBehavior final : public Behavior
{
public:
uint32_t m_absorbAmount;
/*
* Inherited
*/
explicit DamageAbsorptionBehavior(const uint32_t behaviorId) : Behavior(behaviorId)
{
}
void Handle(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) override;
void Calculate(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) override;
void Timer(BehaviorContext* context, BehaviorBranchContext branch, LWOOBJID second) override;
void Load() override;
};

View File

@@ -0,0 +1,62 @@
#include "DamageReductionBehavior.h"
#include "BehaviorBranchContext.h"
#include "BehaviorContext.h"
#include "EntityManager.h"
#include "Game.h"
#include "dLogger.h"
#include "DestroyableComponent.h"
void DamageReductionBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bitStream, const BehaviorBranchContext branch)
{
auto* target = EntityManager::Instance()->GetEntity(branch.target);
if (target == nullptr)
{
Game::logger->Log("DamageReductionBehavior", "Failed to find target (%llu)!\n", branch.target);
return;
}
auto* destroyable = target->GetComponent<DestroyableComponent>();
if (destroyable == nullptr)
{
return;
}
destroyable->SetDamageReduction(m_ReductionAmount);
context->RegisterTimerBehavior(this, branch, target->GetObjectID());
}
void DamageReductionBehavior::Calculate(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch)
{
Handle(context, bitStream, branch);
}
void DamageReductionBehavior::Timer(BehaviorContext* context, BehaviorBranchContext branch, const LWOOBJID second)
{
auto* target = EntityManager::Instance()->GetEntity(second);
if (target == nullptr)
{
Game::logger->Log("DamageReductionBehavior", "Failed to find target (%llu)!\n", second);
return;
}
auto* destroyable = target->GetComponent<DestroyableComponent>();
if (destroyable == nullptr)
{
return;
}
destroyable->SetDamageReduction(0);
}
void DamageReductionBehavior::Load()
{
this->m_ReductionAmount = GetInt("reduction_amount");
}

View File

@@ -0,0 +1,24 @@
#pragma once
#include "Behavior.h"
class DamageReductionBehavior final : public Behavior
{
public:
uint32_t m_ReductionAmount;
/*
* Inherited
*/
explicit DamageReductionBehavior(const uint32_t behaviorId) : Behavior(behaviorId)
{
}
void Handle(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) override;
void Calculate(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) override;
void Timer(BehaviorContext* context, BehaviorBranchContext branch, LWOOBJID second) override;
void Load() override;
};

View File

@@ -0,0 +1,24 @@
#include "DurationBehavior.h"
#include "BehaviorBranchContext.h"
#include "BehaviorContext.h"
void DurationBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch)
{
branch.duration = this->m_duration;
this->m_action->Handle(context, bitStream, branch);
}
void DurationBehavior::Calculate(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch)
{
branch.duration = this->m_duration;
this->m_action->Calculate(context, bitStream, branch);
}
void DurationBehavior::Load()
{
this->m_duration = GetFloat("duration");
this->m_action = GetAction("action");
}

View File

@@ -0,0 +1,22 @@
#pragma once
#include "Behavior.h"
class DurationBehavior final : public Behavior
{
public:
float m_duration;
Behavior* m_action;
explicit DurationBehavior(const uint32_t behavior_id) : Behavior(behavior_id)
{
}
void Handle(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) override;
void Calculate(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) override;
void Load() override;
private:
float m_Timer = 0.0f;
};

View File

@@ -0,0 +1,2 @@
#include "EmptyBehavior.h"

View File

@@ -0,0 +1,11 @@
#pragma once
#include "Behavior.h"
class EmptyBehavior final : public Behavior
{
public:
explicit EmptyBehavior(const uint32_t behavior_id) : Behavior(behavior_id)
{
}
};

View File

@@ -0,0 +1,19 @@
#include "EndBehavior.h"
#include "BehaviorContext.h"
#include "BehaviorBranchContext.h"
void EndBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch)
{
context->InvokeEnd(this->m_startBehavior);
}
void EndBehavior::Calculate(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch)
{
context->InvokeEnd(this->m_startBehavior);
}
void EndBehavior::Load()
{
this->m_startBehavior = GetInt("start_action");
}

View File

@@ -0,0 +1,22 @@
#pragma once
#include "Behavior.h"
class EndBehavior final : public Behavior
{
public:
uint32_t m_startBehavior;
/*
* Inherited
*/
explicit EndBehavior(const uint32_t behaviorId) : Behavior(behaviorId)
{
}
void Handle(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) override;
void Calculate(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) override;
void Load() override;
};

View File

@@ -0,0 +1,44 @@
#include "ForceMovementBehavior.h"
#include "BehaviorBranchContext.h"
#include "BehaviorContext.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;
}
uint32_t handle;
bitStream->Read(handle);
context->RegisterSyncBehavior(handle, this, branch);
}
void ForceMovementBehavior::Sync(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch)
{
uint32_t next;
bitStream->Read(next);
LWOOBJID target;
bitStream->Read(target);
auto* behavior = CreateBehavior(next);
branch.target = target;
behavior->Handle(context, bitStream, branch);
}
void ForceMovementBehavior::Load()
{
this->m_hitAction = GetAction("hit_action");
this->m_hitEnemyAction = GetAction("hit_action_enemy");
this->m_hitFactionAction = GetAction("hit_action_faction");
}

View File

@@ -0,0 +1,27 @@
#pragma once
#include "Behavior.h"
class ForceMovementBehavior final : public Behavior
{
public:
Behavior* m_hitAction;
Behavior* m_hitEnemyAction;
Behavior* m_hitFactionAction;
/*
* Inherited
*/
explicit ForceMovementBehavior(const uint32_t behaviorId) : Behavior(behaviorId)
{
}
void Handle(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) override;
void Sync(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) override;
void Load() override;
};

View File

@@ -0,0 +1,42 @@
#include "HealBehavior.h"
#include "BehaviorBranchContext.h"
#include "Game.h"
#include "dLogger.h"
#include "EntityManager.h"
#include "DestroyableComponent.h"
void HealBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bit_stream, const BehaviorBranchContext branch)
{
auto* entity = EntityManager::Instance()->GetEntity(branch.target);
if (entity == nullptr)
{
Game::logger->Log("HealBehavior", "Failed to find entity for (%llu)!\n", branch.target);
return;
}
auto* destroyable = static_cast<DestroyableComponent*>(entity->GetComponent(COMPONENT_TYPE_DESTROYABLE));
if (destroyable == nullptr)
{
Game::logger->Log("HealBehavior", "Failed to find destroyable component for %(llu)!\n", branch.target);
return;
}
destroyable->Heal(this->m_health);
}
void HealBehavior::Calculate(BehaviorContext* context, RakNet::BitStream* bit_stream, const BehaviorBranchContext branch)
{
Handle(context, bit_stream, branch);
}
void HealBehavior::Load()
{
this->m_health = GetInt("health");
}

View File

@@ -0,0 +1,22 @@
#pragma once
#include "Behavior.h"
class HealBehavior final : public Behavior
{
public:
uint32_t m_health;
/*
* Inherited
*/
explicit HealBehavior(const uint32_t behavior_id) : Behavior(behavior_id)
{
}
void Handle(BehaviorContext* context, RakNet::BitStream* bit_stream, BehaviorBranchContext branch) override;
void Calculate(BehaviorContext* context, RakNet::BitStream* bit_stream, BehaviorBranchContext branch) override;
void Load() override;
};

View File

@@ -0,0 +1,37 @@
#include "ImaginationBehavior.h"
#include "BehaviorBranchContext.h"
#include "DestroyableComponent.h"
#include "dpWorld.h"
#include "EntityManager.h"
#include "dLogger.h"
void ImaginationBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bit_stream, const BehaviorBranchContext branch)
{
auto* entity = EntityManager::Instance()->GetEntity(branch.target);
if (entity == nullptr)
{
return;
}
auto* destroyable = entity->GetComponent<DestroyableComponent>();
if (destroyable == nullptr)
{
return;
}
destroyable->Imagine(this->m_imagination);
}
void ImaginationBehavior::Calculate(BehaviorContext* context, RakNet::BitStream* bit_stream, const BehaviorBranchContext branch)
{
Handle(context, bit_stream, branch);
}
void ImaginationBehavior::Load()
{
this->m_imagination = GetInt("imagination");
}

View File

@@ -0,0 +1,22 @@
#pragma once
#include "Behavior.h"
class ImaginationBehavior final : public Behavior
{
public:
int32_t m_imagination;
/*
* Inherited
*/
explicit ImaginationBehavior(const uint32_t behavior_id) : Behavior(behavior_id)
{
}
void Handle(BehaviorContext* context, RakNet::BitStream* bit_stream, BehaviorBranchContext branch) override;
void Calculate(BehaviorContext* context, RakNet::BitStream* bit_stream, BehaviorBranchContext branch) override;
void Load() override;
};

View File

@@ -0,0 +1,67 @@
#include "ImmunityBehavior.h"
#include "BehaviorBranchContext.h"
#include "BehaviorContext.h"
#include "EntityManager.h"
#include "Game.h"
#include "dLogger.h"
#include "DestroyableComponent.h"
void ImmunityBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bitStream, const BehaviorBranchContext branch)
{
auto* target = EntityManager::Instance()->GetEntity(branch.target);
if (target == nullptr)
{
Game::logger->Log("DamageAbsorptionBehavior", "Failed to find target (%llu)!\n", branch.target);
return;
}
auto* destroyable = static_cast<DestroyableComponent*>(target->GetComponent(COMPONENT_TYPE_DESTROYABLE));
if (destroyable == nullptr)
{
return;
}
if (!this->m_immuneBasicAttack)
{
return;
}
destroyable->PushImmunity();
context->RegisterTimerBehavior(this, branch, target->GetObjectID());
}
void ImmunityBehavior::Calculate(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch)
{
Handle(context, bitStream, branch);
}
void ImmunityBehavior::Timer(BehaviorContext* context, BehaviorBranchContext branch, const LWOOBJID second)
{
auto* target = EntityManager::Instance()->GetEntity(second);
if (target == nullptr)
{
Game::logger->Log("DamageAbsorptionBehavior", "Failed to find target (%llu)!\n", second);
return;
}
auto* destroyable = static_cast<DestroyableComponent*>(target->GetComponent(COMPONENT_TYPE_DESTROYABLE));
if (destroyable == nullptr)
{
return;
}
destroyable->PopImmunity();
}
void ImmunityBehavior::Load()
{
this->m_immuneBasicAttack = GetBoolean("immune_basic_attack");
}

View File

@@ -0,0 +1,24 @@
#pragma once
#include "Behavior.h"
class ImmunityBehavior final : public Behavior
{
public:
uint32_t m_immuneBasicAttack;
/*
* Inherited
*/
explicit ImmunityBehavior(const uint32_t behaviorId) : Behavior(behaviorId)
{
}
void Handle(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) override;
void Calculate(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) override;
void Timer(BehaviorContext* context, BehaviorBranchContext branch, LWOOBJID second) override;
void Load() override;
};

View File

@@ -0,0 +1,84 @@
#include "InterruptBehavior.h"
#include "BehaviorBranchContext.h"
#include "BehaviorContext.h"
#include "Game.h"
#include "dLogger.h"
#include "EntityManager.h"
#include "SkillComponent.h"
void InterruptBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch)
{
if (branch.target != context->originator)
{
bool unknown = false;
bitStream->Read(unknown);
if (unknown) return;
}
if (!this->m_interruptBlock)
{
bool unknown = false;
bitStream->Read(unknown);
if (unknown) return;
}
if (this->m_target) // Guess...
{
bool unknown = false;
bitStream->Read(unknown);
}
if (branch.target == context->originator) return;
auto* target = EntityManager::Instance()->GetEntity(branch.target);
if (target == nullptr) return;
auto* skillComponent = target->GetComponent<SkillComponent>();
if (skillComponent == nullptr) return;
skillComponent->Interrupt();
}
void InterruptBehavior::Calculate(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch)
{
if (branch.target != context->originator)
{
bitStream->Write(false);
}
if (!this->m_interruptBlock)
{
bitStream->Write(false);
}
bitStream->Write(false);
if (branch.target == context->originator) return;
auto* target = EntityManager::Instance()->GetEntity(branch.target);
if (target == nullptr) return;
auto* skillComponent = target->GetComponent<SkillComponent>();
if (skillComponent == nullptr) return;
skillComponent->Interrupt();
}
void InterruptBehavior::Load()
{
this->m_target = GetBoolean("target");
this->m_interruptBlock = GetBoolean("interrupt_block");
}

View File

@@ -0,0 +1,24 @@
#pragma once
#include "Behavior.h"
class InterruptBehavior final : public Behavior
{
public:
bool m_target;
bool m_interruptBlock;
/*
* Inherited
*/
explicit InterruptBehavior(const uint32_t behavior_id) : Behavior(behavior_id)
{
}
void Handle(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) override;
void Calculate(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) override;
void Load() override;
};

View File

@@ -0,0 +1,42 @@
#define _USE_MATH_DEFINES
#include <cmath>
#include "KnockbackBehavior.h"
#include "BehaviorBranchContext.h"
#include "BehaviorContext.h"
#include "EntityManager.h"
#include "GameMessages.h"
#include "DestroyableComponent.h"
void KnockbackBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch)
{
bool unknown;
bitStream->Read(unknown);
}
void KnockbackBehavior::Calculate(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch)
{
bool blocked = false;
auto* target = EntityManager::Instance()->GetEntity(branch.target);
if (target != nullptr)
{
auto* destroyableComponent = target->GetComponent<DestroyableComponent>();
if (destroyableComponent != nullptr)
{
blocked = destroyableComponent->IsKnockbackImmune();
}
}
bitStream->Write(blocked);
}
void KnockbackBehavior::Load()
{
this->m_strength = GetInt("strength");
this->m_angle = GetInt("angle");
this->m_relative = GetBoolean("relative");
this->m_time = GetInt("time_ms");
}

View File

@@ -0,0 +1,26 @@
#pragma once
#include "Behavior.h"
class KnockbackBehavior final : public Behavior
{
public:
/*
* Inherited
*/
uint32_t m_strength;
uint32_t m_angle;
bool m_relative;
uint32_t m_time;
explicit KnockbackBehavior(const uint32_t behaviorID) : Behavior(behaviorID)
{
}
void Handle(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) override;
void Calculate(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) override;
void Load() override;
};

View File

@@ -0,0 +1,62 @@
#include "MovementSwitchBehavior.h"
#include "BehaviorBranchContext.h"
#include "Game.h"
#include "dLogger.h"
void MovementSwitchBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bitStream, const BehaviorBranchContext branch)
{
if (this->m_groundAction->m_templateId == BehaviorTemplates::BEHAVIOR_EMPTY &&
this->m_jumpAction->m_templateId == BehaviorTemplates::BEHAVIOR_EMPTY &&
this->m_fallingAction->m_templateId == BehaviorTemplates::BEHAVIOR_EMPTY &&
this->m_doubleJumpAction->m_templateId == BehaviorTemplates::BEHAVIOR_EMPTY &&
this->m_airAction->m_templateId == BehaviorTemplates::BEHAVIOR_EMPTY &&
this->m_jetpackAction->m_templateId == BehaviorTemplates::BEHAVIOR_EMPTY)
{
return;
}
uint32_t movementType;
bitStream->Read(movementType);
switch (movementType)
{
case 1:
this->m_groundAction->Handle(context, bitStream, branch);
break;
case 2:
this->m_jumpAction->Handle(context, bitStream, branch);
break;
case 3:
this->m_fallingAction->Handle(context, bitStream, branch);
break;
case 4:
this->m_doubleJumpAction->Handle(context, bitStream, branch);
break;
case 5:
this->m_airAction->Handle(context, bitStream, branch);
break;
case 6:
this->m_jetpackAction->Handle(context, bitStream, branch);
break;
default:
Game::logger->Log("MovementSwitchBehavior", "Invalid movement behavior type (%i)!\n", movementType);
break;
}
}
void MovementSwitchBehavior::Load()
{
this->m_airAction = GetAction("air_action");
this->m_doubleJumpAction = GetAction("double_jump_action");
this->m_fallingAction = GetAction("falling_action");
this->m_groundAction = GetAction("ground_action");
this->m_jetpackAction = GetAction("jetpack_action");
this->m_jumpAction = GetAction("jump_action");
}

View File

@@ -0,0 +1,32 @@
#pragma once
#include "Behavior.h"
class MovementSwitchBehavior final : public Behavior
{
public:
/*
* Members
*/
Behavior* m_airAction;
Behavior* m_doubleJumpAction;
Behavior* m_fallingAction;
Behavior* m_groundAction;
Behavior* m_jetpackAction;
Behavior* m_jumpAction;
/*
* Inherited
*/
explicit MovementSwitchBehavior(const uint32_t behavior_id) : Behavior(behavior_id)
{
}
void Handle(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) override;
void Load() override;
};

View File

@@ -0,0 +1,31 @@
#include "NpcCombatSkillBehavior.h"
#include "BehaviorBranchContext.h"
#include "BehaviorContext.h"
void NpcCombatSkillBehavior::Calculate(BehaviorContext* context, RakNet::BitStream* bit_stream, BehaviorBranchContext branch)
{
context->skillTime = this->m_npcSkillTime;
for (auto* behavior : this->m_behaviors)
{
behavior->Calculate(context, bit_stream, branch);
}
}
void NpcCombatSkillBehavior::Load()
{
this->m_npcSkillTime = GetFloat("npc skill time");
const auto parameters = GetParameterNames();
for (const auto& parameter : parameters)
{
if (parameter.first.rfind("behavior", 0) == 0)
{
auto* action = GetAction(parameter.second);
this->m_behaviors.push_back(action);
}
}
}

View File

@@ -0,0 +1,22 @@
#pragma once
#include "Behavior.h"
class NpcCombatSkillBehavior final : public Behavior
{
public:
std::vector<Behavior*> m_behaviors;
float m_npcSkillTime;
/*
* Inherited
*/
explicit NpcCombatSkillBehavior(const uint32_t behavior_id) : Behavior(behavior_id)
{
}
void Calculate(BehaviorContext* context, RakNet::BitStream* bit_stream, BehaviorBranchContext branch) override;
void Load() override;
};

View File

@@ -0,0 +1,80 @@
#include "OverTimeBehavior.h"
#include "BehaviorBranchContext.h"
#include "BehaviorContext.h"
#include "Game.h"
#include "dLogger.h"
#include "EntityManager.h"
#include "SkillComponent.h"
#include "DestroyableComponent.h"
/**
* The OverTime behavior is very inconsistent in how it appears in the skill tree vs. how it should behave.
*
* Items like "Doc in a Box" use an overtime behavior which you would expect have health & armor regen, but is only fallowed by a stun.
*
* Due to this inconsistency, we have to implement a special case for some items.
*/
void OverTimeBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch)
{
const auto originator = context->originator;
auto* entity = EntityManager::Instance()->GetEntity(originator);
if (entity == nullptr)
{
return;
}
for (size_t i = 0; i < m_NumIntervals; i++)
{
entity->AddCallbackTimer((i + 1) * m_Delay, [originator, branch, this]() {
auto* entity = EntityManager::Instance()->GetEntity(originator);
if (entity == nullptr)
{
return;
}
auto* skillComponent = entity->GetComponent<SkillComponent>();
if (skillComponent == nullptr)
{
return;
}
skillComponent->CalculateBehavior(0, m_Action->m_behaviorId, branch.target, true, true);
auto* destroyableComponent = entity->GetComponent<DestroyableComponent>();
if (destroyableComponent == nullptr)
{
return;
}
/**
* Special cases for inconsistent behavior.
*/
switch (m_behaviorId)
{
case 26253: // "Doc in a Box", heal up to 6 health and regen up to 18 armor.
destroyableComponent->Heal(1);
destroyableComponent->Repair(3);
break;
}
});
}
}
void OverTimeBehavior::Calculate(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch)
{
}
void OverTimeBehavior::Load()
{
m_Action = GetAction("action");
m_Delay = GetFloat("delay");
m_NumIntervals = GetInt("num_intervals");
}

View File

@@ -0,0 +1,24 @@
#pragma once
#include "Behavior.h"
class OverTimeBehavior final : public Behavior
{
public:
Behavior* m_Action;
float m_Delay;
int32_t m_NumIntervals;
/*
* Inherited
*/
explicit OverTimeBehavior(const uint32_t behaviorId) : Behavior(behaviorId)
{
}
void Handle(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) override;
void Calculate(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) override;
void Load() override;
};

View File

@@ -0,0 +1,26 @@
#include "PlayEffectBehavior.h"
#include "BehaviorContext.h"
#include "BehaviorBranchContext.h"
void PlayEffectBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch)
{
// On managed behaviors this is handled by the client
if (!context->unmanaged)
return;
const auto& target = branch.target == LWOOBJID_EMPTY ? context->originator : branch.target;
PlayFx(u"", target);
}
void PlayEffectBehavior::Calculate(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch)
{
const auto& target = branch.target == LWOOBJID_EMPTY ? context->originator : branch.target;
//PlayFx(u"", target);
}
void PlayEffectBehavior::Load()
{
}

View File

@@ -0,0 +1,19 @@
#pragma once
#include "Behavior.h"
class PlayEffectBehavior final : public Behavior
{
public:
/*
* Inherited
*/
explicit PlayEffectBehavior(const uint32_t behaviorId) : Behavior(behaviorId)
{
}
void Handle(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) override;
void Calculate(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) override;
void Load() override;
};

View File

@@ -0,0 +1,166 @@
#include "ProjectileAttackBehavior.h"
#include "BehaviorBranchContext.h"
#include "BehaviorContext.h"
#include "EntityManager.h"
#include "Game.h"
#include "dLogger.h"
#include "SkillComponent.h"
#include "../dWorldServer/ObjectIDManager.h"
void ProjectileAttackBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch)
{
LWOOBJID target;
bitStream->Read(target);
auto* entity = EntityManager::Instance()->GetEntity(context->originator);
if (entity == nullptr)
{
Game::logger->Log("ProjectileAttackBehavior", "Failed to find originator (%llu)!\n", context->originator);
return;
}
auto* skillComponent = entity->GetComponent<SkillComponent>();
if (skillComponent == nullptr)
{
Game::logger->Log("ProjectileAttackBehavior", "Failed to find skill component for (%llu)!\n", -context->originator);
return;
}
if (m_useMouseposit)
{
NiPoint3 targetPosition = NiPoint3::ZERO;
bitStream->Read(targetPosition);
}
auto* targetEntity = EntityManager::Instance()->GetEntity(target);
for (auto i = 0u; i < this->m_projectileCount; ++i)
{
LWOOBJID projectileId;
bitStream->Read(projectileId);
branch.target = target;
branch.isProjectile = true;
branch.referencePosition = targetEntity == nullptr ? entity->GetPosition() : targetEntity->GetPosition();
skillComponent->RegisterPlayerProjectile(projectileId, context, branch, this->m_lot);
}
}
void ProjectileAttackBehavior::Calculate(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch)
{
bitStream->Write(branch.target);
auto* entity = EntityManager::Instance()->GetEntity(context->originator);
if (entity == nullptr)
{
Game::logger->Log("ProjectileAttackBehavior", "Failed to find originator (%llu)!\n", context->originator);
return;
}
auto* skillComponent = entity->GetComponent<SkillComponent>();
if (skillComponent == nullptr)
{
Game::logger->Log("ProjectileAttackBehavior", "Failed to find skill component for (%llu)!\n", context->originator);
return;
}
auto* other = EntityManager::Instance()->GetEntity(branch.target);
if (other == nullptr)
{
Game::logger->Log("ProjectileAttackBehavior", "Invalid projectile target (%llu)!\n", branch.target);
return;
}
const auto position = entity->GetPosition() + this->m_offset;
const auto distance = Vector3::Distance(position, other->GetPosition());
const auto time = distance / this->m_projectileSpeed;
const auto rotation = NiQuaternion::LookAtUnlocked(position, other->GetPosition());
const auto targetPosition = other->GetPosition();
//entity->SetRotation(rotation);
const auto angleDelta = this->m_spreadAngle;
const auto angleStep = angleDelta / this->m_projectileCount;
auto angle = -angleStep;
const auto maxTime = this->m_maxDistance / this->m_projectileSpeed;
for (auto i = 0u; i < this->m_projectileCount; ++i)
{
auto id = static_cast<LWOOBJID>(ObjectIDManager::Instance()->GenerateObjectID());
id = GeneralUtils::SetBit(id, OBJECT_BIT_CLIENT);
bitStream->Write(id);
auto eulerAngles = rotation.GetEulerAngles();
eulerAngles.y += angle * (3.14 / 180);
const auto angledRotation = NiQuaternion::FromEulerAngles(eulerAngles);
const auto direction = angledRotation.GetForwardVector();
const auto destination = position + direction * distance;
branch.isProjectile = true;
branch.referencePosition = destination;
skillComponent->RegisterCalculatedProjectile(id, context, branch, this->m_lot, maxTime, position, direction * this->m_projectileSpeed, this->m_trackTarget, this->m_trackRadius);
// No idea how to calculate this properly
if (this->m_projectileCount == 2)
{
angle += angleDelta;
}
else if (this->m_projectileCount == 3)
{
angle += angleStep;
}
}
}
void ProjectileAttackBehavior::Load()
{
this->m_lot = GetInt("LOT_ID");
this->m_projectileCount = GetInt("spread_count");
if (this->m_projectileCount == 0)
{
this->m_projectileCount = 1;
}
this->m_maxDistance = GetFloat("max_distance");
this->m_projectileSpeed = GetFloat("projectile_speed");
this->m_spreadAngle = GetFloat("spread_angle");
this->m_offset = { GetFloat("offset_x"), GetFloat("offset_y"), GetFloat("offset_z") };
this->m_trackTarget = GetBoolean("track_target");
this->m_trackRadius = GetFloat("track_radius");
this->m_useMouseposit = GetBoolean("use_mouseposit");
}

View File

@@ -0,0 +1,39 @@
#pragma once
#include "Behavior.h"
#include "NiPoint3.h"
class ProjectileAttackBehavior final : public Behavior
{
public:
LOT m_lot;
uint32_t m_projectileCount;
float m_projectileSpeed;
float m_maxDistance;
float m_spreadAngle;
NiPoint3 m_offset;
bool m_trackTarget;
float m_trackRadius;
bool m_useMouseposit;
/*
* Inherited
*/
explicit ProjectileAttackBehavior(const uint32_t behavior_id) : Behavior(behavior_id)
{
}
void Handle(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) override;
void Calculate(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) override;
void Load() override;
};

View File

@@ -0,0 +1,38 @@
#include "PullToPointBehavior.h"
#include "BehaviorContext.h"
#include "BehaviorBranchContext.h"
#include "EntityManager.h"
#include "MovementAIComponent.h"
void PullToPointBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch)
{
auto* entity = EntityManager::Instance()->GetEntity(context->originator);
auto* target = EntityManager::Instance()->GetEntity(branch.target);
if (entity == nullptr || target == nullptr)
{
return;
}
auto* movement = target->GetComponent<MovementAIComponent>();
if (movement == nullptr)
{
return;
}
const auto position = branch.isProjectile ? branch.referencePosition : entity->GetPosition();
movement->PullToPoint(position);
}
void PullToPointBehavior::Calculate(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch)
{
Handle(context, bitStream, branch);
}
void PullToPointBehavior::Load()
{
}

View File

@@ -0,0 +1,21 @@
#pragma once
#include "Behavior.h"
class PullToPointBehavior final : public Behavior
{
public:
/*
* Inherited
*/
explicit PullToPointBehavior(const uint32_t behaviorId) : Behavior(behaviorId)
{
}
void Handle(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) override;
void Calculate(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) override;
void Load() override;
};

View File

@@ -0,0 +1,40 @@
#include "RepairBehavior.h"
#include "BehaviorBranchContext.h"
#include "DestroyableComponent.h"
#include "dpWorld.h"
#include "EntityManager.h"
#include "dLogger.h"
#include "Game.h"
void RepairBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bit_stream, const BehaviorBranchContext branch)
{
auto* entity = EntityManager::Instance()->GetEntity(branch.target);
if (entity == nullptr)
{
Game::logger->Log("RepairBehavior", "Failed to find entity for (%llu)!\n", branch.target);
return;
}
auto* destroyable = static_cast<DestroyableComponent*>(entity->GetComponent(COMPONENT_TYPE_DESTROYABLE));
if (destroyable == nullptr)
{
Game::logger->Log("RepairBehavior", "Failed to find destroyable component for %(llu)!\n", branch.target);
return;
}
destroyable->Repair(this->m_armor);
}
void RepairBehavior::Calculate(BehaviorContext* context, RakNet::BitStream* bit_stream, const BehaviorBranchContext branch)
{
Handle(context, bit_stream, branch);
}
void RepairBehavior::Load()
{
this->m_armor = GetInt("armor");
}

View File

@@ -0,0 +1,22 @@
#pragma once
#include "Behavior.h"
class RepairBehavior final : public Behavior
{
public:
uint32_t m_armor;
/*
* Inherited
*/
explicit RepairBehavior(const uint32_t behavior_id) : Behavior(behavior_id)
{
}
void Handle(BehaviorContext* context, RakNet::BitStream* bit_stream, BehaviorBranchContext branch) override;
void Calculate(BehaviorContext* context, RakNet::BitStream* bit_stream, BehaviorBranchContext branch) override;
void Load() override;
};

View File

@@ -0,0 +1,18 @@
#include "SkillCastFailedBehavior.h"
#include "BehaviorContext.h"
#include "BehaviorBranchContext.h"
void SkillCastFailedBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch)
{
context->failed = true;
}
void SkillCastFailedBehavior::Calculate(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch)
{
context->failed = true;
}
void SkillCastFailedBehavior::Load()
{
}

View File

@@ -0,0 +1,20 @@
#pragma once
#include "Behavior.h"
class SkillCastFailedBehavior final : public Behavior
{
public:
/*
* Inherited
*/
explicit SkillCastFailedBehavior(const uint32_t behaviorId) : Behavior(behaviorId)
{
}
void Handle(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) override;
void Calculate(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) override;
void Load() override;
};

View File

@@ -0,0 +1,28 @@
#include "SkillEventBehavior.h"
#include "BehaviorBranchContext.h"
#include "BehaviorContext.h"
#include "EntityManager.h"
#include "CppScripts.h"
void SkillEventBehavior::Handle(BehaviorContext *context, RakNet::BitStream *bitStream, BehaviorBranchContext branch) {
auto* target = EntityManager::Instance()->GetEntity(branch.target);
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);
}
}
}
void
SkillEventBehavior::Calculate(BehaviorContext *context, RakNet::BitStream *bitStream, BehaviorBranchContext branch) {
auto* target = EntityManager::Instance()->GetEntity(branch.target);
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);
}
}
}

View File

@@ -0,0 +1,15 @@
#pragma once
#include "Behavior.h"
/**
* Behavior that casts explicit events on its target
*/
class SkillEventBehavior final : public Behavior {
public:
explicit SkillEventBehavior(const uint32_t behaviorID) : Behavior(behaviorID) {
}
void Handle(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) override;
void Calculate(BehaviorContext *context, RakNet::BitStream *bitStream, BehaviorBranchContext branch) override;
};

View File

@@ -0,0 +1,120 @@
#include "SpawnBehavior.h"
#include "BehaviorContext.h"
#include "BehaviorBranchContext.h"
#include "EntityManager.h"
#include "Game.h"
#include "dLogger.h"
#include "DestroyableComponent.h"
#include "RebuildComponent.h"
void SpawnBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch)
{
auto* origin = EntityManager::Instance()->GetEntity(context->originator);
if (origin == nullptr)
{
Game::logger->Log("SpawnBehavior", "Failed to find self entity (%llu)!\n", context->originator);
return;
}
if (branch.isProjectile)
{
auto* target = EntityManager::Instance()->GetEntity(branch.target);
if (target != nullptr)
{
origin = target;
}
}
EntityInfo info;
info.lot = this->m_lot;
info.pos = origin->GetPosition();
info.rot = origin->GetRotation();
info.scale = 1;
info.spawner = nullptr;
info.spawnerID = context->originator;
info.spawnerNodeID = 0;
info.pos = info.pos + (info.rot.GetForwardVector() * m_Distance);
auto* entity = EntityManager::Instance()->CreateEntity(
info,
nullptr,
EntityManager::Instance()->GetEntity(context->originator)
);
if (entity == nullptr)
{
Game::logger->Log("SpawnBehavior", "Failed to spawn entity (%i)!\n", this->m_lot);
return;
}
entity->SetOwnerOverride(context->originator);
// Unset the flag to reposition the player, this makes it harder to glitch out of the map
auto* rebuildComponent = entity->GetComponent<RebuildComponent>();
if (rebuildComponent != nullptr)
{
rebuildComponent->SetRepositionPlayer(false);
}
EntityManager::Instance()->ConstructEntity(entity);
if (branch.duration > 0)
{
context->RegisterTimerBehavior(this, branch, entity->GetObjectID());
}
if (branch.start != 0)
{
context->RegisterEndBehavior(this, branch, entity->GetObjectID());
}
entity->AddCallbackTimer(60, [entity] () {
entity->Smash();
});
}
void SpawnBehavior::Calculate(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch)
{
Handle(context, bitStream, branch);
}
void SpawnBehavior::Timer(BehaviorContext* context, const BehaviorBranchContext branch, const LWOOBJID second)
{
auto* entity = EntityManager::Instance()->GetEntity(second);
if (entity == nullptr)
{
Game::logger->Log("SpawnBehavior", "Failed to find spawned entity (%llu)!\n", second);
return;
}
auto* destroyable = static_cast<DestroyableComponent*>(entity->GetComponent(COMPONENT_TYPE_DESTROYABLE));
if (destroyable == nullptr)
{
entity->Smash(context->originator);
return;
}
destroyable->Smash(second);
}
void SpawnBehavior::End(BehaviorContext* context, const BehaviorBranchContext branch, const LWOOBJID second)
{
Timer(context, branch, second);
}
void SpawnBehavior::Load()
{
this->m_lot = GetInt("LOT_ID");
this->m_Distance = GetFloat("distance");
}

View File

@@ -0,0 +1,26 @@
#pragma once
#include "Behavior.h"
class SpawnBehavior final : public Behavior
{
public:
LOT m_lot;
float m_Distance;
/*
* Inherited
*/
explicit SpawnBehavior(const uint32_t behaviorId) : Behavior(behaviorId)
{
}
void Handle(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) override;
void Calculate(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) override;
void Timer(BehaviorContext* context, BehaviorBranchContext branch, LWOOBJID second) override;
void End(BehaviorContext* context, BehaviorBranchContext branch, LWOOBJID second) override;
void Load() override;
};

View File

@@ -0,0 +1,13 @@
#include "SpawnQuickbuildBehavior.h"
#include "BehaviorBranchContext.h"
#include "BehaviorContext.h"
void SpawnQuickbuildBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch)
{
}
void SpawnQuickbuildBehavior::Load()
{
}

View File

@@ -0,0 +1,18 @@
#pragma once
#include "Behavior.h"
class SpawnQuickbuildBehavior final : public Behavior
{
public:
/*
* Inherited
*/
explicit SpawnQuickbuildBehavior(const uint32_t behaviorId) : Behavior(behaviorId)
{
}
void Handle(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) override;
void Load() override;
};

View File

@@ -0,0 +1,106 @@
#include "SpeedBehavior.h"
#include "ControllablePhysicsComponent.h"
#include "BehaviorContext.h"
#include "BehaviorBranchContext.h"
#include "dLogger.h"
void SpeedBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch)
{
if (m_AffectsCaster)
{
branch.target = context->caster;
}
auto* target = EntityManager::Instance()->GetEntity(branch.target);
if (target == nullptr)
{
return;
}
auto* controllablePhysicsComponent = target->GetComponent<ControllablePhysicsComponent>();
if (controllablePhysicsComponent == nullptr)
{
return;
}
const auto current = controllablePhysicsComponent->GetSpeedMultiplier();
controllablePhysicsComponent->SetSpeedMultiplier(current + ((m_RunSpeed - 500.0f) / 500.0f));
EntityManager::Instance()->SerializeEntity(target);
if (branch.duration > 0.0f)
{
context->RegisterTimerBehavior(this, branch);
}
else if (branch.start > 0)
{
controllablePhysicsComponent->SetIgnoreMultipliers(true);
context->RegisterEndBehavior(this, branch);
}
}
void SpeedBehavior::Timer(BehaviorContext* context, BehaviorBranchContext branch, LWOOBJID second)
{
auto* target = EntityManager::Instance()->GetEntity(branch.target);
if (target == nullptr)
{
return;
}
auto* controllablePhysicsComponent = target->GetComponent<ControllablePhysicsComponent>();
if (controllablePhysicsComponent == nullptr)
{
return;
}
const auto current = controllablePhysicsComponent->GetSpeedMultiplier();
controllablePhysicsComponent->SetSpeedMultiplier(current - ((m_RunSpeed - 500.0f) / 500.0f));
EntityManager::Instance()->SerializeEntity(target);
}
void SpeedBehavior::End(BehaviorContext* context, BehaviorBranchContext branch, LWOOBJID second)
{
auto* target = EntityManager::Instance()->GetEntity(branch.target);
if (target == nullptr)
{
return;
}
auto* controllablePhysicsComponent = target->GetComponent<ControllablePhysicsComponent>();
if (controllablePhysicsComponent == nullptr)
{
return;
}
const auto current = controllablePhysicsComponent->GetSpeedMultiplier();
controllablePhysicsComponent->SetIgnoreMultipliers(false);
controllablePhysicsComponent->SetSpeedMultiplier(current - ((m_RunSpeed - 500.0f) / 500.0f));
EntityManager::Instance()->SerializeEntity(target);
}
void SpeedBehavior::Load()
{
m_RunSpeed = GetFloat("run_speed");
if (m_RunSpeed < 500.0f)
{
m_RunSpeed = 500.0f;
}
m_AffectsCaster = GetBoolean("affects_caster");
}

View File

@@ -0,0 +1,27 @@
#pragma once
#include "Behavior.h"
class SpeedBehavior final : public Behavior
{
public:
/*
* Inherited
*/
explicit SpeedBehavior(const uint32_t behaviorId) : Behavior(behaviorId)
{
}
void Handle(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) override;
void Timer(BehaviorContext* context, BehaviorBranchContext branch, LWOOBJID second) override;
void End(BehaviorContext* context, BehaviorBranchContext branch, LWOOBJID second) override;
void Load() override;
private:
float m_RunSpeed;
bool m_AffectsCaster;
};

View File

@@ -0,0 +1,21 @@
#include "StartBehavior.h"
#include "BehaviorBranchContext.h"
void StartBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bit_stream, BehaviorBranchContext branch)
{
branch.start = this->m_behaviorId;
this->m_action->Handle(context, bit_stream, branch);
}
void StartBehavior::Calculate(BehaviorContext* context, RakNet::BitStream* bit_stream, BehaviorBranchContext branch)
{
branch.start = this->m_behaviorId;
this->m_action->Calculate(context, bit_stream, branch);
}
void StartBehavior::Load()
{
this->m_action = GetAction("action");
}

View File

@@ -0,0 +1,22 @@
#pragma once
#include "Behavior.h"
class StartBehavior final : public Behavior
{
public:
Behavior* m_action;
/*
* Inherited
*/
explicit StartBehavior(const uint32_t behaviorID) : Behavior(behaviorID)
{
}
void Handle(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) override;
void Calculate(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) override;
void Load() override;
};

View File

@@ -0,0 +1,113 @@
#include "StunBehavior.h"
#include "BaseCombatAIComponent.h"
#include "BehaviorBranchContext.h"
#include "BehaviorContext.h"
#include "EntityManager.h"
#include "Game.h"
#include "dLogger.h"
#include "DestroyableComponent.h"
void StunBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bitStream, const BehaviorBranchContext branch)
{
if (this->m_stunCaster || branch.target == context->originator) {
return;
}
bool blocked;
bitStream->Read(blocked);
auto* target = EntityManager::Instance()->GetEntity(branch.target);
if (target == nullptr)
{
Game::logger->Log("StunBehavior", "Failed to find target (%llu)!\n", branch.target);
return;
}
/*
* If our target is an enemy we can go ahead and stun it.
*/
auto* combatAiComponent = static_cast<BaseCombatAIComponent*>(target->GetComponent(COMPONENT_TYPE_BASE_COMBAT_AI));
if (combatAiComponent == nullptr)
{
return;
}
combatAiComponent->Stun(branch.duration);
}
void StunBehavior::Calculate(BehaviorContext* context, RakNet::BitStream* bitStream, const BehaviorBranchContext branch)
{
if (this->m_stunCaster || branch.target == context->originator)
{
auto* self = EntityManager::Instance()->GetEntity(context->originator);
if (self == nullptr)
{
Game::logger->Log("StunBehavior", "Invalid self entity (%llu)!\n", context->originator);
return;
}
/*
* See if we can stun ourselves
*/
auto* combatAiComponent = static_cast<BaseCombatAIComponent*>(self->GetComponent(COMPONENT_TYPE_BASE_COMBAT_AI));
if (combatAiComponent == nullptr)
{
return;
}
combatAiComponent->Stun(branch.duration);
return;
}
bool blocked = false;
auto* target = EntityManager::Instance()->GetEntity(branch.target);
if (target != nullptr)
{
auto* destroyableComponent = target->GetComponent<DestroyableComponent>();
if (destroyableComponent != nullptr)
{
blocked = destroyableComponent->IsKnockbackImmune();
}
}
bitStream->Write(blocked);
if (target == nullptr)
{
Game::logger->Log("StunBehavior", "Failed to find target (%llu)!\n", branch.target);
return;
}
/*
* If our target is an enemy we can go ahead and stun it.
*/
auto* combatAiComponent = static_cast<BaseCombatAIComponent*>(target->GetComponent(COMPONENT_TYPE_BASE_COMBAT_AI));
if (combatAiComponent == nullptr)
{
return;
}
combatAiComponent->Stun(branch.duration);
}
void StunBehavior::Load()
{
this->m_stunCaster = GetBoolean("stun_caster");
}

View File

@@ -0,0 +1,21 @@
#pragma once
#include "Behavior.h"
class StunBehavior final : public Behavior
{
public:
bool m_stunCaster;
/*
* Inherited
*/
explicit StunBehavior(const uint32_t behavior_id) : Behavior(behavior_id)
{
}
void Handle(BehaviorContext* context, RakNet::BitStream* bit_stream, BehaviorBranchContext branch) override;
void Calculate(BehaviorContext* context, RakNet::BitStream* bit_stream, BehaviorBranchContext branch) override;
void Load() override;
};

View File

@@ -0,0 +1,88 @@
#include "SwitchBehavior.h"
#include "BehaviorBranchContext.h"
#include "EntityManager.h"
#include "dLogger.h"
#include "DestroyableComponent.h"
#include "BehaviorContext.h"
#include "BuffComponent.h"
void SwitchBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bitStream, const BehaviorBranchContext branch)
{
auto state = true;
if (this->m_imagination > 0 || !this->m_isEnemyFaction)
{
bitStream->Read(state);
}
auto* entity = EntityManager::Instance()->GetEntity(context->originator);
if (entity == nullptr)
{
return;
}
auto* destroyableComponent = entity->GetComponent<DestroyableComponent>();
if (destroyableComponent == nullptr)
{
return;
}
Game::logger->Log("SwitchBehavior", "[%i] State: (%d), imagination: (%i) / (%f)\n", entity->GetLOT(), state, destroyableComponent->GetImagination(), destroyableComponent->GetMaxImagination());
if (state || (entity->GetLOT() == 8092 && destroyableComponent->GetImagination() >= m_imagination))
{
this->m_actionTrue->Handle(context, bitStream, branch);
}
else
{
this->m_actionFalse->Handle(context, bitStream, branch);
}
}
void SwitchBehavior::Calculate(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch)
{
auto state = true;
if (this->m_imagination > 0 || !this->m_isEnemyFaction)
{
auto* entity = EntityManager::Instance()->GetEntity(branch.target);
state = entity != nullptr;
if (state && m_targetHasBuff != 0)
{
auto* buffComponent = entity->GetComponent<BuffComponent>();
if (buffComponent != nullptr && !buffComponent->HasBuff(m_targetHasBuff))
{
state = false;
}
}
bitStream->Write(state);
}
if (state)
{
this->m_actionTrue->Calculate(context, bitStream, branch);
}
else
{
this->m_actionFalse->Calculate(context, bitStream, branch);
}
}
void SwitchBehavior::Load()
{
this->m_actionTrue = GetAction("action_true");
this->m_actionFalse = GetAction("action_false");
this->m_imagination = GetInt("imagination");
this->m_isEnemyFaction = GetBoolean("isEnemyFaction");
this->m_targetHasBuff = GetInt("target_has_buff");
}

View File

@@ -0,0 +1,30 @@
#pragma once
#include "Behavior.h"
class SwitchBehavior final : public Behavior
{
public:
Behavior* m_actionTrue;
Behavior* m_actionFalse;
uint32_t m_imagination;
bool m_isEnemyFaction;
int32_t m_targetHasBuff;
/*
* Inherited
*/
explicit SwitchBehavior(const uint32_t behaviorId) : Behavior(behaviorId)
{
}
void Handle(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) override;
void Calculate(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) override;
void Load() override;
};

View File

@@ -0,0 +1,63 @@
#include "SwitchMultipleBehavior.h"
#include <sstream>
#include "BehaviorBranchContext.h"
#include "CDActivitiesTable.h"
#include "Game.h"
#include "dLogger.h"
#include "EntityManager.h"
void SwitchMultipleBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bit_stream, BehaviorBranchContext branch)
{
float value;
bit_stream->Read(value);
uint32_t trigger = 0;
for (unsigned int i = 0; i < this->m_behaviors.size(); i++) {
const double data = this->m_behaviors.at(i).first;
if (value <= data) {
trigger = i;
break;
}
}
auto* behavior = this->m_behaviors.at(trigger).second;
behavior->Handle(context, bit_stream, branch);
}
void SwitchMultipleBehavior::Calculate(BehaviorContext* context, RakNet::BitStream* bit_stream, BehaviorBranchContext branch)
{
// TODO
}
void SwitchMultipleBehavior::Load()
{
const auto b = std::to_string(this->m_behaviorId);
std::stringstream query;
query << "SELECT replace(bP1.parameterID, 'behavior ', '') as key, bP1.value as behavior, "
<< "(select bP2.value FROM BehaviorParameter bP2 WHERE bP2.behaviorID = " << b << " AND bP2.parameterID LIKE 'value %' "
<< "AND replace(bP1.parameterID, 'behavior ', '') = replace(bP2.parameterID, 'value ', '')) as value "
<< "FROM BehaviorParameter bP1 WHERE bP1.behaviorID = " << b << " AND bP1.parameterID LIKE 'behavior %'";
auto result = CDClientDatabase::ExecuteQuery(query.str());
while (!result.eof()) {
const auto behavior_id = static_cast<uint32_t>(result.getFloatField(1));
auto* behavior = CreateBehavior(behavior_id);
auto value = result.getFloatField(2);
this->m_behaviors.emplace_back(value, behavior);
result.nextRow();
}
}

View File

@@ -0,0 +1,24 @@
#pragma once
#include "Behavior.h"
#include <vector>
class SwitchMultipleBehavior final : public Behavior
{
public:
std::vector<std::pair<float, Behavior*>> m_behaviors;
/*
* Inherited
*/
explicit SwitchMultipleBehavior(const uint32_t behavior_id) : Behavior(behavior_id)
{
}
void Handle(BehaviorContext* context, RakNet::BitStream* bit_stream, BehaviorBranchContext branch) override;
void Calculate(BehaviorContext* context, RakNet::BitStream* bit_stream, BehaviorBranchContext branch) override;
void Load() override;
};

View File

@@ -0,0 +1,288 @@
#include "TacArcBehavior.h"
#include "BehaviorBranchContext.h"
#include "Game.h"
#include "dLogger.h"
#include "Entity.h"
#include "BehaviorContext.h"
#include "BaseCombatAIComponent.h"
#include "EntityManager.h"
#include "RebuildComponent.h"
#include "DestroyableComponent.h"
#include <vector>
void TacArcBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch)
{
if (this->m_targetEnemy && this->m_usePickedTarget && branch.target > 0)
{
this->m_action->Handle(context, bitStream, branch);
return;
}
bool hit = false;
bitStream->Read(hit);
if (this->m_checkEnv)
{
bool blocked = false;
bitStream->Read(blocked);
if (blocked)
{
this->m_blockedAction->Handle(context, bitStream, branch);
return;
}
}
if (hit)
{
uint32_t count = 0;
bitStream->Read(count);
if (count > m_maxTargets && m_maxTargets > 0)
{
count = m_maxTargets;
}
std::vector<LWOOBJID> targets;
for (auto i = 0u; i < count; ++i)
{
LWOOBJID id;
bitStream->Read(id);
targets.push_back(id);
}
for (auto target : targets)
{
branch.target = target;
this->m_action->Handle(context, bitStream, branch);
}
}
else
{
this->m_missAction->Handle(context, bitStream, branch);
}
}
void TacArcBehavior::Calculate(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch)
{
auto* self = EntityManager::Instance()->GetEntity(context->originator);
if (self == nullptr) {
Game::logger->Log("TacArcBehavior", "Invalid self for (%llu)!\n", context->originator);
return;
}
const auto* destroyableComponent = self->GetComponent<DestroyableComponent>();
if ((this->m_usePickedTarget || context->clientInitalized) && branch.target > 0) {
const auto* target = EntityManager::Instance()->GetEntity(branch.target);
if (target == nullptr)
{
return;
}
// If the game is specific about who to target, check that
if (destroyableComponent == nullptr || ((!m_targetFriend && !m_targetEnemy
|| m_targetFriend && destroyableComponent->IsFriend(target)
|| m_targetEnemy && destroyableComponent->IsEnemy(target)))) {
this->m_action->Calculate(context, bitStream, branch);
}
return;
}
auto* combatAi = self->GetComponent<BaseCombatAIComponent>();
const auto casterPosition = self->GetPosition();
auto reference = self->GetPosition(); //+ m_offset;
std::vector<Entity*> targets;
std::vector<LWOOBJID> validTargets;
if (combatAi != nullptr)
{
if (combatAi->GetTarget() != LWOOBJID_EMPTY)
{
validTargets.push_back(combatAi->GetTarget());
}
}
// Find all valid targets, based on whether we target enemies or friends
for (const auto& contextTarget : context->GetValidTargets()) {
if (destroyableComponent != nullptr) {
const auto* targetEntity = EntityManager::Instance()->GetEntity(contextTarget);
if (m_targetEnemy && destroyableComponent->IsEnemy(targetEntity)
|| m_targetFriend && destroyableComponent->IsFriend(targetEntity)) {
validTargets.push_back(contextTarget);
}
} else {
validTargets.push_back(contextTarget);
}
}
for (auto validTarget : validTargets)
{
if (targets.size() >= this->m_maxTargets)
{
break;
}
auto* entity = EntityManager::Instance()->GetEntity(validTarget);
if (entity == nullptr)
{
Game::logger->Log("TacArcBehavior", "Invalid target (%llu) for (%llu)!\n", validTarget, context->originator);
continue;
}
if (std::find(targets.begin(), targets.end(), entity) != targets.end())
{
continue;
}
if (entity->GetIsDead()) continue;
const auto otherPosition = entity->GetPosition();
const auto heightDifference = std::abs(otherPosition.y - casterPosition.y);
/*if (otherPosition.y > reference.y && heightDifference > this->m_upperBound || otherPosition.y < reference.y && heightDifference > this->m_lowerBound)
{
continue;
}*/
const auto forward = self->GetRotation().GetForwardVector();
// forward is a normalized vector of where the caster is facing.
// otherPosition is the position of the target.
// reference is the position of the caster.
// If we cast a ray forward from the caster, does it come within m_farWidth of the target?
const auto distance = Vector3::Distance(reference, otherPosition);
if (m_method == 2)
{
NiPoint3 rayPoint = casterPosition + forward * distance;
if (m_farWidth > 0 && Vector3::DistanceSquared(rayPoint, otherPosition) > this->m_farWidth * this->m_farWidth)
{
continue;
}
}
auto normalized = (reference - otherPosition) / distance;
const float degreeAngle = std::abs(Vector3::Angle(forward, normalized) * (180 / 3.14) - 180);
if (distance >= this->m_minDistance && this->m_maxDistance >= distance && degreeAngle <= 2 * this->m_angle)
{
targets.push_back(entity);
}
}
std::sort(targets.begin(), targets.end(), [reference](Entity* a, Entity* b)
{
const auto aDistance = Vector3::DistanceSquared(reference, a->GetPosition());
const auto bDistance = Vector3::DistanceSquared(reference, b->GetPosition());
return aDistance > bDistance;
});
const auto hit = !targets.empty();
bitStream->Write(hit);
if (this->m_checkEnv)
{
const auto blocked = false; // TODO
bitStream->Write(blocked);
}
if (hit)
{
if (combatAi != nullptr)
{
combatAi->LookAt(targets[0]->GetPosition());
}
context->foundTarget = true; // We want to continue with this behavior
const auto count = static_cast<uint32_t>(targets.size());
bitStream->Write(count);
for (auto* target : targets)
{
bitStream->Write(target->GetObjectID());
}
for (auto* target : targets)
{
branch.target = target->GetObjectID();
this->m_action->Calculate(context, bitStream, branch);
}
}
else
{
this->m_missAction->Calculate(context, bitStream, branch);
}
}
void TacArcBehavior::Load()
{
this->m_usePickedTarget = GetBoolean("use_picked_target");
this->m_action = GetAction("action");
this->m_missAction = GetAction("miss action");
this->m_checkEnv = GetBoolean("check_env");
this->m_blockedAction = GetAction("blocked action");
this->m_minDistance = GetFloat("min range");
this->m_maxDistance = GetFloat("max range");
this->m_maxTargets = GetInt("max targets");
this->m_targetEnemy = GetBoolean("target_enemy");
this->m_targetFriend = GetBoolean("target_friend");
this->m_targetTeam = GetBoolean("target_team");
this->m_angle = GetFloat("angle");
this->m_upperBound = GetFloat("upper_bound");
this->m_lowerBound = GetFloat("lower_bound");
this->m_farHeight = GetFloat("far_height");
this->m_farWidth = GetFloat("far_width");
this->m_method = GetInt("method");
this->m_offset = {
GetFloat("offset_x"),
GetFloat("offset_y"),
GetFloat("offset_z")
};
}

View File

@@ -0,0 +1,58 @@
#pragma once
#include "Behavior.h"
#include "dCommonVars.h"
#include "NiPoint3.h"
class TacArcBehavior final : public Behavior
{
public:
bool m_usePickedTarget;
Behavior* m_action;
bool m_checkEnv;
Behavior* m_missAction;
Behavior* m_blockedAction;
float m_minDistance;
float m_maxDistance;
uint32_t m_maxTargets;
bool m_targetEnemy;
bool m_targetFriend;
bool m_targetTeam;
float m_angle;
float m_upperBound;
float m_lowerBound;
float m_farHeight;
float m_farWidth;
uint32_t m_method;
NiPoint3 m_offset;
/*
* Inherited
*/
explicit TacArcBehavior(const uint32_t behavior_id) : Behavior(behavior_id)
{
}
void Handle(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) override;
void Calculate(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) override;
void Load() override;
};

View File

@@ -0,0 +1,29 @@
#include "TargetCasterBehavior.h"
#include "BehaviorBranchContext.h"
#include "BehaviorContext.h"
void TargetCasterBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bit_stream, BehaviorBranchContext branch)
{
branch.target = context->caster;
this->m_action->Handle(context, bit_stream, branch);
}
void TargetCasterBehavior::UnCast(BehaviorContext* context, BehaviorBranchContext branch)
{
this->m_action->UnCast(context, branch);
}
void TargetCasterBehavior::Calculate(BehaviorContext* context, RakNet::BitStream* bit_stream, BehaviorBranchContext branch)
{
branch.target = context->caster;
this->m_action->Calculate(context, bit_stream, branch);
}
void TargetCasterBehavior::Load()
{
this->m_action = GetAction("action");
}

View File

@@ -0,0 +1,24 @@
#pragma once
#include "Behavior.h"
class TargetCasterBehavior final : public Behavior
{
public:
Behavior* m_action;
/*
* Inherited
*/
explicit TargetCasterBehavior(const uint32_t behavior_id) : Behavior(behavior_id)
{
}
void Handle(BehaviorContext* context, RakNet::BitStream* bit_stream, BehaviorBranchContext branch) override;
void UnCast(BehaviorContext* context, BehaviorBranchContext branch) override;
void Calculate(BehaviorContext* context, RakNet::BitStream* bit_stream, BehaviorBranchContext branch) override;
void Load() override;
};

View File

@@ -0,0 +1,51 @@
#include "TauntBehavior.h"
#include "BehaviorBranchContext.h"
#include "BehaviorContext.h"
#include "BaseCombatAIComponent.h"
#include "EntityManager.h"
#include "dLogger.h"
void TauntBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch)
{
auto* target = EntityManager::Instance()->GetEntity(branch.target);
if (target == nullptr)
{
Game::logger->Log("TauntBehavior", "Failed to find target (%llu)!\n", branch.target);
return;
}
auto* combatComponent = target->GetComponent<BaseCombatAIComponent>();
if (combatComponent != nullptr)
{
combatComponent->Taunt(context->originator, m_threatToAdd);
}
}
void TauntBehavior::Calculate(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch)
{
auto* target = EntityManager::Instance()->GetEntity(branch.target);
if (target == nullptr)
{
Game::logger->Log("TauntBehavior", "Failed to find target (%llu)!\n", branch.target);
return;
}
auto* combatComponent = target->GetComponent<BaseCombatAIComponent>();
if (combatComponent != nullptr)
{
combatComponent->Taunt(context->originator, m_threatToAdd);
}
}
void TauntBehavior::Load()
{
this->m_threatToAdd = GetFloat("threat to add");
}

View File

@@ -0,0 +1,23 @@
#pragma once
#include "Behavior.h"
class TauntBehavior final : public Behavior
{
public:
float m_threatToAdd;
/*
* Inherited
*/
explicit TauntBehavior(const uint32_t behaviorId) : Behavior(behaviorId)
{
}
void Handle(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) override;
void Calculate(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) override;
void Load() override;
};

View File

@@ -0,0 +1,72 @@
#include "VerifyBehavior.h"
#include "BehaviorBranchContext.h"
#include "EntityManager.h"
#include "NiPoint3.h"
#include "BehaviorContext.h"
#include "Game.h"
#include "dLogger.h"
void VerifyBehavior::Calculate(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch)
{
auto* entity = EntityManager::Instance()->GetEntity(branch.target);
auto success = true;
if (entity == nullptr)
{
success = false;
}
else if (this->m_rangeCheck)
{
auto* self = EntityManager::Instance()->GetEntity(context->originator);
if (self == nullptr)
{
Game::logger->Log("VerifyBehavior", "Invalid self for (%llu)", context->originator);
return;
}
const auto distance = Vector3::DistanceSquared(self->GetPosition(), entity->GetPosition());
if (distance > this->m_range * this->m_range)
{
success = false;
}
}
else if (this->m_blockCheck)
{
// TODO
}
if (branch.target != LWOOBJID_EMPTY && branch.target != context->originator)
{
bitStream->Write(success);
if (success)
{
bitStream->Write<uint32_t>(1);
bitStream->Write0();
bitStream->Write0();
}
}
if (!success)
{
branch.target = LWOOBJID_EMPTY;
}
m_action->Calculate(context, bitStream, branch);
}
void VerifyBehavior::Load()
{
this->m_rangeCheck = GetBoolean("check_range");
this->m_blockCheck = GetBoolean("check blocking");
this->m_action = GetAction("action");
this->m_range = GetFloat("range");
}

View File

@@ -0,0 +1,26 @@
#pragma once
#include "Behavior.h"
class VerifyBehavior final : public Behavior
{
public:
bool m_rangeCheck;
bool m_blockCheck;
float m_range;
Behavior* m_action;
/*
* Inherited
*/
explicit VerifyBehavior(const uint32_t behaviorId) : Behavior(behaviorId)
{
}
void Calculate(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) override;
void Load() override;
};