2021-12-05 17:54:36 +00:00
|
|
|
#include "TacArcBehavior.h"
|
|
|
|
#include "BehaviorBranchContext.h"
|
|
|
|
#include "Game.h"
|
2023-10-21 23:31:55 +00:00
|
|
|
#include "Logger.h"
|
2021-12-05 17:54:36 +00:00
|
|
|
#include "Entity.h"
|
|
|
|
#include "BehaviorContext.h"
|
|
|
|
#include "BaseCombatAIComponent.h"
|
|
|
|
#include "EntityManager.h"
|
2023-12-29 04:24:30 +00:00
|
|
|
#include "QuickBuildComponent.h"
|
2021-12-05 17:54:36 +00:00
|
|
|
#include "DestroyableComponent.h"
|
|
|
|
|
|
|
|
#include <vector>
|
|
|
|
|
2024-02-27 07:25:44 +00:00
|
|
|
void TacArcBehavior::Handle(BehaviorContext* context, RakNet::BitStream& bitStream, BehaviorBranchContext branch) {
|
2023-10-09 20:18:51 +00:00
|
|
|
std::vector<Entity*> targets = {};
|
|
|
|
|
|
|
|
if (this->m_usePickedTarget && branch.target != LWOOBJID_EMPTY) {
|
|
|
|
auto target = Game::entityManager->GetEntity(branch.target);
|
2024-03-13 02:10:01 +00:00
|
|
|
if (!target) Log::Warn("target {} is null", branch.target);
|
2023-10-09 20:18:51 +00:00
|
|
|
else {
|
|
|
|
targets.push_back(target);
|
|
|
|
context->FilterTargets(targets, this->m_ignoreFactionList, this->m_includeFactionList, this->m_targetSelf, this->m_targetEnemy, this->m_targetFriend, this->m_targetTeam);
|
|
|
|
if (!targets.empty()) {
|
|
|
|
this->m_action->Handle(context, bitStream, branch);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
2021-12-05 17:54:36 +00:00
|
|
|
}
|
|
|
|
|
2023-10-09 20:18:51 +00:00
|
|
|
bool hasTargets = false;
|
2024-02-27 07:25:44 +00:00
|
|
|
if (!bitStream.Read(hasTargets)) {
|
2024-03-13 02:10:01 +00:00
|
|
|
Log::Warn("Unable to read hasTargets from bitStream, aborting Handle! {}", bitStream.GetNumberOfUnreadBits());
|
2022-12-18 14:49:13 +00:00
|
|
|
return;
|
2022-12-16 21:23:02 +00:00
|
|
|
};
|
2021-12-05 17:54:36 +00:00
|
|
|
|
|
|
|
if (this->m_checkEnv) {
|
|
|
|
bool blocked = false;
|
2022-07-25 02:26:51 +00:00
|
|
|
|
2024-02-27 07:25:44 +00:00
|
|
|
if (!bitStream.Read(blocked)) {
|
2024-03-13 02:10:01 +00:00
|
|
|
Log::Warn("Unable to read blocked from bitStream, aborting Handle! {}", bitStream.GetNumberOfUnreadBits());
|
2022-12-18 14:49:13 +00:00
|
|
|
return;
|
2022-12-16 21:23:02 +00:00
|
|
|
};
|
2021-12-05 17:54:36 +00:00
|
|
|
|
|
|
|
if (blocked) {
|
|
|
|
this->m_blockedAction->Handle(context, bitStream, branch);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-10-09 20:18:51 +00:00
|
|
|
if (hasTargets) {
|
2021-12-05 17:54:36 +00:00
|
|
|
uint32_t count = 0;
|
2024-02-27 07:25:44 +00:00
|
|
|
if (!bitStream.Read(count)) {
|
2024-03-13 02:10:01 +00:00
|
|
|
Log::Warn("Unable to read count from bitStream, aborting Handle! {}", bitStream.GetNumberOfUnreadBits());
|
2022-12-18 14:49:13 +00:00
|
|
|
return;
|
2022-12-16 21:23:02 +00:00
|
|
|
};
|
2021-12-05 17:54:36 +00:00
|
|
|
|
2023-10-09 20:18:51 +00:00
|
|
|
if (count > m_maxTargets) {
|
2024-03-13 02:10:01 +00:00
|
|
|
Log::Warn("Bitstream has too many targets Max:{} Recv:{}", this->m_maxTargets, count);
|
2023-10-09 20:18:51 +00:00
|
|
|
return;
|
2021-12-05 17:54:36 +00:00
|
|
|
}
|
|
|
|
|
2023-10-09 20:18:51 +00:00
|
|
|
for (auto i = 0u; i < count; i++) {
|
2022-12-16 21:23:02 +00:00
|
|
|
LWOOBJID id{};
|
2021-12-05 17:54:36 +00:00
|
|
|
|
2024-02-27 07:25:44 +00:00
|
|
|
if (!bitStream.Read(id)) {
|
2024-03-13 02:10:01 +00:00
|
|
|
Log::Warn("Unable to read id from bitStream, aborting Handle! {}", bitStream.GetNumberOfUnreadBits());
|
2022-12-18 14:49:13 +00:00
|
|
|
return;
|
2022-12-16 21:23:02 +00:00
|
|
|
};
|
2021-12-05 17:54:36 +00:00
|
|
|
|
2023-10-09 20:18:51 +00:00
|
|
|
if (id != LWOOBJID_EMPTY) {
|
|
|
|
auto* canidate = Game::entityManager->GetEntity(id);
|
|
|
|
if (canidate) targets.push_back(canidate);
|
|
|
|
} else {
|
2024-03-13 02:10:01 +00:00
|
|
|
Log::Warn("Bitstream has LWOOBJID_EMPTY as a target!");
|
2023-10-09 20:18:51 +00:00
|
|
|
}
|
2021-12-05 17:54:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
for (auto target : targets) {
|
2023-10-09 20:18:51 +00:00
|
|
|
branch.target = target->GetObjectID();
|
2021-12-05 17:54:36 +00:00
|
|
|
this->m_action->Handle(context, bitStream, branch);
|
|
|
|
}
|
2023-10-09 20:18:51 +00:00
|
|
|
} else this->m_missAction->Handle(context, bitStream, branch);
|
2021-12-05 17:54:36 +00:00
|
|
|
}
|
|
|
|
|
2024-02-27 07:25:44 +00:00
|
|
|
void TacArcBehavior::Calculate(BehaviorContext* context, RakNet::BitStream& bitStream, BehaviorBranchContext branch) {
|
2023-07-15 20:56:33 +00:00
|
|
|
auto* self = Game::entityManager->GetEntity(context->originator);
|
2021-12-05 17:54:36 +00:00
|
|
|
if (self == nullptr) {
|
2024-03-13 02:10:01 +00:00
|
|
|
Log::Warn("Invalid self for ({})!", context->originator);
|
2021-12-05 17:54:36 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2023-10-09 20:18:51 +00:00
|
|
|
std::vector<Entity*> targets = {};
|
|
|
|
if (this->m_usePickedTarget && branch.target != LWOOBJID_EMPTY) {
|
|
|
|
auto target = Game::entityManager->GetEntity(branch.target);
|
|
|
|
targets.push_back(target);
|
|
|
|
context->FilterTargets(targets, this->m_ignoreFactionList, this->m_includeFactionList, this->m_targetSelf, this->m_targetEnemy, this->m_targetFriend, this->m_targetTeam);
|
|
|
|
if (!targets.empty()) {
|
|
|
|
this->m_action->Handle(context, bitStream, branch);
|
2021-12-05 17:54:36 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
auto* combatAi = self->GetComponent<BaseCombatAIComponent>();
|
2022-07-25 02:26:51 +00:00
|
|
|
|
2021-12-05 17:54:36 +00:00
|
|
|
const auto casterPosition = self->GetPosition();
|
|
|
|
|
2023-11-28 04:16:31 +00:00
|
|
|
auto reference = self->GetPosition() + m_offset;
|
2021-12-05 17:54:36 +00:00
|
|
|
|
2023-10-09 20:18:51 +00:00
|
|
|
targets.clear();
|
2021-12-05 17:54:36 +00:00
|
|
|
|
2023-10-09 20:18:51 +00:00
|
|
|
std::vector<Entity*> validTargets = Game::entityManager->GetEntitiesByProximity(reference, this->m_maxRange);
|
2022-07-28 13:39:57 +00:00
|
|
|
|
2023-10-09 20:18:51 +00:00
|
|
|
// filter all valid targets, based on whether we target enemies or friends
|
|
|
|
context->FilterTargets(validTargets, 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
|
|
|
|
|
|
|
for (auto validTarget : validTargets) {
|
2023-11-28 04:16:31 +00:00
|
|
|
if (targets.size() >= this->m_maxTargets) break;
|
|
|
|
if (std::find(targets.begin(), targets.end(), validTarget) != targets.end()) continue;
|
2023-10-09 20:18:51 +00:00
|
|
|
if (validTarget->GetIsDead()) continue;
|
2021-12-05 17:54:36 +00:00
|
|
|
|
2023-11-28 04:16:31 +00:00
|
|
|
const auto targetPos = validTarget->GetPosition();
|
2021-12-05 17:54:36 +00:00
|
|
|
|
2023-11-28 04:16:31 +00:00
|
|
|
// make sure we aren't too high or low in comparison to the targer
|
2023-11-28 04:18:27 +00:00
|
|
|
const auto heightDifference = std::abs(reference.y - targetPos.y);
|
2023-11-28 04:16:31 +00:00
|
|
|
if (targetPos.y > reference.y && heightDifference > this->m_upperBound || targetPos.y < reference.y && heightDifference > this->m_lowerBound)
|
2021-12-05 17:54:36 +00:00
|
|
|
continue;
|
|
|
|
|
|
|
|
const auto forward = self->GetRotation().GetForwardVector();
|
|
|
|
|
|
|
|
// forward is a normalized vector of where the caster is facing.
|
2023-11-28 04:16:31 +00:00
|
|
|
// targetPos is the position of the target.
|
2021-12-05 17:54:36 +00:00
|
|
|
// 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?
|
2022-07-25 02:26:51 +00:00
|
|
|
|
2023-11-28 04:16:31 +00:00
|
|
|
const auto distance = Vector3::Distance(reference, targetPos);
|
2021-12-05 17:54:36 +00:00
|
|
|
|
|
|
|
if (m_method == 2) {
|
|
|
|
NiPoint3 rayPoint = casterPosition + forward * distance;
|
2023-11-28 04:16:31 +00:00
|
|
|
if (m_farWidth > 0 && Vector3::DistanceSquared(rayPoint, targetPos) > this->m_farWidth * this->m_farWidth)
|
2021-12-05 17:54:36 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2023-11-28 04:16:31 +00:00
|
|
|
auto normalized = (reference - targetPos) / distance;
|
2021-12-05 17:54:36 +00:00
|
|
|
const float degreeAngle = std::abs(Vector3::Angle(forward, normalized) * (180 / 3.14) - 180);
|
2023-10-09 20:18:51 +00:00
|
|
|
if (distance >= this->m_minRange && this->m_maxRange >= distance && degreeAngle <= 2 * this->m_angle) {
|
|
|
|
targets.push_back(validTarget);
|
2021-12-05 17:54:36 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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();
|
2024-02-27 07:25:44 +00:00
|
|
|
bitStream.Write(hit);
|
2021-12-05 17:54:36 +00:00
|
|
|
|
|
|
|
if (this->m_checkEnv) {
|
|
|
|
const auto blocked = false; // TODO
|
2024-02-27 07:25:44 +00:00
|
|
|
bitStream.Write(blocked);
|
2021-12-05 17:54:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (hit) {
|
2023-11-28 04:16:31 +00:00
|
|
|
if (combatAi) combatAi->LookAt(targets[0]->GetPosition());
|
2021-12-05 17:54:36 +00:00
|
|
|
|
|
|
|
context->foundTarget = true; // We want to continue with this behavior
|
|
|
|
const auto count = static_cast<uint32_t>(targets.size());
|
|
|
|
|
2024-02-27 07:25:44 +00:00
|
|
|
bitStream.Write(count);
|
2021-12-05 17:54:36 +00:00
|
|
|
for (auto* target : targets) {
|
2024-02-27 07:25:44 +00:00
|
|
|
bitStream.Write(target->GetObjectID());
|
2021-12-05 17:54:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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() {
|
2023-10-09 20:18:51 +00:00
|
|
|
this->m_maxRange = GetFloat("max range");
|
|
|
|
this->m_height = GetFloat("height", 2.2f);
|
|
|
|
this->m_distanceWeight = GetFloat("distance_weight", 0.0f);
|
|
|
|
this->m_angleWeight = GetFloat("angle_weight", 0.0f);
|
|
|
|
this->m_angle = GetFloat("angle", 45.0f);
|
|
|
|
this->m_minRange = GetFloat("min range", 0.0f);
|
|
|
|
this->m_offset = NiPoint3(
|
|
|
|
GetFloat("offset_x", 0.0f),
|
|
|
|
GetFloat("offset_y", 0.0f),
|
|
|
|
GetFloat("offset_z", 0.0f)
|
|
|
|
);
|
|
|
|
this->m_method = GetInt("method", 1);
|
2023-11-28 04:18:27 +00:00
|
|
|
this->m_upperBound = std::abs(GetFloat("upper_bound", 4.4f));
|
|
|
|
this->m_lowerBound = std::abs(GetFloat("lower_bound", 0.4f));
|
2023-10-09 20:18:51 +00:00
|
|
|
this->m_usePickedTarget = GetBoolean("use_picked_target", false);
|
|
|
|
this->m_useTargetPostion = GetBoolean("use_target_position", false);
|
|
|
|
this->m_checkEnv = GetBoolean("check_env", false);
|
|
|
|
this->m_useAttackPriority = GetBoolean("use_attack_priority", false);
|
2021-12-05 17:54:36 +00:00
|
|
|
|
|
|
|
this->m_action = GetAction("action");
|
|
|
|
this->m_missAction = GetAction("miss action");
|
|
|
|
this->m_blockedAction = GetAction("blocked action");
|
|
|
|
|
2023-10-09 20:18:51 +00:00
|
|
|
this->m_maxTargets = GetInt("max targets", 100);
|
|
|
|
if (this->m_maxTargets == 0) this->m_maxTargets = 100;
|
2021-12-05 17:54:36 +00:00
|
|
|
|
2023-10-09 20:18:51 +00:00
|
|
|
this->m_farHeight = GetFloat("far_height", 5.0f);
|
|
|
|
this->m_farWidth = GetFloat("far_width", 5.0f);
|
|
|
|
this->m_nearHeight = GetFloat("near_height", 5.0f);
|
|
|
|
this->m_nearWidth = GetFloat("near_width", 5.0f);
|
2021-12-05 17:54:36 +00:00
|
|
|
|
2023-10-09 20:18:51 +00:00
|
|
|
// 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_caster", 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
|
|
|
}
|