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:
Aaron Kimbrell
2023-10-09 15:18:51 -05:00
committed by GitHub
parent 471d65707c
commit d8ac148cee
18 changed files with 474 additions and 366 deletions

View File

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