mirror of https://github.com/AxioDL/metaforce.git
1226 lines
47 KiB
C++
1226 lines
47 KiB
C++
#include "Runtime/MP1/World/CWarWasp.hpp"
|
|
|
|
#include <array>
|
|
|
|
#include "Runtime/CSimplePool.hpp"
|
|
#include "Runtime/GameGlobalObjects.hpp"
|
|
#include "Runtime/Character/CCharLayoutInfo.hpp"
|
|
#include "Runtime/Weapon/CGameProjectile.hpp"
|
|
#include "Runtime/World/CPatternedInfo.hpp"
|
|
#include "Runtime/World/CPlayer.hpp"
|
|
#include "Runtime/World/CScriptWaypoint.hpp"
|
|
#include "Runtime/World/CTeamAiMgr.hpp"
|
|
#include "Runtime/World/CWorld.hpp"
|
|
|
|
#include "TCastTo.hpp" // Generated file, do not modify include path
|
|
|
|
namespace metaforce::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_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::Ranged : 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::skZero3f);
|
|
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::optional<zeus::CAABox> CWarWasp::GetTouchBounds() const {
|
|
return {x570_cSphere.CalculateAABox(GetTransform())};
|
|
}
|
|
|
|
zeus::CVector3f CWarWasp::GetProjectileAimPos(const CStateManager& mgr, float zBias) const {
|
|
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::skOne3f);
|
|
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::skZero3f, 1.f));
|
|
zeus::CVector3f target = GetTranslation();
|
|
target.z() = float(teamPos.z());
|
|
zeus::CVector3f moveVec = x45c_steeringBehaviors.Arrival(*this, target, 2.5f);
|
|
if (moveVec.magSquared() > 0.01f) {
|
|
x450_bodyController->GetCommandMgr().DeliverCmd(CBCLocomotionCmd(moveVec, zeus::skZero3f, 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::skZero3f, 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::Ranged)
|
|
useSep *= 2.f;
|
|
}
|
|
zeus::CVector3f separation = x45c_steeringBehaviors.Separation(*this, ai->GetTranslation(), useSep);
|
|
if (!separation.isZero()) {
|
|
x450_bodyController->GetCommandMgr().DeliverCmd(CBCLocomotionCmd(separation, zeus::skZero3f, 1.f));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
bool CWarWasp::PathToHiveIsClear(CStateManager& mgr) const {
|
|
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::skZero3f, 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::skZero3f, 3.f));
|
|
}
|
|
x450_bodyController->GetCommandMgr().DeliverCmd(CBCLocomotionCmd(moveVec, zeus::skZero3f, 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(const CStateManager& mgr) const {
|
|
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::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::skZero3f, 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::skZero3f, 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::Ranged,
|
|
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::Ranged, mgr, x674_aiMgr, GetUniqueId(), false);
|
|
x700_attackRemTime = CalcTimeToNextAttack(mgr);
|
|
x72e_28_inProjectileAttack = false;
|
|
break;
|
|
}
|
|
}
|
|
|
|
s32 CWarWasp::GetAttackTeamSize(const CStateManager& mgr, s32 team) const {
|
|
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) const {
|
|
float mul = 1.f;
|
|
if (TCastToConstPtr<CTeamAiMgr> aimgr = mgr.GetObjectById(x674_aiMgr)) {
|
|
const s32 maxCount =
|
|
(x3fc_flavor == EFlavorType::Two) ? aimgr->GetMaxRangedAttackerCount() : aimgr->GetMaxMeleeAttackerCount();
|
|
const s32 count = (x3fc_flavor == EFlavorType::Two) ? aimgr->GetNumAssignedOfRole(CTeamAiRole::ETeamAiRole::Ranged)
|
|
: 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) const {
|
|
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(const CStateManager& mgr, s32 team) const {
|
|
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) const {
|
|
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;
|
|
}
|
|
|
|
float CWarWasp::CalcSeekMagnitude(const CStateManager& mgr) const {
|
|
static constexpr std::array Table{0.4f, 0.6f, 1.f};
|
|
|
|
const 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()) {
|
|
const 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) {
|
|
const 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::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::skZero3f, 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::skZero3f, 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::Ranged) {
|
|
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->HasRangedAttackers();
|
|
}
|
|
}
|
|
}
|
|
} 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(const CStateManager& mgr, s32 team) const {
|
|
if (TCastToConstPtr<CTeamAiMgr> aimgr = mgr.GetObjectById(x674_aiMgr)) {
|
|
const s32 teamSize = GetAttackTeamSize(mgr, team);
|
|
if (teamSize == 1)
|
|
return true;
|
|
const TUniqueId leaderId = GetAttackTeamLeader(mgr, team);
|
|
if (const CWarWasp* leaderWasp = CPatterned::CastTo<CWarWasp>(mgr.GetObjectById(leaderId))) {
|
|
const 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;
|
|
const 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 metaforce::MP1
|