mirror of
https://github.com/DarkflameUniverse/DarkflameServer.git
synced 2025-08-04 01:34:07 +00:00
refactor: re-write AOE, add FilterTargets, Update TacArc Reading (#1035)
* Re-write AOE behavior for new filter targets Update Tacarc to use new filter targets Added dev commands for skill and attack debugging * Get all entities by detroyable rather than controllable physics Since destroyables are what can be hit * Re-work filter targets to be 100% live accurate reduce memory usage by only using one vector and removing invalid entries get entities in the proximity rather than all entities with des comps in the instance, as was done in live * remove debuging longs and remove oopsie * address feedback * make log more useful * make filter more flat * Add some more checks to filter targets add pvp checks to isenemy * fix typing * Add filter target to TacArc and update filter target * fix double declaration * Some debugging logs * Update TacArc reading * make log clearer * logs * Update TacArcBehavior.cpp * banana * fix max targets * remove extreanous parenthesesuuesdsds * make behavior slot use a real type --------- Co-authored-by: David Markowitz <EmosewaMC@gmail.com>
This commit is contained in:
@@ -20,134 +20,114 @@ void AreaOfEffectBehavior::Handle(BehaviorContext* context, RakNet::BitStream* b
|
||||
return;
|
||||
}
|
||||
|
||||
if (targetCount > this->m_maxTargets) {
|
||||
if (this->m_useTargetPosition && branch.target == LWOOBJID_EMPTY) return;
|
||||
|
||||
if (targetCount == 0){
|
||||
PlayFx(u"miss", context->originator);
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<LWOOBJID> targets;
|
||||
if (targetCount > this->m_maxTargets) {
|
||||
Game::logger->Log("AreaOfEffectBehavior", "Serialized size is greater than max targets! Size: %i, Max: %i", targetCount, this->m_maxTargets);
|
||||
return;
|
||||
}
|
||||
|
||||
auto caster = context->caster;
|
||||
if (this->m_useTargetAsCaster) context->caster = branch.target;
|
||||
|
||||
std::vector<LWOOBJID> targets;
|
||||
targets.reserve(targetCount);
|
||||
|
||||
for (auto i = 0u; i < targetCount; ++i) {
|
||||
LWOOBJID target{};
|
||||
|
||||
if (!bitStream->Read(target)) {
|
||||
Game::logger->Log("AreaOfEffectBehavior", "failed to read in target %i from bitStream, aborting target Handle!", i);
|
||||
return;
|
||||
};
|
||||
|
||||
targets.push_back(target);
|
||||
}
|
||||
|
||||
for (auto target : targets) {
|
||||
branch.target = target;
|
||||
|
||||
this->m_action->Handle(context, bitStream, branch);
|
||||
}
|
||||
context->caster = caster;
|
||||
PlayFx(u"cast", context->originator);
|
||||
}
|
||||
|
||||
void AreaOfEffectBehavior::Calculate(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) {
|
||||
auto* self = Game::entityManager->GetEntity(context->caster);
|
||||
if (self == nullptr) {
|
||||
Game::logger->Log("AreaOfEffectBehavior", "Invalid self for (%llu)!", context->originator);
|
||||
auto* caster = Game::entityManager->GetEntity(context->caster);
|
||||
if (!caster) return;
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
auto reference = branch.isProjectile ? branch.referencePosition : self->GetPosition();
|
||||
reference += this->m_offset;
|
||||
|
||||
std::vector<Entity*> targets;
|
||||
|
||||
auto* presetTarget = Game::entityManager->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;
|
||||
}
|
||||
|
||||
// Gets all of the valid targets, passing in if should target enemies and friends
|
||||
for (auto validTarget : context->GetValidTargets(m_ignoreFaction, includeFaction, m_TargetSelf == 1, m_targetEnemy == 1, m_targetFriend == 1)) {
|
||||
auto* entity = Game::entityManager->GetEntity(validTarget);
|
||||
|
||||
if (entity == nullptr) {
|
||||
Game::logger->Log("AreaOfEffectBehavior", "Invalid target (%llu) for (%llu)!", 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::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);
|
||||
|
||||
// sort by distance
|
||||
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);
|
||||
const auto aDistance = NiPoint3::Distance(a->GetPosition(), reference);
|
||||
const auto bDistance = NiPoint3::Distance(b->GetPosition(), reference);
|
||||
return aDistance < bDistance;
|
||||
}
|
||||
);
|
||||
|
||||
return aDistance > bDistance;
|
||||
});
|
||||
// resize if we have more than max targets allows
|
||||
if (targets.size() > this->m_maxTargets) targets.resize(this->m_maxTargets);
|
||||
|
||||
const uint32_t size = targets.size();
|
||||
bitStream->Write<uint32_t>(targets.size());
|
||||
|
||||
bitStream->Write(size);
|
||||
|
||||
if (size == 0) {
|
||||
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) {
|
||||
bitStream->Write(target->GetObjectID());
|
||||
}
|
||||
|
||||
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);
|
||||
// then cast all the actions
|
||||
for (auto* target : targets) {
|
||||
branch.target = target->GetObjectID();
|
||||
this->m_action->Calculate(context, bitStream, branch);
|
||||
}
|
||||
PlayFx(u"cast", context->originator);
|
||||
}
|
||||
}
|
||||
|
||||
void AreaOfEffectBehavior::Load() {
|
||||
this->m_action = GetAction("action");
|
||||
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)
|
||||
);
|
||||
|
||||
this->m_radius = GetFloat("radius");
|
||||
|
||||
this->m_maxTargets = GetInt("max targets");
|
||||
|
||||
this->m_ignoreFaction = GetInt("ignore_faction");
|
||||
|
||||
this->m_includeFaction = GetInt("include_faction");
|
||||
|
||||
this->m_TargetSelf = GetInt("target_self");
|
||||
|
||||
this->m_targetEnemy = GetInt("target_enemy");
|
||||
|
||||
this->m_targetFriend = GetInt("target_friend");
|
||||
// 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);
|
||||
}
|
||||
|
@@ -1,34 +1,26 @@
|
||||
#pragma once
|
||||
#include "Behavior.h"
|
||||
#include <forward_list>
|
||||
|
||||
class AreaOfEffectBehavior final : public Behavior
|
||||
{
|
||||
public:
|
||||
Behavior* m_action;
|
||||
|
||||
uint32_t m_maxTargets;
|
||||
|
||||
float m_radius;
|
||||
|
||||
int32_t m_ignoreFaction;
|
||||
|
||||
int32_t m_includeFaction;
|
||||
|
||||
int32_t m_TargetSelf;
|
||||
|
||||
int32_t m_targetEnemy;
|
||||
|
||||
int32_t m_targetFriend;
|
||||
|
||||
/*
|
||||
* Inherited
|
||||
*/
|
||||
explicit AreaOfEffectBehavior(const uint32_t behaviorId) : Behavior(behaviorId) {
|
||||
}
|
||||
|
||||
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;
|
||||
private:
|
||||
Behavior* m_action;
|
||||
uint32_t m_maxTargets;
|
||||
float m_radius;
|
||||
bool m_useTargetPosition;
|
||||
bool m_useTargetAsCaster;
|
||||
NiPoint3 m_offset;
|
||||
|
||||
std::forward_list<int32_t> m_ignoreFactionList {};
|
||||
std::forward_list<int32_t> m_includeFactionList {};
|
||||
bool m_targetSelf;
|
||||
bool m_targetEnemy;
|
||||
bool m_targetFriend;
|
||||
bool m_targetTeam;
|
||||
};
|
||||
|
@@ -175,7 +175,7 @@ Behavior* Behavior::CreateBehavior(const uint32_t behaviorId) {
|
||||
case BehaviorTemplates::BEHAVIOR_SPEED:
|
||||
behavior = new SpeedBehavior(behaviorId);
|
||||
break;
|
||||
case BehaviorTemplates::BEHAVIOR_DARK_INSPIRATION:
|
||||
case BehaviorTemplates::BEHAVIOR_DARK_INSPIRATION:
|
||||
behavior = new DarkInspirationBehavior(behaviorId);
|
||||
break;
|
||||
case BehaviorTemplates::BEHAVIOR_LOOT_BUFF:
|
||||
|
@@ -15,6 +15,7 @@
|
||||
#include "PhantomPhysicsComponent.h"
|
||||
#include "RebuildComponent.h"
|
||||
#include "eReplicaComponentType.h"
|
||||
#include "TeamManager.h"
|
||||
#include "eConnectionType.h"
|
||||
|
||||
BehaviorSyncEntry::BehaviorSyncEntry() {
|
||||
@@ -307,46 +308,123 @@ void BehaviorContext::Reset() {
|
||||
this->scheduledUpdates.clear();
|
||||
}
|
||||
|
||||
std::vector<LWOOBJID> BehaviorContext::GetValidTargets(int32_t ignoreFaction, int32_t includeFaction, bool targetSelf, bool targetEnemy, bool targetFriend) const {
|
||||
auto* entity = Game::entityManager->GetEntity(this->caster);
|
||||
void BehaviorContext::FilterTargets(std::vector<Entity*>& targets, std::forward_list<int32_t>& ignoreFactionList, std::forward_list<int32_t>& includeFactionList, bool targetSelf, bool targetEnemy, bool targetFriend, bool targetTeam) const {
|
||||
|
||||
std::vector<LWOOBJID> targets;
|
||||
|
||||
if (entity == nullptr) {
|
||||
Game::logger->Log("BehaviorContext", "Invalid entity for (%llu)!", this->originator);
|
||||
|
||||
return targets;
|
||||
// if we aren't targeting anything, then clear the targets vector
|
||||
if (!targetSelf && !targetEnemy && !targetFriend && !targetTeam && ignoreFactionList.empty() && includeFactionList.empty()) {
|
||||
targets.clear();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!ignoreFaction && !includeFaction) {
|
||||
for (auto entry : entity->GetTargetsInPhantom()) {
|
||||
auto* instance = Game::entityManager->GetEntity(entry);
|
||||
|
||||
if (instance == nullptr) {
|
||||
continue;
|
||||
}
|
||||
|
||||
targets.push_back(entry);
|
||||
}
|
||||
// if the caster is not there, return empty targets list
|
||||
auto* caster = Game::entityManager->GetEntity(this->caster);
|
||||
if (!caster) {
|
||||
Game::logger->LogDebug("BehaviorContext", "Invalid caster for (%llu)!", this->originator);
|
||||
targets.clear();
|
||||
return;
|
||||
}
|
||||
|
||||
if (ignoreFaction || includeFaction || (!entity->HasComponent(eReplicaComponentType::PHANTOM_PHYSICS) && targets.empty())) {
|
||||
DestroyableComponent* destroyableComponent;
|
||||
if (!entity->TryGetComponent(eReplicaComponentType::DESTROYABLE, destroyableComponent)) {
|
||||
return targets;
|
||||
auto index = targets.begin();
|
||||
while (index != targets.end()) {
|
||||
auto candidate = *index;
|
||||
|
||||
// make sure we don't have a nullptr
|
||||
if (!candidate) {
|
||||
index = targets.erase(index);
|
||||
continue;
|
||||
}
|
||||
|
||||
auto entities = Game::entityManager->GetEntitiesByComponent(eReplicaComponentType::CONTROLLABLE_PHYSICS);
|
||||
for (auto* candidate : entities) {
|
||||
const auto id = candidate->GetObjectID();
|
||||
// handle targeting the caster
|
||||
if (candidate == caster){
|
||||
// if we aren't targeting self, erase, otherise increment and continue
|
||||
if (!targetSelf) index = targets.erase(index);
|
||||
else index++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((id != entity->GetObjectID() || targetSelf) && destroyableComponent->CheckValidity(id, ignoreFaction || includeFaction, targetEnemy, targetFriend)) {
|
||||
targets.push_back(id);
|
||||
// make sure that the entity is targetable
|
||||
if (!CheckTargetingRequirements(candidate)) {
|
||||
index = targets.erase(index);
|
||||
continue;
|
||||
}
|
||||
|
||||
// get factions to check against
|
||||
// CheckTargetingRequirements checks for a destroyable component
|
||||
// but we check again because bounds check are necessary
|
||||
auto candidateDestroyableComponent = candidate->GetComponent<DestroyableComponent>();
|
||||
if (!candidateDestroyableComponent) {
|
||||
index = targets.erase(index);
|
||||
continue;
|
||||
}
|
||||
|
||||
// if they are dead, then earse and continue
|
||||
if (candidateDestroyableComponent->GetIsDead()){
|
||||
index = targets.erase(index);
|
||||
continue;
|
||||
}
|
||||
|
||||
// if their faction is explicitly included, increment and continue
|
||||
auto candidateFactions = candidateDestroyableComponent->GetFactionIDs();
|
||||
if (CheckFactionList(includeFactionList, candidateFactions)){
|
||||
index++;
|
||||
continue;
|
||||
}
|
||||
|
||||
// check if they are a team member
|
||||
if (targetTeam){
|
||||
auto* team = TeamManager::Instance()->GetTeam(this->caster);
|
||||
if (team){
|
||||
// if we find a team member keep it and continue to skip enemy checks
|
||||
if(std::find(team->members.begin(), team->members.end(), candidate->GetObjectID()) != team->members.end()){
|
||||
index++;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return targets;
|
||||
// if the caster doesn't have a destroyable component, return an empty targets list
|
||||
auto* casterDestroyableComponent = caster->GetComponent<DestroyableComponent>();
|
||||
if (!casterDestroyableComponent) {
|
||||
targets.clear();
|
||||
return;
|
||||
}
|
||||
|
||||
// if we arent targeting a friend, and they are a friend OR
|
||||
// if we are not targeting enemies and they are an enemy OR.
|
||||
// if we are ignoring their faction is explicitly ignored
|
||||
// erase and continue
|
||||
auto isEnemy = casterDestroyableComponent->IsEnemy(candidate);
|
||||
if ((!targetFriend && !isEnemy) ||
|
||||
(!targetEnemy && isEnemy) ||
|
||||
CheckFactionList(ignoreFactionList, candidateFactions)) {
|
||||
index = targets.erase(index);
|
||||
continue;
|
||||
}
|
||||
|
||||
index++;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// some basic checks as well as the check that matters for this: if the quickbuild is complete
|
||||
bool BehaviorContext::CheckTargetingRequirements(const Entity* target) const {
|
||||
// if the target is a nullptr, then it's not valid
|
||||
if (!target) return false;
|
||||
|
||||
// ignore quickbuilds that aren't completed
|
||||
auto* targetQuickbuildComponent = target->GetComponent<RebuildComponent>();
|
||||
if (targetQuickbuildComponent && targetQuickbuildComponent->GetState() != eRebuildState::COMPLETED) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// returns true if any of the object factions are in the faction list
|
||||
bool BehaviorContext::CheckFactionList(std::forward_list<int32_t>& factionList, std::vector<int32_t>& objectsFactions) const {
|
||||
if (factionList.empty() || objectsFactions.empty()) return false;
|
||||
for (auto faction : factionList){
|
||||
if(std::find(objectsFactions.begin(), objectsFactions.end(), faction) != objectsFactions.end()) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
@@ -6,6 +6,7 @@
|
||||
#include "GameMessages.h"
|
||||
|
||||
#include <vector>
|
||||
#include <forward_list>
|
||||
|
||||
class Behavior;
|
||||
|
||||
@@ -106,7 +107,11 @@ struct BehaviorContext
|
||||
|
||||
void Reset();
|
||||
|
||||
std::vector<LWOOBJID> GetValidTargets(int32_t ignoreFaction = 0, int32_t includeFaction = 0, const bool targetSelf = false, const bool targetEnemy = true, const bool targetFriend = false) const;
|
||||
void FilterTargets(std::vector<Entity*>& targetsReference, std::forward_list<int32_t>& ignoreFaction, std::forward_list<int32_t>& includeFaction, const bool targetSelf = false, const bool targetEnemy = true, const bool targetFriend = false, const bool targetTeam = false) const;
|
||||
|
||||
bool CheckTargetingRequirements(const Entity* target) const;
|
||||
|
||||
bool CheckFactionList(std::forward_list<int32_t>& factionList, std::vector<int32_t>& objectsFactions) const;
|
||||
|
||||
explicit BehaviorContext(LWOOBJID originator, bool calculation = false);
|
||||
|
||||
|
@@ -2,9 +2,9 @@
|
||||
|
||||
#ifndef BEHAVIORSLOT_H
|
||||
#define BEHAVIORSLOT_H
|
||||
#include <cstdint>
|
||||
|
||||
enum class BehaviorSlot
|
||||
{
|
||||
enum class BehaviorSlot : int32_t {
|
||||
Invalid = -1,
|
||||
Primary,
|
||||
Offhand,
|
||||
|
@@ -12,16 +12,24 @@
|
||||
#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);
|
||||
std::vector<Entity*> targets = {};
|
||||
|
||||
return;
|
||||
if (this->m_usePickedTarget && branch.target != LWOOBJID_EMPTY) {
|
||||
auto target = Game::entityManager->GetEntity(branch.target);
|
||||
if (!target) Game::logger->Log("TacArcBehavior", "target %llu is null", branch.target);
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool hit = false;
|
||||
|
||||
if (!bitStream->Read(hit)) {
|
||||
Game::logger->Log("TacArcBehavior", "Unable to read hit from bitStream, aborting Handle! %i", bitStream->GetNumberOfUnreadBits());
|
||||
bool hasTargets = false;
|
||||
if (!bitStream->Read(hasTargets)) {
|
||||
Game::logger->Log("TacArcBehavior", "Unable to read hasTargets from bitStream, aborting Handle! %i", bitStream->GetNumberOfUnreadBits());
|
||||
return;
|
||||
};
|
||||
|
||||
@@ -35,26 +43,23 @@ void TacArcBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bitStre
|
||||
|
||||
if (blocked) {
|
||||
this->m_blockedAction->Handle(context, bitStream, branch);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (hit) {
|
||||
if (hasTargets) {
|
||||
uint32_t count = 0;
|
||||
|
||||
if (!bitStream->Read(count)) {
|
||||
Game::logger->Log("TacArcBehavior", "Unable to read count from bitStream, aborting Handle! %i", bitStream->GetNumberOfUnreadBits());
|
||||
return;
|
||||
};
|
||||
|
||||
if (count > m_maxTargets && m_maxTargets > 0) {
|
||||
count = m_maxTargets;
|
||||
if (count > m_maxTargets) {
|
||||
Game::logger->Log("TacArcBehavior", "Bitstream has too many targets Max:%i Recv:%i", this->m_maxTargets, count);
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<LWOOBJID> targets;
|
||||
|
||||
for (auto i = 0u; i < count; ++i) {
|
||||
for (auto i = 0u; i < count; i++) {
|
||||
LWOOBJID id{};
|
||||
|
||||
if (!bitStream->Read(id)) {
|
||||
@@ -62,17 +67,19 @@ void TacArcBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bitStre
|
||||
return;
|
||||
};
|
||||
|
||||
targets.push_back(id);
|
||||
if (id != LWOOBJID_EMPTY) {
|
||||
auto* canidate = Game::entityManager->GetEntity(id);
|
||||
if (canidate) targets.push_back(canidate);
|
||||
} else {
|
||||
Game::logger->Log("TacArcBehavior", "Bitstream has LWOOBJID_EMPTY as a target!");
|
||||
}
|
||||
}
|
||||
|
||||
for (auto target : targets) {
|
||||
branch.target = target;
|
||||
|
||||
branch.target = target->GetObjectID();
|
||||
this->m_action->Handle(context, bitStream, branch);
|
||||
}
|
||||
} else {
|
||||
this->m_missAction->Handle(context, bitStream, branch);
|
||||
}
|
||||
} else this->m_missAction->Handle(context, bitStream, branch);
|
||||
}
|
||||
|
||||
void TacArcBehavior::Calculate(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) {
|
||||
@@ -82,23 +89,15 @@ void TacArcBehavior::Calculate(BehaviorContext* context, RakNet::BitStream* bitS
|
||||
return;
|
||||
}
|
||||
|
||||
const auto* destroyableComponent = self->GetComponent<DestroyableComponent>();
|
||||
|
||||
if ((this->m_usePickedTarget || context->clientInitalized) && branch.target > 0) {
|
||||
const auto* target = Game::entityManager->GetEntity(branch.target);
|
||||
|
||||
if (target == nullptr) {
|
||||
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);
|
||||
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>();
|
||||
@@ -107,50 +106,25 @@ void TacArcBehavior::Calculate(BehaviorContext* context, RakNet::BitStream* bitS
|
||||
|
||||
auto reference = self->GetPosition(); //+ m_offset;
|
||||
|
||||
std::vector<Entity*> targets;
|
||||
targets.clear();
|
||||
|
||||
std::vector<LWOOBJID> validTargets;
|
||||
std::vector<Entity*> validTargets = Game::entityManager->GetEntitiesByProximity(reference, this->m_maxRange);
|
||||
|
||||
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 = Game::entityManager->GetEntity(contextTarget);
|
||||
|
||||
if (m_targetEnemy && destroyableComponent->IsEnemy(targetEntity)
|
||||
|| m_targetFriend && destroyableComponent->IsFriend(targetEntity)) {
|
||||
validTargets.push_back(contextTarget);
|
||||
}
|
||||
} else {
|
||||
validTargets.push_back(contextTarget);
|
||||
}
|
||||
}
|
||||
// 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);
|
||||
|
||||
for (auto validTarget : validTargets) {
|
||||
if (targets.size() >= this->m_maxTargets) {
|
||||
break;
|
||||
}
|
||||
|
||||
auto* entity = Game::entityManager->GetEntity(validTarget);
|
||||
|
||||
if (entity == nullptr) {
|
||||
Game::logger->Log("TacArcBehavior", "Invalid target (%llu) for (%llu)!", validTarget, context->originator);
|
||||
|
||||
if (std::find(targets.begin(), targets.end(), validTarget) != targets.end()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (std::find(targets.begin(), targets.end(), entity) != targets.end()) {
|
||||
continue;
|
||||
}
|
||||
if (validTarget->GetIsDead()) continue;
|
||||
|
||||
if (entity->GetIsDead()) continue;
|
||||
|
||||
const auto otherPosition = entity->GetPosition();
|
||||
const auto otherPosition = validTarget->GetPosition();
|
||||
|
||||
const auto heightDifference = std::abs(otherPosition.y - casterPosition.y);
|
||||
|
||||
@@ -180,8 +154,8 @@ void TacArcBehavior::Calculate(BehaviorContext* context, RakNet::BitStream* bitS
|
||||
|
||||
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);
|
||||
if (distance >= this->m_minRange && this->m_maxRange >= distance && degreeAngle <= 2 * this->m_angle) {
|
||||
targets.push_back(validTarget);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -228,43 +202,48 @@ void TacArcBehavior::Calculate(BehaviorContext* context, RakNet::BitStream* bitS
|
||||
}
|
||||
|
||||
void TacArcBehavior::Load() {
|
||||
this->m_usePickedTarget = GetBoolean("use_picked_target");
|
||||
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);
|
||||
this->m_upperBound = GetFloat("upper_bound", 4.4f);
|
||||
this->m_lowerBound = GetFloat("lower_bound", 0.4f);
|
||||
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);
|
||||
|
||||
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_maxTargets = GetInt("max targets", 100);
|
||||
if (this->m_maxTargets == 0) this->m_maxTargets = 100;
|
||||
|
||||
this->m_maxDistance = GetFloat("max range");
|
||||
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);
|
||||
|
||||
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")
|
||||
};
|
||||
// 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);
|
||||
}
|
||||
|
@@ -2,56 +2,42 @@
|
||||
#include "Behavior.h"
|
||||
#include "dCommonVars.h"
|
||||
#include "NiPoint3.h"
|
||||
#include <forward_list>
|
||||
|
||||
class TacArcBehavior final : public Behavior
|
||||
{
|
||||
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) {
|
||||
}
|
||||
|
||||
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;
|
||||
private:
|
||||
float m_maxRange;
|
||||
float m_height;
|
||||
float m_distanceWeight;
|
||||
float m_angleWeight;
|
||||
float m_angle;
|
||||
float m_minRange;
|
||||
NiPoint3 m_offset;
|
||||
uint32_t m_method;
|
||||
float m_upperBound;
|
||||
float m_lowerBound;
|
||||
bool m_usePickedTarget;
|
||||
bool m_useTargetPostion;
|
||||
bool m_checkEnv;
|
||||
bool m_useAttackPriority;
|
||||
Behavior* m_action;
|
||||
Behavior* m_missAction;
|
||||
Behavior* m_blockedAction;
|
||||
uint32_t m_maxTargets;
|
||||
float m_farHeight;
|
||||
float m_farWidth;
|
||||
float m_nearHeight;
|
||||
float m_nearWidth;
|
||||
|
||||
std::forward_list<int32_t> m_ignoreFactionList {};
|
||||
std::forward_list<int32_t> m_includeFactionList {};
|
||||
bool m_targetSelf;
|
||||
bool m_targetEnemy;
|
||||
bool m_targetFriend;
|
||||
bool m_targetTeam;
|
||||
};
|
||||
|
Reference in New Issue
Block a user