2022-08-06 03:01:59 +00:00
|
|
|
#include "AreaOfEffectBehavior.h"
|
2021-12-05 17:54:36 +00:00
|
|
|
|
|
|
|
#include <vector>
|
|
|
|
|
|
|
|
#include "EntityManager.h"
|
|
|
|
#include "Game.h"
|
2023-10-21 23:31:55 +00:00
|
|
|
#include "Logger.h"
|
2021-12-05 17:54:36 +00:00
|
|
|
#include "BehaviorBranchContext.h"
|
|
|
|
#include "BehaviorContext.h"
|
2023-12-29 04:24:30 +00:00
|
|
|
#include "QuickBuildComponent.h"
|
2021-12-05 17:54:36 +00:00
|
|
|
#include "DestroyableComponent.h"
|
2022-12-16 21:23:02 +00:00
|
|
|
#include "Game.h"
|
2023-10-21 23:31:55 +00:00
|
|
|
#include "Logger.h"
|
2021-12-05 17:54:36 +00:00
|
|
|
|
2024-02-27 07:25:44 +00:00
|
|
|
void AreaOfEffectBehavior::Handle(BehaviorContext* context, RakNet::BitStream& bitStream, BehaviorBranchContext branch) {
|
2022-12-16 21:23:02 +00:00
|
|
|
uint32_t targetCount{};
|
2021-12-05 17:54:36 +00:00
|
|
|
|
2024-02-27 07:25:44 +00:00
|
|
|
if (!bitStream.Read(targetCount)) {
|
2024-03-08 21:44:02 +00:00
|
|
|
Log::Warn("Unable to read targetCount from bitStream, aborting Handle! {:d}", bitStream.GetNumberOfUnreadBits());
|
2022-12-16 21:23:02 +00:00
|
|
|
return;
|
|
|
|
}
|
2021-12-05 17:54:36 +00:00
|
|
|
|
2023-10-09 20:18:51 +00:00
|
|
|
if (this->m_useTargetPosition && branch.target == LWOOBJID_EMPTY) return;
|
|
|
|
|
|
|
|
if (targetCount == 0){
|
|
|
|
PlayFx(u"miss", context->originator);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2022-07-28 13:39:57 +00:00
|
|
|
if (targetCount > this->m_maxTargets) {
|
2024-03-08 21:44:02 +00:00
|
|
|
Log::Warn("Serialized size is greater than max targets! Size: {:d}, Max: {:d}", targetCount, this->m_maxTargets);
|
2021-12-05 17:54:36 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2023-10-09 20:18:51 +00:00
|
|
|
auto caster = context->caster;
|
|
|
|
if (this->m_useTargetAsCaster) context->caster = branch.target;
|
2021-12-05 17:54:36 +00:00
|
|
|
|
2023-10-09 20:18:51 +00:00
|
|
|
std::vector<LWOOBJID> targets;
|
2021-12-05 17:54:36 +00:00
|
|
|
targets.reserve(targetCount);
|
|
|
|
|
2022-07-28 13:39:57 +00:00
|
|
|
for (auto i = 0u; i < targetCount; ++i) {
|
2022-12-16 21:23:02 +00:00
|
|
|
LWOOBJID target{};
|
2024-02-27 07:25:44 +00:00
|
|
|
if (!bitStream.Read(target)) {
|
2024-03-08 21:44:02 +00:00
|
|
|
Log::Warn("failed to read in target {:d} from bitStream, aborting target Handle!", i);
|
2022-12-16 21:23:02 +00:00
|
|
|
};
|
2021-12-05 17:54:36 +00:00
|
|
|
targets.push_back(target);
|
|
|
|
}
|
|
|
|
|
2022-07-28 13:39:57 +00:00
|
|
|
for (auto target : targets) {
|
2021-12-05 17:54:36 +00:00
|
|
|
branch.target = target;
|
|
|
|
this->m_action->Handle(context, bitStream, branch);
|
|
|
|
}
|
2023-10-09 20:18:51 +00:00
|
|
|
context->caster = caster;
|
|
|
|
PlayFx(u"cast", context->originator);
|
2021-12-05 17:54:36 +00:00
|
|
|
}
|
|
|
|
|
2024-02-27 07:25:44 +00:00
|
|
|
void AreaOfEffectBehavior::Calculate(BehaviorContext* context, RakNet::BitStream& bitStream, BehaviorBranchContext branch) {
|
2023-10-09 20:18:51 +00:00
|
|
|
auto* caster = Game::entityManager->GetEntity(context->caster);
|
|
|
|
if (!caster) return;
|
|
|
|
|
|
|
|
// determine the position we are casting the AOE from
|
|
|
|
auto reference = branch.isProjectile ? branch.referencePosition : caster->GetPosition();
|
|
|
|
if (this->m_useTargetPosition) {
|
|
|
|
if (branch.target == LWOOBJID_EMPTY) return;
|
|
|
|
auto branchTarget = Game::entityManager->GetEntity(branch.target);
|
|
|
|
if (branchTarget) reference = branchTarget->GetPosition();
|
2021-12-05 17:54:36 +00:00
|
|
|
}
|
|
|
|
|
2023-10-09 20:18:51 +00:00
|
|
|
reference += this->m_offset;
|
2021-12-05 17:54:36 +00:00
|
|
|
|
2023-10-09 20:18:51 +00:00
|
|
|
std::vector<Entity*> targets {};
|
|
|
|
targets = Game::entityManager->GetEntitiesByProximity(reference, this->m_radius);
|
|
|
|
context->FilterTargets(targets, this->m_ignoreFactionList, this->m_includeFactionList, this->m_targetSelf, this->m_targetEnemy, this->m_targetFriend, this->m_targetTeam);
|
2021-12-05 17:54:36 +00:00
|
|
|
|
2023-10-09 20:18:51 +00:00
|
|
|
// sort by distance
|
|
|
|
std::sort(targets.begin(), targets.end(), [reference](Entity* a, Entity* b) {
|
|
|
|
const auto aDistance = NiPoint3::Distance(a->GetPosition(), reference);
|
|
|
|
const auto bDistance = NiPoint3::Distance(b->GetPosition(), reference);
|
|
|
|
return aDistance < bDistance;
|
2021-12-05 17:54:36 +00:00
|
|
|
}
|
2023-10-09 20:18:51 +00:00
|
|
|
);
|
2022-07-25 02:26:51 +00:00
|
|
|
|
2023-10-09 20:18:51 +00:00
|
|
|
// resize if we have more than max targets allows
|
|
|
|
if (targets.size() > this->m_maxTargets) targets.resize(this->m_maxTargets);
|
2021-12-05 17:54:36 +00:00
|
|
|
|
2024-02-27 07:25:44 +00:00
|
|
|
bitStream.Write<uint32_t>(targets.size());
|
2021-12-05 17:54:36 +00:00
|
|
|
|
2023-10-09 20:18:51 +00:00
|
|
|
if (targets.size() == 0) {
|
|
|
|
PlayFx(u"miss", context->originator);
|
|
|
|
return;
|
|
|
|
} else {
|
|
|
|
context->foundTarget = true;
|
|
|
|
// write all the targets to the bitstream
|
|
|
|
for (auto* target : targets) {
|
2024-02-27 07:25:44 +00:00
|
|
|
bitStream.Write(target->GetObjectID());
|
2021-12-05 17:54:36 +00:00
|
|
|
}
|
|
|
|
|
2023-10-09 20:18:51 +00:00
|
|
|
// then cast all the actions
|
|
|
|
for (auto* target : targets) {
|
|
|
|
branch.target = target->GetObjectID();
|
|
|
|
this->m_action->Calculate(context, bitStream, branch);
|
2021-12-05 17:54:36 +00:00
|
|
|
}
|
2023-10-09 20:18:51 +00:00
|
|
|
PlayFx(u"cast", context->originator);
|
2021-12-05 17:54:36 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-07-28 13:39:57 +00:00
|
|
|
void AreaOfEffectBehavior::Load() {
|
2023-10-09 20:18:51 +00:00
|
|
|
this->m_action = GetAction("action"); // required
|
|
|
|
this->m_radius = GetFloat("radius", 0.0f); // required
|
|
|
|
this->m_maxTargets = GetInt("max targets", 100);
|
|
|
|
if (this->m_maxTargets == 0) this->m_maxTargets = 100;
|
|
|
|
this->m_useTargetPosition = GetBoolean("use_target_position", false);
|
|
|
|
this->m_useTargetAsCaster = GetBoolean("use_target_as_caster", false);
|
|
|
|
this->m_offset = NiPoint3(
|
|
|
|
GetFloat("offset_x", 0.0f),
|
|
|
|
GetFloat("offset_y", 0.0f),
|
|
|
|
GetFloat("offset_z", 0.0f)
|
|
|
|
);
|
|
|
|
|
|
|
|
// params after this are needed for filter targets
|
|
|
|
const auto parameters = GetParameterNames();
|
|
|
|
for (const auto& parameter : parameters) {
|
|
|
|
if (parameter.first.rfind("include_faction", 0) == 0) {
|
|
|
|
this->m_includeFactionList.push_front(parameter.second);
|
|
|
|
} else if (parameter.first.rfind("ignore_faction", 0) == 0) {
|
|
|
|
this->m_ignoreFactionList.push_front(parameter.second);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
this->m_targetSelf = GetBoolean("target_self", false);
|
|
|
|
this->m_targetEnemy = GetBoolean("target_enemy", false);
|
|
|
|
this->m_targetFriend = GetBoolean("target_friend", false);
|
|
|
|
this->m_targetTeam = GetBoolean("target_team", false);
|
2021-12-05 17:54:36 +00:00
|
|
|
}
|