Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: re-write AOE, add FilterTargets, Update TacArc Reading #1035

Merged
merged 24 commits into from
Oct 9, 2023
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
fdeb2e6
Re-write AOE behavior for new filter targets
aronwk-aaron Mar 30, 2023
8a64d2b
Get all entities by detroyable
aronwk-aaron Mar 30, 2023
892d5d3
Re-work filter targets to be 100% live accurate
aronwk-aaron Mar 31, 2023
d6bf2e0
remove debuging longs and remove oopsie
aronwk-aaron Apr 1, 2023
4988015
address feedback
aronwk-aaron Apr 1, 2023
68abb74
make log more useful
aronwk-aaron Apr 1, 2023
83d05f1
make filter more flat
aronwk-aaron Apr 3, 2023
d331702
Merge branch 'main' into make-filterTarget-better
aronwk-aaron Apr 5, 2023
33bbb48
Add some more checks to filter targets
aronwk-aaron Apr 7, 2023
644e169
Merge branch 'main' into make-filterTarget-better
aronwk-aaron May 13, 2023
9a401cc
fix typing
aronwk-aaron May 13, 2023
35a1747
Add filter target to TacArc and update filter target
aronwk-aaron May 14, 2023
98787c8
fix double declaration
aronwk-aaron May 14, 2023
910c18e
Some debugging logs
aronwk-aaron May 14, 2023
319b406
Update TacArc reading
aronwk-aaron May 15, 2023
3fdcd8f
make log clearer
aronwk-aaron May 15, 2023
269ce14
logs
EmosewaMC May 16, 2023
5917404
Update TacArcBehavior.cpp
EmosewaMC May 16, 2023
e0327c6
banana
EmosewaMC May 16, 2023
2053676
Merge branch 'main' into make-filterTarget-better
aronwk-aaron Aug 23, 2023
4f16f1e
fix max targets
aronwk-aaron Aug 23, 2023
678eece
remove extreanous parenthesesuuesdsds
aronwk-aaron Aug 23, 2023
8273ac5
make behavior slot use a real type
aronwk-aaron Oct 9, 2023
b93bb75
Merge branch 'main' into make-filterTarget-better
aronwk-aaron Oct 9, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
139 changes: 56 additions & 83 deletions dGame/dBehaviors/AreaOfEffectBehavior.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,133 +21,106 @@ void AreaOfEffectBehavior::Handle(BehaviorContext* context, RakNet::BitStream* b
}

if (targetCount > this->m_maxTargets) {
Game::logger->Log("AreaOfEffectBehavior::Handle", "More targets than allowed! Likely a cheating attempt. Got: %i, Max: %i", targetCount, this->m_maxTargets);
return;
}

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);
}
}

void AreaOfEffectBehavior::Calculate(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) {
auto* self = EntityManager::Instance()->GetEntity(context->caster);
if (self == nullptr) {
Game::logger->Log("AreaOfEffectBehavior", "Invalid self for (%llu)!", context->originator);

auto* caster = EntityManager::Instance()->GetEntity(context->caster);
if (!caster) {
Game::logger->Log("AreaOfEffectBehavior::Calculate", "There is no caster to be found!");
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);
}
// determine the position we are casting the AOE from
auto reference = branch.isProjectile ? branch.referencePosition : caster->GetPosition();
if (m_UseTargetPosition && branch.target) {
auto branchTarget = EntityManager::Instance()->GetEntity(branch.target);
aronwk-aaron marked this conversation as resolved.
Show resolved Hide resolved
reference = branchTarget->GetPosition();
}

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 = EntityManager::Instance()->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;
}
reference += this->m_offset;

if (destroyableComponent->HasFaction(m_ignoreFaction)) {
continue;
}
std::vector<Entity*> allTargets {};
std::vector<Entity*> validTargets {};
// Gets all of the valid validTargets, passing in if should target enemies and friends
allTargets = context->FilterTargets(this->m_ignoreFactionList, this->m_includeFactionList, this->m_targetSelf, this->m_targetEnemy, this->m_targetFriend, this->m_targetTeam);

const auto distance = Vector3::DistanceSquared(reference, entity->GetPosition());
// filter based on the radius
for (auto* candidate : allTargets) if (NiPoint3::Distance(reference, candidate->GetPosition()) <= this->m_radius) validTargets.push_back(candidate);

if (this->m_radius * this->m_radius >= distance && (this->m_maxTargets == 0 || targets.size() < this->m_maxTargets)) {
targets.push_back(entity);
// sort by distance
std::sort(validTargets.begin(), validTargets.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;
}
}

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();
// resize if we have more than max validTargets allows
if (validTargets.size() > this->m_maxTargets) validTargets.resize(this->m_maxTargets);

bitStream->Write(size);
bitStream->Write<uint32_t>(validTargets.size());

if (size == 0) {
return;
}

context->foundTarget = true;
if (validTargets.size() > 0) context->foundTarget = true;

for (auto* target : targets) {
bitStream->Write(target->GetObjectID());

PlayFx(u"cast", context->originator, target->GetObjectID());
// write all the targets to the bitstream
for (auto* validTarget : validTargets) {
bitStream->Write(validTarget->GetObjectID());
}

for (auto* target : targets) {
branch.target = target->GetObjectID();

// then case all the actions
for (auto* validTarget : validTargets) {
bitStream->Write(validTarget->GetObjectID());
branch.target = validTarget->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");
this->m_radius = GetFloat("radius");
this->m_maxTargets = GetInt("max targets", 100);
if (this->m_maxTargets == 0) this->m_maxTargets = 100;
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);

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");
}
31 changes: 13 additions & 18 deletions dGame/dBehaviors/AreaOfEffectBehavior.h
Original file line number Diff line number Diff line change
@@ -1,30 +1,25 @@
#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) {
}
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;

explicit AreaOfEffectBehavior(const uint32_t behaviorId) : Behavior(behaviorId) {}

void Handle(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) override;

Expand Down
83 changes: 56 additions & 27 deletions dGame/dBehaviors/BehaviorContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#include "PhantomPhysicsComponent.h"
#include "RebuildComponent.h"
#include "eReplicaComponentType.h"
#include "TeamManager.h"

BehaviorSyncEntry::BehaviorSyncEntry() {
}
Expand Down Expand Up @@ -289,46 +290,74 @@ 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 = EntityManager::Instance()->GetEntity(this->caster);
std::vector<Entity*> BehaviorContext::FilterTargets(std::forward_list<int32_t> ignoreFactionList, std::forward_list<int32_t> includeFactionList, bool targetSelf, bool targetEnemy, bool targetFriend, bool targetTeam) const {
std::vector<Entity*> targets = {};

std::vector<LWOOBJID> targets;
// if we aren't targeting anything, then return empty targets list
if (!targetSelf && !targetEnemy && !targetFriend && !targetTeam && ignoreFactionList.empty() && includeFactionList.empty()) return targets;

if (entity == nullptr) {
Game::logger->Log("BehaviorContext", "Invalid entity for (%llu)!", this->originator);
auto* caster = EntityManager::Instance()->GetEntity(this->caster);

// if the caster is not there, return empty targets list
if (!caster) {
Game::logger->LogDebug("BehaviorContext", "Invalid caster for (%llu)!", this->originator);
return targets;
}

if (!ignoreFaction && !includeFaction) {
for (auto entry : entity->GetTargetsInPhantom()) {
auto* instance = EntityManager::Instance()->GetEntity(entry);

if (instance == nullptr) {
continue;
auto* casterDestroyableComponent = caster->GetComponent<DestroyableComponent>();
if (!casterDestroyableComponent) return targets;

auto candidates = EntityManager::Instance()->GetEntitiesByComponent(eReplicaComponentType::DESTROYABLE);
aronwk-aaron marked this conversation as resolved.
Show resolved Hide resolved
for (auto* candidate : candidates) {
if (!candidate) continue;
if (candidate == caster){
if (targetSelf) targets.push_back(candidate);
} else {
if (CheckTargetingRequirements(candidate)){
auto candidateDestroyableComponent = candidate->GetComponent<DestroyableComponent>();
if (!candidateDestroyableComponent) continue;
if (candidateDestroyableComponent->GetIsDead()) continue;
auto candidateFactions = candidateDestroyableComponent->GetFactionIDs();
if (!CheckFactionList(includeFactionList, candidateFactions)){
if (targetTeam){
auto* team = TeamManager::Instance()->GetTeam(this->caster);
if (team){
if(std::find(team->members.begin(), team->members.end(), candidate->GetObjectID()) != team->members.end()){
targets.push_back(candidate);
continue;
}
}
}
auto isEnemy = casterDestroyableComponent->IsEnemy(candidate);
if (!targetFriend && !isEnemy) continue;
else if(!targetEnemy && isEnemy) continue;
else if(!CheckFactionList(ignoreFactionList, candidateFactions)) targets.push_back(candidate);
} else targets.push_back(candidate);
}

targets.push_back(entry);
}
}
return targets;
}

if (ignoreFaction || includeFaction || (!entity->HasComponent(eReplicaComponentType::PHANTOM_PHYSICS) && targets.empty())) {
DestroyableComponent* destroyableComponent;
if (!entity->TryGetComponent(eReplicaComponentType::DESTROYABLE, destroyableComponent)) {
return targets;
}
// 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;

auto entities = EntityManager::Instance()->GetEntitiesByComponent(eReplicaComponentType::CONTROLLABLE_PHYSICS);
for (auto* candidate : entities) {
const auto id = candidate->GetObjectID();
// only target quickbuilds in the are completed
aronwk-aaron marked this conversation as resolved.
Show resolved Hide resolved
auto* targetQuickbuild = target->GetComponent<RebuildComponent>();
if (targetQuickbuild && targetQuickbuild->GetState() != REBUILD_COMPLETED) return false;

if ((id != entity->GetObjectID() || targetSelf) && destroyableComponent->CheckValidity(id, ignoreFaction || includeFaction, targetEnemy, targetFriend)) {
targets.push_back(id);
}
}
}
return true;
}

return targets;
// 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;
}


Expand Down
7 changes: 6 additions & 1 deletion dGame/dBehaviors/BehaviorContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include "GameMessages.h"

#include <vector>
#include <forward_list>

class Behavior;

Expand Down Expand Up @@ -104,7 +105,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;
std::vector<Entity*> FilterTargets(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;
aronwk-aaron marked this conversation as resolved.
Show resolved Hide resolved

explicit BehaviorContext(LWOOBJID originator, bool calculation = false);

Expand Down
Loading