metaforce/Runtime/MP1/World/CWarWasp.cpp

1232 lines
47 KiB
C++

#include "CWarWasp.hpp"
#include "Character/CCharLayoutInfo.hpp"
#include "TCastTo.hpp"
#include "World/CPatternedInfo.hpp"
#include "GameGlobalObjects.hpp"
#include "CSimplePool.hpp"
#include "World/CPlayer.hpp"
#include "World/CTeamAiMgr.hpp"
#include "World/CWorld.hpp"
#include "World/CScriptWaypoint.hpp"
#include "Weapon/CGameProjectile.hpp"
namespace urde::MP1 {
CWarWasp::CWarWasp(TUniqueId uid, std::string_view name, const CEntityInfo& info, const zeus::CTransform& xf,
CModelData&& mData, const CPatternedInfo& pInfo, CPatterned::EFlavorType flavor,
CPatterned::EColliderType collider, const CDamageInfo& dInfo1, const CActorParameters& actorParms,
CAssetId projectileWeapon, const CDamageInfo& projectileDamage, CAssetId projectileVisorParticle,
u32 projecileVisorSfx)
: CPatterned(ECharacter::WarWasp, uid, name, flavor, info, xf, std::move(mData), pInfo, EMovementType::Flyer, collider,
EBodyType::Flyer, actorParms, EKnockBackVariant::Small)
, x570_cSphere(zeus::CSphere({0.f, 0.f, 1.8f}, 1.f), x68_material)
, x590_pfSearch(nullptr, 0x3, pInfo.GetPathfindingIndex(), 1.f, 1.f)
, x684_(dInfo1)
, x6d4_projectileInfo(projectileWeapon, projectileDamage)
, x72c_projectileVisorSfx(CSfxManager::TranslateSFXID(projecileVisorSfx)) {
x72e_24_jumpBackRepeat = true;
x72e_26_initiallyInactive = !pInfo.GetActive();
x6d4_projectileInfo.Token().Lock();
UpdateTouchBounds();
SetCoefficientOfRestitutionModifier(0.1f);
if (projectileVisorParticle.IsValid())
x71c_projectileVisorParticle = g_SimplePool->GetObj(SObjectTag{FOURCC('PART'), projectileVisorParticle});
x328_29_noPatternShagging = true;
x460_knockBackController.SetAnimationStateRange(EKnockBackAnimationState::KnockBack,
EKnockBackAnimationState::KnockBack);
}
void CWarWasp::Accept(IVisitor& visitor) { visitor.Visit(this); }
void CWarWasp::SwarmAdd(CStateManager& mgr) {
if (x674_aiMgr != kInvalidUniqueId) {
if (TCastToPtr<CTeamAiMgr> aimgr = mgr.ObjectById(x674_aiMgr)) {
CTeamAiRole::ETeamAiRole role = x3fc_flavor == EFlavorType::Two ?
CTeamAiRole::ETeamAiRole::Projectile : CTeamAiRole::ETeamAiRole::Melee;
if (!aimgr->IsPartOfTeam(GetUniqueId())) {
aimgr->AssignTeamAiRole(*this, role, CTeamAiRole::ETeamAiRole::Invalid, CTeamAiRole::ETeamAiRole::Invalid);
}
}
}
}
void CWarWasp::SwarmRemove(CStateManager& mgr) {
if (x674_aiMgr != kInvalidUniqueId) {
if (TCastToPtr<CTeamAiMgr> aimgr = mgr.ObjectById(x674_aiMgr)) {
if (aimgr->IsPartOfTeam(GetUniqueId())) {
aimgr->RemoveTeamAiRole(GetUniqueId());
}
}
}
}
void CWarWasp::ApplyDamage(CStateManager& mgr) {
if (x72e_25_canApplyDamage && x450_bodyController->GetCurrentStateId() == pas::EAnimationState::MeleeAttack) {
if (mgr.GetPlayer().GetBoundingBox().pointInside(
GetTransform() * (GetLocatorTransform("LCTR_WARTAIL"sv).origin * x64_modelData->GetScale()))) {
mgr.ApplyDamage(GetUniqueId(), mgr.GetPlayer().GetUniqueId(), GetUniqueId(), GetContactDamage(),
CMaterialFilter::MakeIncludeExclude({EMaterialTypes::Solid}, {}), zeus::CVector3f::skZero);
x72e_25_canApplyDamage = false;
}
}
}
void CWarWasp::Think(float dt, CStateManager& mgr) {
if (!GetActive())
return;
if (x700_attackRemTime > 0.f) {
float rate = 1.f;
if (mgr.GetPlayer().GetMorphballTransitionState() == CPlayer::EPlayerMorphBallState::Unmorphed)
rate = 1.f - zeus::CVector2f::getAngleDiff(mgr.GetPlayer().GetTransform().basis[1].toVec2f(),
GetTranslation().toVec2f() - mgr.GetPlayer().GetTranslation().toVec2f()) / M_PIF * 0.666f;
x700_attackRemTime -= rate * dt;
}
ApplyDamage(mgr);
CPatterned::Think(dt, mgr);
}
void CWarWasp::SetUpCircleBurstWaypoint(CStateManager& mgr) {
for (const auto& conn : GetConnectionList()) {
if (conn.x0_state == EScriptObjectState::CloseIn && conn.x4_msg == EScriptObjectMessage::Follow) {
if (TCastToConstPtr<CScriptWaypoint> wp = mgr.GetObjectById(mgr.GetIdForScript(conn.x8_objId))) {
x6b0_circleBurstPos = wp->GetTranslation();
x6bc_circleBurstDir = wp->GetTransform().basis[1];
x6c8_circleBurstRight = wp->GetTransform().basis[0];
break;
}
}
}
}
void CWarWasp::AcceptScriptMsg(EScriptObjectMessage msg, TUniqueId sender, CStateManager& mgr) {
CPatterned::AcceptScriptMsg(msg, sender, mgr);
switch (msg) {
case EScriptObjectMessage::Deleted:
case EScriptObjectMessage::Deactivate:
SwarmRemove(mgr);
break;
case EScriptObjectMessage::InitializedInArea:
if (x674_aiMgr == kInvalidUniqueId)
x674_aiMgr = CTeamAiMgr::GetTeamAiMgr(*this, mgr);
x590_pfSearch.SetArea(mgr.GetWorld()->GetAreaAlways(GetAreaIdAlways())->GetPostConstructed()->x10bc_pathArea);
if (x6b0_circleBurstPos.isZero())
SetUpCircleBurstWaypoint(mgr);
break;
default:
break;
}
}
std::experimental::optional<zeus::CAABox> CWarWasp::GetTouchBounds() const {
return {x570_cSphere.CalculateAABox(GetTransform())};
}
zeus::CVector3f CWarWasp::GetProjectileAimPos(CStateManager& mgr, float zBias) {
zeus::CVector3f ret = mgr.GetPlayer().GetAimPosition(mgr, 0.f);
if (mgr.GetPlayer().GetMorphballTransitionState() != CPlayer::EPlayerMorphBallState::Morphed)
ret += zeus::CVector3f(0.f, 0.f, zBias);
return ret;
}
void CWarWasp::DoUserAnimEvent(CStateManager& mgr, const CInt32POINode& node, EUserEventType type, float dt) {
bool handled = false;
switch (type) {
case EUserEventType::Projectile: {
zeus::CTransform xf = GetLctrTransform(node.GetLocatorName());
zeus::CVector3f aimPos = GetProjectileAimPos(mgr, -0.07f);
if (mgr.GetPlayer().GetMorphballTransitionState() != CPlayer::EPlayerMorphBallState::Morphed) {
zeus::CVector3f delta = aimPos - xf.origin;
if (delta.canBeNormalized()) {
rstl::reserved_vector<TUniqueId, 1024> nearList;
TUniqueId bestId = kInvalidUniqueId;
CRayCastResult res = mgr.RayWorldIntersection(bestId, xf.origin, delta.normalized(), delta.magnitude(),
CMaterialFilter::MakeInclude({EMaterialTypes::Solid}), nearList);
if (res.IsValid())
aimPos = res.GetPoint();
}
}
LaunchProjectile(
zeus::lookAt(xf.origin, GetProjectileInfo()->PredictInterceptPos(xf.origin, aimPos, mgr.GetPlayer(), true, dt)),
mgr, 4, EProjectileAttrib::None, false, {x71c_projectileVisorParticle}, x72c_projectileVisorSfx, true,
zeus::CVector3f::skOne);
handled = true;
break;
}
case EUserEventType::DeGenerate:
SendScriptMsgs(EScriptObjectState::DeactivateState, mgr, EScriptObjectMessage::None);
mgr.FreeScriptObject(GetUniqueId());
handled = true;
break;
case EUserEventType::GenerateEnd:
AddMaterial(EMaterialTypes::Character, EMaterialTypes::Solid, mgr);
handled = true;
break;
case EUserEventType::DamageOn:
x72e_25_canApplyDamage = true;
break;
case EUserEventType::DamageOff:
x72e_25_canApplyDamage = false;
break;
default:
break;
}
if (!handled)
CPatterned::DoUserAnimEvent(mgr, node, type, dt);
}
const CCollisionPrimitive* CWarWasp::GetCollisionPrimitive() const {
return &x570_cSphere;
}
void CWarWasp::Death(CStateManager& mgr, const zeus::CVector3f& direction, EScriptObjectState state) {
CPatterned::Death(mgr, direction, state);
x328_25_verticalMovement = false;
AddMaterial(EMaterialTypes::GroundCollider, mgr);
SwarmRemove(mgr);
}
bool CWarWasp::IsListening() const {
return true;
}
bool CWarWasp::Listen(const zeus::CVector3f& pos, EListenNoiseType type) {
switch (type) {
case EListenNoiseType::PlayerFire:
case EListenNoiseType::BombExplode:
case EListenNoiseType::ProjectileExplode:
if ((GetTranslation() - pos).magSquared() < x3bc_detectionRange * x3bc_detectionRange) {
x72e_31_heardNoise = true;
return true;
}
break;
default:
break;
}
return false;
}
float CWarWasp::GetCloseInZBasis(const CStateManager& mgr) const {
return mgr.GetPlayer().GetTranslation().z() + mgr.GetPlayer().GetEyeHeight() - 0.5f;
}
zeus::CVector3f CWarWasp::GetCloseInPos(const CStateManager& mgr, const zeus::CVector3f& aimPos) const {
float midRange = (x2fc_minAttackRange + x300_maxAttackRange) * 0.5f;
zeus::CVector3f ret;
if (mgr.GetPlayer().GetMorphballTransitionState() == CPlayer::EPlayerMorphBallState::Unmorphed) {
ret = mgr.GetPlayer().GetTransform().basis[1] * midRange + aimPos;
} else {
zeus::CVector3f delta = GetTranslation() - aimPos;
if (delta.canBeNormalized())
ret = delta.normalized() * midRange + aimPos;
else
ret = GetTransform().basis[1] * midRange + aimPos;
}
ret.z() = 0.5f + GetCloseInZBasis(mgr);
return ret;
}
zeus::CVector3f CWarWasp::GetOrigin(const CStateManager& mgr, const CTeamAiRole& role,
const zeus::CVector3f& aimPos) const {
if (x6b0_circleBurstPos.isZero())
return GetCloseInPos(mgr, aimPos);
else
return GetTranslation();
}
void CWarWasp::UpdateTouchBounds() {
zeus::CAABox aabb = x64_modelData->GetAnimationData()->GetBoundingBox();
x570_cSphere.SetSphereCenter(aabb.center());
SetBoundingBox(aabb.getTransformedAABox(zeus::CTransform(GetTransform().basis)));
}
void CWarWasp::Patrol(CStateManager& mgr, EStateMsg msg, float dt) {
switch (msg) {
case EStateMsg::Activate: {
float maxSpeed = x450_bodyController->GetBodyStateInfo().GetMaxSpeed();
if (maxSpeed > 0.f) {
x450_bodyController->GetCommandMgr().SetSteeringBlendMode(ESteeringBlendMode::FullSpeed);
float speedFactor =
x450_bodyController->GetBodyStateInfo().GetLocomotionSpeed(pas::ELocomotionAnim::Walk) * 0.9f / maxSpeed;
x450_bodyController->GetCommandMgr().SetSteeringSpeedRange(speedFactor, speedFactor);
}
x450_bodyController->SetLocomotionType(pas::ELocomotionType::Relaxed);
x72e_31_heardNoise = false;
break;
}
case EStateMsg::Deactivate:
x450_bodyController->GetCommandMgr().SetSteeringBlendMode(ESteeringBlendMode::Normal);
break;
default:
break;
}
CPatterned::Patrol(mgr, msg, dt);
}
void CWarWasp::SetUpPathFindBehavior(CStateManager& mgr) {
x72e_29_pathObstructed = false;
if (GetSearchPath()) {
SwarmAdd(mgr);
zeus::CVector3f pos;
if (CTeamAiRole* role = CTeamAiMgr::GetTeamAiRole(mgr, x674_aiMgr, GetUniqueId()))
pos = role->GetTeamPosition();
else
pos = GetCloseInPos(mgr, GetProjectileAimPos(mgr, -1.25f));
SetDestPos(pos);
if ((x2e0_destPos - GetTranslation()).magSquared() > 64.f ||
IsPatternObstructed(mgr, GetTranslation(), x2e0_destPos)) {
zeus::CVector3f aimPos = GetProjectileAimPos(mgr, -1.25f);
zeus::CVector3f delta = x2e0_destPos - aimPos;
if (delta.canBeNormalized()) {
zeus::CVector3f dir = delta.normalized();
CRayCastResult res = mgr.RayStaticIntersection(aimPos, dir, delta.magnitude(),
CMaterialFilter::MakeIncludeExclude({EMaterialTypes::Solid}, {EMaterialTypes::Player}));
if (res.IsValid()) {
SetDestPos(dir * res.GetT() * 0.5f + aimPos);
x72e_29_pathObstructed = true;
}
}
CPatterned::PathFind(mgr, EStateMsg::Activate, 0.f);
}
}
}
void CWarWasp::ApplyNormalSteering(CStateManager& mgr) {
zeus::CVector3f delta = mgr.GetPlayer().GetTranslation() - GetTranslation();
zeus::CVector3f teamPos;
if (CTeamAiRole* role = CTeamAiMgr::GetTeamAiRole(mgr, x674_aiMgr, GetUniqueId()))
teamPos = role->GetTeamPosition();
else
teamPos = GetProjectileAimPos(mgr, -1.25f);
zeus::CVector2f toTeamPos2d = (teamPos - GetTranslation()).toVec2f();
float toTeamPosH = teamPos.z() - GetTranslation().z();
if (toTeamPos2d.magSquared() > 1.f || std::fabs(toTeamPosH) > 2.5f) {
pas::EStepDirection stepDir = GetStepDirection(toTeamPos2d);
if (stepDir != pas::EStepDirection::Forward) {
x450_bodyController->GetCommandMgr().DeliverCmd(CBCStepCmd(stepDir, pas::EStepType::Normal));
} else {
x450_bodyController->GetCommandMgr().DeliverCmd(
CBCLocomotionCmd(x45c_steeringBehaviors.Arrival(*this, teamPos, 3.f), zeus::CVector3f::skZero, 1.f));
zeus::CVector3f target = GetTranslation();
target.z() = float(teamPos.z());
zeus::CVector3f moveVec = x45c_steeringBehaviors.Arrival(*this, target, 3.f);
if (moveVec.magSquared() > 0.01f) {
x450_bodyController->GetCommandMgr().DeliverCmd(
CBCLocomotionCmd(moveVec, zeus::CVector3f::skZero, 3.f));
}
}
} else {
switch (mgr.GetActiveRandom()->Range(0, 2)) {
case 0:
x450_bodyController->GetCommandMgr().DeliverCmd(CBCStepCmd(pas::EStepDirection::Left, pas::EStepType::Normal));
break;
case 1:
x450_bodyController->GetCommandMgr().DeliverCmd(CBCStepCmd(pas::EStepDirection::Right, pas::EStepType::Normal));
break;
case 2:
if (ShouldTurn(mgr, 30.f) && delta.canBeNormalized()) {
x450_bodyController->GetCommandMgr().DeliverCmd(
CBCLocomotionCmd(zeus::CVector3f::skZero, delta.normalized(), 1.f));
}
break;
default:
break;
}
}
x450_bodyController->GetCommandMgr().DeliverTargetVector(delta);
}
void CWarWasp::ApplySeparationBehavior(CStateManager& mgr, float sep) {
for (CEntity* ent : mgr.GetListeningAiObjectList()) {
if (TCastToPtr<CPatterned> ai = ent) {
if (ai.GetPtr() != this && ai->GetAreaIdAlways() == GetAreaIdAlways()) {
float useSep = sep;
if (CTeamAiRole* role = CTeamAiMgr::GetTeamAiRole(mgr, x674_aiMgr, ai->GetUniqueId())) {
if (role->GetTeamAiRole() == CTeamAiRole::ETeamAiRole::Melee ||
role->GetTeamAiRole() == CTeamAiRole::ETeamAiRole::Projectile)
useSep *= 2.f;
}
zeus::CVector3f separation = x45c_steeringBehaviors.Separation(*this, ai->GetTranslation(), useSep);
if (!separation.isZero()) {
x450_bodyController->GetCommandMgr().DeliverCmd(
CBCLocomotionCmd(separation, zeus::CVector3f::skZero, 1.f));
}
}
}
}
}
bool CWarWasp::PathToHiveIsClear(CStateManager& mgr) {
zeus::CVector3f delta = x3a0_latestLeashPosition - GetTranslation();
if (GetTransform().basis[1].dot(delta) > 0.f) {
zeus::CAABox aabb(GetTranslation() - 10.f, GetTranslation() + 10.f);
rstl::reserved_vector<TUniqueId, 1024> nearList;
mgr.BuildNearList(nearList, aabb, CMaterialFilter::MakeInclude({EMaterialTypes::Character}), nullptr);
float deltaMagSq = delta.magSquared();
for (TUniqueId id : nearList) {
if (const CWarWasp* other = CPatterned::CastTo<CWarWasp>(mgr.GetObjectById(id))) {
if (other->GetUniqueId() != GetUniqueId() && other->x72e_30_isRetreating &&
zeus::close_enough(other->x3a0_latestLeashPosition, x3a0_latestLeashPosition, 3.f)) {
zeus::CVector3f waspDelta = other->GetTranslation() - GetTranslation();
if (GetTransform().basis[1].dot(waspDelta) > 0.f && waspDelta.magSquared() < 3.f &&
(other->GetTranslation() - x3a0_latestLeashPosition).magSquared() < deltaMagSq) {
return false;
}
}
}
}
}
return true;
}
bool CWarWasp::SteerToDeactivatePos(CStateManager& mgr, EStateMsg msg, float dt) {
float distSq = (x3a0_latestLeashPosition - GetTranslation()).magSquared();
if (distSq > 1.f + x570_cSphere.GetSphere().radius) {
if (PathToHiveIsClear(mgr)) {
zeus::CVector3f arrival1 = x45c_steeringBehaviors.Arrival(*this, x3a0_latestLeashPosition, 15.f);
float maxSpeed = x450_bodyController->GetBodyStateInfo().GetMaxSpeed();
float minMoveFactor;
if (maxSpeed > 0.f)
minMoveFactor = x450_bodyController->GetBodyStateInfo().GetLocomotionSpeed(
pas::ELocomotionAnim::Walk) * 0.5f / maxSpeed;
else
minMoveFactor = 1.f;
float moveFactor = zeus::clamp(minMoveFactor, arrival1.magnitude(), 1.f);
zeus::CVector3f moveVec;
if (arrival1.canBeNormalized())
moveVec = arrival1.normalized() * moveFactor;
else
moveVec = GetTransform().basis[1] * moveFactor;
x450_bodyController->GetCommandMgr().SetSteeringBlendMode(ESteeringBlendMode::FullSpeed);
x450_bodyController->GetCommandMgr().SetSteeringSpeedRange(moveFactor, moveFactor);
if (distSq > 64.f + x570_cSphere.GetSphere().radius) {
if (GetSearchPath() && !PathShagged(mgr, 0.f) && !GetSearchPath()->IsOver()) {
CPatterned::PathFind(mgr, msg, dt);
} else {
x450_bodyController->GetCommandMgr().DeliverCmd(
CBCLocomotionCmd(moveVec, zeus::CVector3f::skZero, 1.f));
}
} else {
RemoveMaterial(EMaterialTypes::Solid, mgr);
zeus::CVector3f target = GetTranslation();
target.z() = float(x3a0_latestLeashPosition.z());
zeus::CVector3f arrival2 = x45c_steeringBehaviors.Arrival(*this, target, 2.5f);
if (arrival2.magSquared() > 0.01f) {
x450_bodyController->GetCommandMgr().DeliverCmd(
CBCLocomotionCmd(arrival2, zeus::CVector3f::skZero, 3.f));
}
x450_bodyController->GetCommandMgr().DeliverCmd(
CBCLocomotionCmd(moveVec, zeus::CVector3f::skZero, 1.f));
}
}
return false;
} else if (distSq > 0.01f) {
RemoveMaterial(EMaterialTypes::Solid, mgr);
zeus::CQuaternion q;
q.rotateZ(zeus::degToRad(180.f));
SetTranslation(GetTranslation() * 0.9f + x3a0_latestLeashPosition * 0.1f);
SetTransform(zeus::CQuaternion::slerpShort(zeus::CQuaternion(GetTransform().basis), x6a0_initialRot * q, 0.1f).
normalized().toTransform(GetTranslation()));
return false;
}
return true;
}
void CWarWasp::PathFind(CStateManager& mgr, EStateMsg msg, float dt) {
switch (msg) {
case EStateMsg::Activate:
SetUpPathFindBehavior(mgr);
break;
case EStateMsg::Update: {
if (mgr.GetPlayer().GetOrbitState() != CPlayer::EPlayerOrbitState::NoOrbit &&
mgr.GetPlayer().GetOrbitTargetId() == GetUniqueId())
x450_bodyController->SetLocomotionType(pas::ELocomotionType::Relaxed);
else
x450_bodyController->SetLocomotionType(pas::ELocomotionType::Lurk);
if (GetSearchPath() && !PathShagged(mgr, 0.f) && !GetSearchPath()->IsOver()) {
CPatterned::PathFind(mgr, msg, dt);
} else {
ApplyNormalSteering(mgr);
}
ApplySeparationBehavior(mgr, 9.f);
float distTest = 2.f * x300_maxAttackRange;
if ((mgr.GetPlayer().GetTranslation() - GetTranslation()).magSquared() > distTest * distTest) {
x450_bodyController->GetCommandMgr().SetSteeringBlendMode(ESteeringBlendMode::FullSpeed);
x450_bodyController->GetCommandMgr().SetSteeringSpeedRange(1.f, 1.f);
} else {
x450_bodyController->GetCommandMgr().SetSteeringBlendMode(ESteeringBlendMode::Normal);
}
break;
}
case EStateMsg::Deactivate:
x450_bodyController->GetCommandMgr().SetSteeringBlendMode(ESteeringBlendMode::Normal);
break;
}
}
void CWarWasp::TargetPatrol(CStateManager& mgr, EStateMsg msg, float dt) {
switch (msg) {
case EStateMsg::Activate:
x450_bodyController->SetLocomotionType(pas::ELocomotionType::Relaxed);
SwarmRemove(mgr);
CPatterned::Patrol(mgr, msg, dt);
CPatterned::UpdateDest(mgr);
x678_targetPos = x2e0_destPos;
if (GetSearchPath())
CPatterned::PathFind(mgr, msg, dt);
break;
case EStateMsg::Update:
if (GetSearchPath() && !PathShagged(mgr, 0.f))
CPatterned::PathFind(mgr, msg, dt);
else
CPatterned::Patrol(mgr, msg, dt);
ApplySeparationBehavior(mgr, 9.f);
break;
default:
break;
}
}
void CWarWasp::Generate(CStateManager& mgr, EStateMsg msg, float dt) {
switch (msg) {
case EStateMsg::Activate:
x450_bodyController->Activate(mgr);
if (x72e_26_initiallyInactive) {
RemoveMaterial(EMaterialTypes::Character, EMaterialTypes::Solid, mgr);
x6a0_initialRot = zeus::CQuaternion(GetTransform().basis);
zeus::CQuaternion q;
q.rotateZ(mgr.GetActiveRandom()->Float() * zeus::degToRad(45.f) - zeus::degToRad(22.5f));
SetTransform((x6a0_initialRot * q).normalized().toTransform(GetTranslation()));
x568_stateProg = 0;
} else {
x568_stateProg = 3;
}
break;
case EStateMsg::Update:
switch (x568_stateProg) {
case 0:
if (x450_bodyController->GetCurrentStateId() == pas::EAnimationState::Generate) {
x568_stateProg = 2;
} else {
x450_bodyController->GetCommandMgr().DeliverCmd(CBCGenerateCmd(pas::EGenerateType::Zero, x388_anim));
}
break;
case 2:
if (x450_bodyController->GetCurrentStateId() != pas::EAnimationState::Generate) {
x568_stateProg = 3;
} else {
zeus::CVector3f moveVec;
for (CEntity* ent : mgr.GetListeningAiObjectList())
if (TCastToPtr<CPatterned> act = ent)
if (act.GetPtr() != this && act->GetAreaIdAlways() == GetAreaIdAlways())
moveVec += x45c_steeringBehaviors.Separation(*this, act->GetTranslation(), 5.f) * (5.f * dt);
if (!moveVec.isZero())
ApplyImpulseWR(GetMoveToORImpulseWR(moveVec, dt), {});
}
break;
default:
break;
}
break;
case EStateMsg::Deactivate:
if (x72e_26_initiallyInactive) {
AddMaterial(EMaterialTypes::Character, EMaterialTypes::Solid, mgr);
if (x328_26_solidCollision)
x401_30_pendingDeath = true;
}
break;
}
}
void CWarWasp::Deactivate(CStateManager& mgr, EStateMsg msg, float dt) {
switch (msg) {
case EStateMsg::Activate:
x568_stateProg = 1;
x72e_30_isRetreating = true;
x450_bodyController->SetLocomotionType(pas::ELocomotionType::Relaxed);
SwarmRemove(mgr);
x674_aiMgr = kInvalidUniqueId;
x678_targetPos = x3a0_latestLeashPosition;
SetDestPos(x678_targetPos);
if (GetSearchPath())
CPatterned::PathFind(mgr, msg, dt);
break;
case EStateMsg::Update:
switch (x568_stateProg) {
case 1:
if (SteerToDeactivatePos(mgr, msg, dt)) {
RemoveMaterial(EMaterialTypes::Solid, mgr);
x568_stateProg = 0;
}
break;
case 0:
if (x450_bodyController->GetCurrentStateId() == pas::EAnimationState::Generate) {
RemoveMaterial(EMaterialTypes::Character, EMaterialTypes::Target, EMaterialTypes::Orbit, mgr);
mgr.GetPlayer().SetOrbitRequestForTarget(GetUniqueId(), CPlayer::EPlayerOrbitRequest::ActivateOrbitSource, mgr);
x568_stateProg = 2;
} else {
x450_bodyController->GetCommandMgr().DeliverCmd(CBCGenerateCmd(pas::EGenerateType::One));
}
break;
case 2:
if (x450_bodyController->GetCurrentStateId() != pas::EAnimationState::Generate)
x568_stateProg = 3;
break;
default:
break;
}
break;
default:
break;
}
}
void CWarWasp::Attack(CStateManager& mgr, EStateMsg msg, float dt) {
switch (msg) {
case EStateMsg::Activate:
x72e_27_teamMatesMelee = true;
x72e_25_canApplyDamage = false;
if (x674_aiMgr != kInvalidUniqueId)
x568_stateProg = CTeamAiMgr::AddAttacker(CTeamAiMgr::EAttackType::Melee, mgr, x674_aiMgr, GetUniqueId()) ? 0 : 3;
else
x568_stateProg = 0;
break;
case EStateMsg::Update:
switch (x568_stateProg) {
case 0:
if (x450_bodyController->GetCurrentStateId() == pas::EAnimationState::MeleeAttack) {
x568_stateProg = 2;
} else {
zeus::CVector3f aimPos = GetProjectileAimPos(mgr, -1.25f);
zeus::CVector3f aimDelta = aimPos - GetTranslation();
if (aimDelta.canBeNormalized())
aimPos += aimDelta.normalized() * 7.5f;
x450_bodyController->GetCommandMgr().DeliverCmd(CBCMeleeAttackCmd(pas::ESeverity::One, aimPos));
}
break;
case 2:
if (x450_bodyController->GetCurrentStateId() != pas::EAnimationState::MeleeAttack) {
x568_stateProg = 3;
} else {
x450_bodyController->GetCommandMgr().DeliverTargetVector(mgr.GetPlayer().GetTranslation() - GetTranslation());
}
break;
default:
break;
}
break;
case EStateMsg::Deactivate:
CTeamAiMgr::ResetTeamAiRole(CTeamAiMgr::EAttackType::Melee, mgr, x674_aiMgr, GetUniqueId(), false);
x700_attackRemTime = CalcTimeToNextAttack(mgr);
x72e_27_teamMatesMelee = false;
break;
}
}
void CWarWasp::JumpBack(CStateManager& mgr, EStateMsg msg, float dt) {
switch (msg) {
case EStateMsg::Activate:
x568_stateProg = 0;
x72e_24_jumpBackRepeat = true;
SwarmRemove(mgr);
break;
case EStateMsg::Update:
switch (x568_stateProg) {
case 0:
if (x450_bodyController->GetCurrentStateId() == pas::EAnimationState::Step) {
x568_stateProg = 2;
} else {
x450_bodyController->GetCommandMgr().DeliverCmd(
CBCStepCmd(pas::EStepDirection::Backward, pas::EStepType::Normal));
}
break;
case 2:
if (x450_bodyController->GetCurrentStateId() != pas::EAnimationState::Step) {
x568_stateProg = x72e_24_jumpBackRepeat ? 0 : 3;
x72e_24_jumpBackRepeat = false;
} else {
x450_bodyController->GetCommandMgr().DeliverTargetVector(mgr.GetPlayer().GetTranslation() - GetTranslation());
}
break;
default:
break;
}
break;
default:
break;
}
}
zeus::CVector3f CWarWasp::CalcShuffleDest(CStateManager& mgr) {
zeus::CVector2f aimPos2d = GetProjectileAimPos(mgr, -1.25f).toVec2f();
zeus::CVector3f playerDir2d = mgr.GetPlayer().GetTransform().basis[1];
playerDir2d.z() = 0.f;
zeus::CVector3f useDir;
if (playerDir2d.canBeNormalized())
useDir = playerDir2d.normalized();
else
useDir = zeus::CVector3f::skForward;
aimPos2d += useDir.toVec2f() * (7.5f + x300_maxAttackRange);
zeus::CVector3f ret(aimPos2d);
ret.z() = GetCloseInZBasis(mgr);
return ret;
}
void CWarWasp::Shuffle(CStateManager& mgr, EStateMsg msg, float dt) {
switch (msg) {
case EStateMsg::Activate:
x450_bodyController->SetLocomotionType(pas::ELocomotionType::Lurk);
x568_stateProg = 2;
break;
case EStateMsg::Update: {
s32 numRoles = 0;
if (TCastToConstPtr<CTeamAiMgr> aiMgr = mgr.GetObjectById(x674_aiMgr))
numRoles = aiMgr->GetNumAssignedAiRoles();
if (numRoles && x700_attackRemTime > 0.f) {
zeus::CVector3f shuffleDest = CalcShuffleDest(mgr);
float zDelta = shuffleDest.z() - GetTranslation().z();
if (zDelta * zDelta > 1.f) {
zeus::CVector3f dest = GetTranslation();
dest.z() = float(shuffleDest.z());
zeus::CVector3f moveVec = x45c_steeringBehaviors.Arrival(*this, dest, 1.f);
if (moveVec.magSquared() > 0.01f)
x450_bodyController->GetCommandMgr().DeliverCmd(CBCLocomotionCmd(moveVec, zeus::CVector3f::skZero, 1.f));
} else {
zeus::CVector3f moveVec = shuffleDest - GetTranslation();
moveVec.z() = zDelta;
if (moveVec.magSquared() > 64.f) {
pas::EStepDirection stepDir = CPatterned::GetStepDirection(moveVec);
if (stepDir != pas::EStepDirection::Forward) {
x450_bodyController->GetCommandMgr().DeliverCmd(CBCStepCmd(stepDir, pas::EStepType::Normal));
} else {
x450_bodyController->GetCommandMgr().DeliverCmd(
CBCLocomotionCmd(x45c_steeringBehaviors.Seek(*this, shuffleDest), zeus::CVector3f::skZero, 1.f));
ApplySeparationBehavior(mgr, 15.f);
}
} else {
x568_stateProg = 3;
}
}
x450_bodyController->GetCommandMgr().DeliverTargetVector(mgr.GetPlayer().GetTranslation() - GetTranslation());
} else {
x568_stateProg = 3;
}
break;
}
case EStateMsg::Deactivate:
x450_bodyController->SetLocomotionType(pas::ELocomotionType::Relaxed);
break;
}
}
void CWarWasp::ProjectileAttack(CStateManager& mgr, EStateMsg msg, float dt) {
switch (msg) {
case EStateMsg::Activate:
x72e_28_inProjectileAttack = true;
if (x674_aiMgr != kInvalidUniqueId) {
x568_stateProg = CTeamAiMgr::AddAttacker(CTeamAiMgr::EAttackType::Projectile,
mgr, x674_aiMgr, GetUniqueId()) ? 0 : 3;
} else {
x568_stateProg = 0;
}
break;
case EStateMsg::Update:
switch (x568_stateProg) {
case 0:
if (x450_bodyController->GetCurrentStateId() == pas::EAnimationState::ProjectileAttack) {
x568_stateProg = 2;
} else {
SetDestPos(GetProjectileAimPos(mgr, -0.07f));
x450_bodyController->GetCommandMgr().DeliverCmd(
CBCProjectileAttackCmd(pas::ESeverity::One, x2e0_destPos, false));
}
break;
case 2:
if (x450_bodyController->GetCurrentStateId() != pas::EAnimationState::ProjectileAttack) {
x450_bodyController->GetCommandMgr().DeliverTargetVector(x2e0_destPos - GetTranslation());
x568_stateProg = 3;
}
break;
default:
break;
}
break;
case EStateMsg::Deactivate:
CTeamAiMgr::ResetTeamAiRole(CTeamAiMgr::EAttackType::Projectile, mgr, x674_aiMgr, GetUniqueId(), false);
x700_attackRemTime = CalcTimeToNextAttack(mgr);
x72e_28_inProjectileAttack = false;
break;
}
}
s32 CWarWasp::GetAttackTeamSize(CStateManager& mgr, s32 team) {
s32 count = 0;
if (TCastToConstPtr<CTeamAiMgr> aimgr = mgr.GetObjectById(x674_aiMgr)) {
if (aimgr->IsPartOfTeam(GetUniqueId())) {
for (const CTeamAiRole& role : aimgr->GetRoles()) {
if (const CWarWasp* other = CPatterned::CastTo<CWarWasp>(mgr.GetObjectById(role.GetOwnerId()))) {
if (team == other->x708_circleAttackTeam)
++count;
}
}
}
}
return count;
}
float CWarWasp::CalcTimeToNextAttack(CStateManager& mgr) {
float mul = 1.f;
if (TCastToConstPtr<CTeamAiMgr> aimgr = mgr.GetObjectById(x674_aiMgr)) {
s32 maxCount = (x3fc_flavor == EFlavorType::Two) ?
aimgr->GetMaxProjectileAttackerCount() : aimgr->GetMaxMeleeAttackerCount();
s32 count = (x3fc_flavor == EFlavorType::Two) ?
aimgr->GetNumAssignedOfRole(CTeamAiRole::ETeamAiRole::Projectile) :
aimgr->GetNumAssignedOfRole(CTeamAiRole::ETeamAiRole::Melee);
if (count <= maxCount)
mul *= 0.5f;
}
return (mgr.GetActiveRandom()->Float() * x308_attackTimeVariation + x304_averageAttackTime) * mul;
}
float CWarWasp::CalcOffTotemAngle(CStateManager& mgr) {
return mgr.GetActiveRandom()->Float() * zeus::degToRad(80.f) + zeus::degToRad(10.f);
}
void CWarWasp::JoinCircleAttackTeam(s32 unit, CStateManager& mgr) {
if (!x6b0_circleBurstPos.isZero()) {
if (x70c_initialCircleAttackTeam == -1) {
x710_initialCircleAttackTeamUnit = GetAttackTeamSize(mgr, unit);
x70c_initialCircleAttackTeam = unit;
}
x708_circleAttackTeam = unit;
x700_attackRemTime = CalcTimeToNextAttack(mgr);
x718_circleBurstOffTotemAngle = CalcOffTotemAngle(mgr);
}
}
void CWarWasp::SetUpCircleTelegraphTeam(CStateManager& mgr) {
if (x708_circleAttackTeam == -1) {
if (TCastToConstPtr<CTeamAiMgr> aimgr = mgr.GetObjectById(x674_aiMgr)) {
if (aimgr->IsPartOfTeam(GetUniqueId()) && aimgr->GetMaxMeleeAttackerCount() > 0) {
s32 teamUnit = 0;
s32 targetUnitSize = 0;
bool rejoinInitial = false;
for (const CTeamAiRole& role : aimgr->GetRoles()) {
if (const CWarWasp* other = CPatterned::CastTo<CWarWasp>(mgr.GetObjectById(role.GetOwnerId()))) {
if (x70c_initialCircleAttackTeam != -1 &&
other->x70c_initialCircleAttackTeam == x70c_initialCircleAttackTeam &&
other->x708_circleAttackTeam >= 0) {
teamUnit = other->x708_circleAttackTeam;
rejoinInitial = true;
break;
}
if (other->x708_circleAttackTeam > teamUnit) {
teamUnit = other->x708_circleAttackTeam;
targetUnitSize = 1;
} else if (other->x708_circleAttackTeam == teamUnit) {
++targetUnitSize;
}
}
}
if (!rejoinInitial && (x70c_initialCircleAttackTeam != -1 ||
targetUnitSize >= aimgr->GetMaxMeleeAttackerCount()))
++teamUnit;
JoinCircleAttackTeam(teamUnit, mgr);
x714_circleTelegraphSeekHeight = mgr.GetActiveRandom()->Float() * -0.5f;
}
}
}
}
TUniqueId CWarWasp::GetAttackTeamLeader(CStateManager& mgr, s32 team) {
if (TCastToConstPtr<CTeamAiMgr> aimgr = mgr.GetObjectById(x674_aiMgr)) {
if (aimgr->IsPartOfTeam(GetUniqueId())) {
for (const CTeamAiRole& role : aimgr->GetRoles()) {
if (const CWarWasp* other = CPatterned::CastTo<CWarWasp>(mgr.GetObjectById(role.GetOwnerId()))) {
if (team == other->x708_circleAttackTeam)
return role.GetOwnerId();
}
}
}
}
return kInvalidUniqueId;
}
void CWarWasp::TryCircleTeamMerge(CStateManager& mgr) {
if (x708_circleAttackTeam > 0) {
if (TCastToConstPtr<CTeamAiMgr> aimgr = mgr.GetObjectById(x674_aiMgr)) {
if (aimgr->IsPartOfTeam(GetUniqueId())) {
if (GetAttackTeamLeader(mgr, x708_circleAttackTeam) == GetUniqueId()) {
if (GetAttackTeamSize(mgr, x708_circleAttackTeam - 1) == 0) {
for (const CTeamAiRole& role : aimgr->GetRoles()) {
if (const CWarWasp* other = CPatterned::CastTo<CWarWasp>(mgr.GetObjectById(role.GetOwnerId()))) {
if (x708_circleAttackTeam == other->x708_circleAttackTeam)
JoinCircleAttackTeam(x708_circleAttackTeam - 1, mgr);
}
}
}
}
}
}
}
}
float CWarWasp::GetTeamZStratum(s32 team) {
if (team > 0) {
if ((team & 1) == 1)
return -3.f - float(team / 2) * 3.f;
else
return float(team / 2) * 3.f;
}
return 0.f;
}
static const float Table[] = {
0.4f, 0.6f, 1.f
};
float CWarWasp::CalcSeekMagnitude(CStateManager& mgr) {
float ret = ((x708_circleAttackTeam >= 0 && x708_circleAttackTeam < 3) ? Table[x708_circleAttackTeam] : 1.f) * 0.9f;
if (TCastToConstPtr<CTeamAiMgr> aimgr = mgr.GetObjectById(x674_aiMgr)) {
if (aimgr->IsPartOfTeam(GetUniqueId())) {
if (aimgr->GetMaxMeleeAttackerCount() > 1) {
if (GetAttackTeamLeader(mgr, x708_circleAttackTeam) != GetUniqueId()) {
zeus::CVector3f fromPlatformCenter = GetTranslation() - x6b0_circleBurstPos;
float minAngle = zeus::degToRad(360.f);
for (const CTeamAiRole& role : aimgr->GetRoles()) {
if (const CWarWasp* other = CPatterned::CastTo<CWarWasp>(mgr.GetObjectById(role.GetOwnerId()))) {
if (x708_circleAttackTeam == other->x708_circleAttackTeam &&
GetTransform().basis[1].dot(other->GetTranslation() - GetTranslation()) > 0.f) {
float angle = zeus::CVector3f::getAngleDiff(
fromPlatformCenter, other->GetTranslation() - x6b0_circleBurstPos);
if (angle < minAngle)
minAngle = angle;
}
}
}
if (minAngle < zeus::degToRad(30.f))
return 0.8f;
if (minAngle > zeus::degToRad(50.f))
return 1.f;
}
}
}
}
return ret;
}
void CWarWasp::UpdateTelegraphMoveSpeed(CStateManager& mgr) {
TUniqueId leaderId = GetAttackTeamLeader(mgr, x708_circleAttackTeam);
if (const CWarWasp* other = CPatterned::CastTo<CWarWasp>(mgr.GetObjectById(leaderId))) {
if (leaderId == GetUniqueId()) {
float cycleTime = x330_stateMachineState.GetTime() - std::trunc(x330_stateMachineState.GetTime() / 2.8f) * 2.8f;
if (cycleTime < 2.f) {
x3b4_speed = x6fc_initialSpeed;
} else {
float t = (cycleTime - 2.f) / 0.8f;
x3b4_speed = ((1.f - t) * 0.7f + 2.f * t) * x6fc_initialSpeed;
}
} else {
x3b4_speed = other->x3b4_speed;
}
} else {
x3b4_speed = x6fc_initialSpeed;
}
}
void CWarWasp::TelegraphAttack(CStateManager& mgr, EStateMsg msg, float dt) {
switch (msg) {
case EStateMsg::Activate:
x450_bodyController->SetLocomotionType(pas::ELocomotionType::Combat);
RemoveMaterial(EMaterialTypes::Orbit, mgr);
mgr.GetPlayer().SetOrbitRequestForTarget(GetUniqueId(), CPlayer::EPlayerOrbitRequest::ActivateOrbitSource, mgr);
SwarmAdd(mgr);
SetUpCircleTelegraphTeam(mgr);
break;
case EStateMsg::Update:
if (!x6b0_circleBurstPos.isZero()) {
TryCircleTeamMerge(mgr);
zeus::CVector3f closeInDelta = GetTranslation() - x6b0_circleBurstPos;
closeInDelta.z() = 0.f;
zeus::CVector3f moveVec = GetTransform().basis[1];
if (closeInDelta.canBeNormalized()) {
zeus::CVector3f closeInDeltaNorm = closeInDelta.normalized();
moveVec = closeInDeltaNorm.cross(zeus::CVector3f::skUp);
zeus::CVector3f seekOrigin = x6b0_circleBurstPos + closeInDeltaNorm * x2fc_minAttackRange;
if (x708_circleAttackTeam > 0)
moveVec = moveVec * -1.f;
float seekHeight = x714_circleTelegraphSeekHeight + GetTeamZStratum(x708_circleAttackTeam);
float seekMag = CalcSeekMagnitude(mgr);
moveVec = x45c_steeringBehaviors.Seek(*this,
seekOrigin + moveVec * 5.f + zeus::CVector3f(0.f, 0.f, seekHeight)) * seekMag;
}
x450_bodyController->GetCommandMgr().DeliverCmd(CBCLocomotionCmd(moveVec, zeus::CVector3f::skZero, 1.f));
UpdateTelegraphMoveSpeed(mgr);
}
break;
case EStateMsg::Deactivate:
AddMaterial(EMaterialTypes::Orbit, mgr);
break;
}
}
void CWarWasp::Dodge(CStateManager& mgr, EStateMsg msg, float dt) {
switch (msg) {
case EStateMsg::Activate:
if (x704_dodgeDir != pas::EStepDirection::Invalid) {
x450_bodyController->GetCommandMgr().DeliverCmd(CBCStepCmd(x704_dodgeDir, pas::EStepType::Dodge));
x568_stateProg = 2;
}
break;
case EStateMsg::Update:
if (x450_bodyController->GetCurrentStateId() != pas::EAnimationState::Step) {
x568_stateProg = 3;
} else {
x450_bodyController->GetCommandMgr().DeliverTargetVector(mgr.GetPlayer().GetTranslation() - GetTranslation());
}
break;
case EStateMsg::Deactivate:
x704_dodgeDir = pas::EStepDirection::Invalid;
break;
}
}
void CWarWasp::Retreat(CStateManager& mgr, EStateMsg msg, float dt) {
switch (msg) {
case EStateMsg::Activate:
SwarmRemove(mgr);
x450_bodyController->SetLocomotionType(pas::ELocomotionType::Internal5);
break;
case EStateMsg::Update:
x450_bodyController->GetCommandMgr().DeliverCmd(
CBCLocomotionCmd(x45c_steeringBehaviors.Flee2D(
*this, mgr.GetPlayer().GetTranslation().toVec2f()), zeus::CVector3f::skZero, 1.f));
break;
case EStateMsg::Deactivate:
x400_24_hitByPlayerProjectile = false;
x450_bodyController->SetLocomotionType(pas::ELocomotionType::Relaxed);
break;
}
}
void CWarWasp::SpecialAttack(CStateManager& mgr, EStateMsg msg, float dt) {
switch (msg) {
case EStateMsg::Activate:
x72e_27_teamMatesMelee = true;
x72e_25_canApplyDamage = true;
x568_stateProg = 0;
x3b4_speed = x6fc_initialSpeed;
break;
case EStateMsg::Update:
switch (x568_stateProg) {
case 0:
if (x450_bodyController->GetCurrentStateId() == pas::EAnimationState::MeleeAttack) {
x568_stateProg = 2;
} else {
x450_bodyController->GetCommandMgr().DeliverCmd(CBCMeleeAttackCmd(pas::ESeverity::Eight));
}
break;
case 2:
if (x450_bodyController->GetCurrentStateId() != pas::EAnimationState::MeleeAttack) {
x568_stateProg = 3;
} else if (GetTransform().basis[1].dot(x6b0_circleBurstPos - GetTranslation()) > 0.f) {
x450_bodyController->GetCommandMgr().DeliverTargetVector(x6b0_circleBurstPos - GetTranslation());
}
break;
default:
break;
}
break;
case EStateMsg::Deactivate:
CTeamAiMgr::ResetTeamAiRole(CTeamAiMgr::EAttackType::Melee, mgr, x674_aiMgr, GetUniqueId(), false);
x72e_27_teamMatesMelee = false;
x708_circleAttackTeam = -1;
x718_circleBurstOffTotemAngle = CalcOffTotemAngle(mgr);
break;
}
}
bool CWarWasp::InAttackPosition(CStateManager& mgr, float arg) {
zeus::CVector3f delta = GetProjectileAimPos(mgr, -1.25f) - GetTranslation();
float negTest = x2fc_minAttackRange - 5.f;
float posTest = 5.f + x300_maxAttackRange;
float magSq = delta.magSquared();
bool ret = magSq > negTest * negTest && magSq < posTest * posTest && !ShouldTurn(mgr, 45.f);
if (ret && delta.canBeNormalized()) {
float deltaMag = delta.magnitude();
ret = mgr.RayStaticIntersection(GetTranslation(), delta / deltaMag, deltaMag,
CMaterialFilter::MakeIncludeExclude({EMaterialTypes::Solid}, {EMaterialTypes::Player})).IsInvalid();
}
return ret;
}
bool CWarWasp::Leash(CStateManager& mgr, float arg) {
if ((x3a0_latestLeashPosition - GetTranslation()).magSquared() > x3c8_leashRadius * x3c8_leashRadius) {
if ((mgr.GetPlayer().GetTranslation() - GetTranslation()).magSquared() >
x3cc_playerLeashRadius * x3cc_playerLeashRadius && x3d4_curPlayerLeashTime > x3d0_playerLeashTime) {
return true;
}
}
return false;
}
bool CWarWasp::PathShagged(CStateManager& mgr, float arg) {
if (CPathFindSearch* pf = GetSearchPath())
return pf->IsShagged();
return false;
}
bool CWarWasp::AnimOver(CStateManager& mgr, float arg) {
return x568_stateProg == 3;
}
bool CWarWasp::ShouldAttack(CStateManager& mgr, float arg) {
if (x700_attackRemTime <= 0.f) {
if (CTeamAiRole* role = CTeamAiMgr::GetTeamAiRole(mgr, x674_aiMgr, GetUniqueId())) {
if (role->GetTeamAiRole() == CTeamAiRole::ETeamAiRole::Melee && !mgr.GetPlayer().IsInWaterMovement()) {
if (TCastToPtr<CTeamAiMgr> tmgr = mgr.ObjectById(x674_aiMgr)) {
if (!tmgr->HasMeleeAttackers()) {
return !IsPatternObstructed(mgr, GetTranslation(), GetProjectileAimPos(mgr, -1.25f));
}
}
}
}
if (x3fc_flavor != EFlavorType::Two && !mgr.GetPlayer().IsInWaterMovement()) {
return !IsPatternObstructed(mgr, GetTranslation(), GetProjectileAimPos(mgr, -1.25f));
}
}
return false;
}
bool CWarWasp::InPosition(CStateManager& mgr, float arg) {
if (CPathFindSearch* pf = GetSearchPath()) {
return pf->IsOver();
} else {
return (x678_targetPos - GetTranslation()).magSquared() < 1.f;
}
}
bool CWarWasp::ShouldTurn(CStateManager& mgr, float arg) {
return zeus::CVector2f::getAngleDiff(GetTransform().basis[1].toVec2f(),
(mgr.GetPlayer().GetTranslation() - GetTranslation()).toVec2f()) > zeus::degToRad(arg);
}
bool CWarWasp::HearShot(CStateManager& mgr, float arg) {
if (x72e_31_heardNoise || x400_24_hitByPlayerProjectile)
return true;
if (TCastToConstPtr<CTeamAiMgr> aimgr = mgr.GetObjectById(x674_aiMgr))
return aimgr->GetNumRoles() != 0;
return false;
}
bool CWarWasp::ShouldFire(CStateManager& mgr, float arg) {
if (x700_attackRemTime <= 0.f) {
if (CTeamAiRole* role = CTeamAiMgr::GetTeamAiRole(mgr, x674_aiMgr, GetUniqueId())) {
if (role->GetTeamAiRole() == CTeamAiRole::ETeamAiRole::Projectile) {
zeus::CVector3f delta = GetProjectileAimPos(mgr, -1.25f) - GetTranslation();
if (delta.canBeNormalized() && GetTransform().basis[1].dot(delta.normalized()) >= 0.906f) {
if (TCastToPtr<CTeamAiMgr> aimgr = mgr.ObjectById(x674_aiMgr)) {
return !aimgr->HasProjectileAttackers();
}
}
}
} else if (x3fc_flavor == EFlavorType::Two) {
zeus::CVector3f delta = GetProjectileAimPos(mgr, -1.25f) - GetTranslation();
if (delta.canBeNormalized()) {
return GetTransform().basis[1].dot(delta.normalized()) >= 0.906f;
}
}
}
return false;
}
bool CWarWasp::ShouldDodge(CStateManager& mgr, float arg) {
zeus::CAABox aabb(GetTranslation() - 7.5f, GetTranslation() + 7.5f);
rstl::reserved_vector<TUniqueId, 1024> nearList;
mgr.BuildNearList(nearList, aabb, CMaterialFilter::MakeInclude({EMaterialTypes::Projectile}), nullptr);
for (TUniqueId id : nearList) {
if (TCastToConstPtr<CGameProjectile> proj = mgr.GetObjectById(id)) {
if (mgr.GetPlayer().GetUniqueId() == proj->GetOwnerId()) {
zeus::CVector3f delta = proj->GetTranslation() - GetTranslation();
if (zeus::CVector3f::getAngleDiff(GetTransform().basis[1], delta) < zeus::degToRad(45.f)) {
x704_dodgeDir = GetTransform().basis[0].dot(delta) > 0.f ?
pas::EStepDirection::Right : pas::EStepDirection::Left;
return true;
}
}
}
}
return false;
}
bool CWarWasp::CheckCircleAttackSpread(CStateManager& mgr, s32 team) {
if (TCastToConstPtr<CTeamAiMgr> aimgr = mgr.GetObjectById(x674_aiMgr)) {
s32 teamSize = GetAttackTeamSize(mgr, team);
if (teamSize == 1)
return true;
TUniqueId leaderId = GetAttackTeamLeader(mgr, team);
if (const CWarWasp* leaderWasp = CPatterned::CastTo<CWarWasp>(mgr.GetObjectById(leaderId))) {
zeus::CVector3f platformToLeaderWasp = leaderWasp->GetTranslation() - x6b0_circleBurstPos;
float maxAng = 0.f;
for (const CTeamAiRole& role : aimgr->GetRoles()) {
if (role.GetOwnerId() == leaderId)
continue;
if (const CWarWasp* wasp2 = CPatterned::CastTo<CWarWasp>(mgr.GetObjectById(role.GetOwnerId()))) {
if (wasp2->x708_circleAttackTeam == team) {
if (leaderWasp->GetTransform().basis[1].dot(wasp2->GetTranslation() - leaderWasp->GetTranslation()) > 0.f)
return false;
float angle = zeus::CVector3f::getAngleDiff(wasp2->GetTranslation() - x6b0_circleBurstPos,
platformToLeaderWasp);
if (angle > maxAng)
maxAng = angle;
}
}
}
return maxAng < zeus::degToRad(40.f) * float(teamSize - 1) + zeus::degToRad(20.f);
}
}
return false;
}
bool CWarWasp::ShouldSpecialAttack(CStateManager& mgr, float arg) {
if (x708_circleAttackTeam == 0 && !mgr.GetPlayer().IsInWaterMovement()) {
if (TCastToConstPtr<CTeamAiMgr> aimgr = mgr.GetObjectById(x674_aiMgr)) {
if (CheckCircleAttackSpread(mgr, x708_circleAttackTeam)) {
TUniqueId leaderId = GetAttackTeamLeader(mgr, x708_circleAttackTeam);
if (leaderId == GetUniqueId()) {
if (x700_attackRemTime <= 0.f &&
(mgr.GetPlayer().GetTranslation().toVec2f() - x6b0_circleBurstPos.toVec2f()).magSquared() < 90.25f) {
zeus::CVector3f fromPlatformCenter = GetTranslation() - x6b0_circleBurstPos;
zeus::CVector3f thresholdVec = x6bc_circleBurstDir;
if (x718_circleBurstOffTotemAngle <= zeus::degToRad(90.f)) {
thresholdVec = zeus::CVector3f::slerp(x6c8_circleBurstRight, x6bc_circleBurstDir,
x718_circleBurstOffTotemAngle);
} else {
thresholdVec = zeus::CVector3f::slerp(x6bc_circleBurstDir, -x6c8_circleBurstRight,
x718_circleBurstOffTotemAngle - zeus::degToRad(90.f));
}
if (zeus::CVector3f::getAngleDiff(thresholdVec, fromPlatformCenter) < zeus::degToRad(10.f))
return CTeamAiMgr::AddAttacker(CTeamAiMgr::EAttackType::Melee, mgr, x674_aiMgr, GetUniqueId());
}
} else {
if (const CWarWasp* leaderWasp = CPatterned::CastTo<CWarWasp>(mgr.GetObjectById(leaderId))) {
if (leaderWasp->x72e_27_teamMatesMelee) {
return CTeamAiMgr::AddAttacker(CTeamAiMgr::EAttackType::Melee, mgr, x674_aiMgr, GetUniqueId());
}
}
}
}
}
}
return false;
}
CPathFindSearch* CWarWasp::GetSearchPath() {
return &x590_pfSearch;
}
CProjectileInfo* CWarWasp::GetProjectileInfo() {
return &x6d4_projectileInfo;
}
} // namespace urde::MP1