mirror of
				https://github.com/AxioDL/metaforce.git
				synced 2025-10-27 15:30:23 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			1013 lines
		
	
	
		
			36 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			1013 lines
		
	
	
		
			36 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| #include "CBeetle.hpp"
 | |
| #include "CStateManager.hpp"
 | |
| #include "World/CDamageInfo.hpp"
 | |
| #include "Character/CCharLayoutInfo.hpp"
 | |
| #include "TCastTo.hpp"
 | |
| #include "World/CPatternedInfo.hpp"
 | |
| #include "Character/CPASAnimParmData.hpp"
 | |
| #include "World/CTeamAiMgr.hpp"
 | |
| #include "World/CWorld.hpp"
 | |
| #include "World/CPlayer.hpp"
 | |
| #include "World/CScriptWaypoint.hpp"
 | |
| 
 | |
| namespace urde::MP1 {
 | |
| 
 | |
| CBeetle::CBeetle(TUniqueId uid, std::string_view name, const CEntityInfo& info, const zeus::CTransform& xf,
 | |
|                  CModelData&& mData, const CPatternedInfo& pInfo, CPatterned::EFlavorType flavor,
 | |
|                  CBeetle::EEntranceType entranceType, const CDamageInfo& touchDamage,
 | |
|                  const CDamageVulnerability& platingVuln, const zeus::CVector3f& tailAimReference,
 | |
|                  float initialAttackDelay, float retreatTime, float f3,
 | |
|                  const CDamageVulnerability& tailVuln, const CActorParameters& aParams,
 | |
|                  const std::experimental::optional<CStaticRes>& tailModel)
 | |
| : CPatterned(ECharacter::Beetle, uid, name, flavor, info, xf, std::move(mData), pInfo, EMovementType::Ground,
 | |
|              EColliderType::One, EBodyType::BiPedal, aParams, EKnockBackVariant(flavor))
 | |
| , x56c_entranceType(entranceType)
 | |
| , x574_tailAimReference(tailAimReference)
 | |
| , x580_f3(f3)
 | |
| , x584_touchDamage(touchDamage)
 | |
| , x5ac_tailModel(tailModel ? std::experimental::optional<CModelData>(CModelData(*tailModel)) :
 | |
|                              std::experimental::optional<CModelData>())
 | |
| , x5fc_pathFindSearch(nullptr, 1, pInfo.GetPathfindingIndex(), 1.f, 1.f)
 | |
| , x744_platingVuln(platingVuln)
 | |
| , x7ac_tailVuln(tailVuln)
 | |
| , x814_attackDelayTimer(initialAttackDelay)
 | |
| , x834_retreatTime(retreatTime) {
 | |
|   x5a0_headbuttDist = GetAnimationDistance(CPASAnimParmData(7, CPASAnimParm::FromEnum(0), CPASAnimParm::FromEnum(1)));
 | |
|   x5a4_jumpBackwardDist = x64_modelData->GetScale().y() *
 | |
|     GetAnimationDistance(CPASAnimParmData(3, CPASAnimParm::FromEnum(1), CPASAnimParm::FromEnum(0)));
 | |
|   MakeThermalColdAndHot();
 | |
|   if (x3fc_flavor == EFlavorType::One)
 | |
|     x460_knockBackController.SetLocomotionDuringElectrocution(true);
 | |
| }
 | |
| 
 | |
| void CBeetle::Accept(IVisitor& visitor) { visitor.Visit(this); }
 | |
| 
 | |
| void CBeetle::SquadAdd(CStateManager& mgr) {
 | |
|   if (x570_aiMgr != kInvalidUniqueId)
 | |
|     if (TCastToPtr<CTeamAiMgr> aimgr = mgr.ObjectById(x570_aiMgr))
 | |
|       aimgr->AssignTeamAiRole(*this, CTeamAiRole::ETeamAiRole::Melee,
 | |
|                               CTeamAiRole::ETeamAiRole::Unknown, CTeamAiRole::ETeamAiRole::Invalid);
 | |
| }
 | |
| 
 | |
| void CBeetle::SquadRemove(CStateManager& mgr) {
 | |
|   if (x570_aiMgr != kInvalidUniqueId)
 | |
|     if (TCastToPtr<CTeamAiMgr> aimgr = mgr.ObjectById(x570_aiMgr))
 | |
|       if (aimgr->IsPartOfTeam(GetUniqueId()))
 | |
|         aimgr->RemoveTeamAiRole(GetUniqueId());
 | |
| }
 | |
| 
 | |
| void CBeetle::Think(float dt, CStateManager& mgr) {
 | |
|   if (!GetActive())
 | |
|     return;
 | |
|   if (CTeamAiRole* role = CTeamAiMgr::GetTeamAiRole(mgr, x570_aiMgr, GetUniqueId())) {
 | |
|     x450_bodyController->SetLocomotionType(
 | |
|       role->GetTeamAiRole() == CTeamAiRole::ETeamAiRole::Melee ?
 | |
|       pas::ELocomotionType::Lurk : pas::ELocomotionType::Relaxed);
 | |
|   } else {
 | |
|     SquadAdd(mgr);
 | |
|     x450_bodyController->SetLocomotionType(pas::ELocomotionType::Lurk);
 | |
|   }
 | |
|   x460_knockBackController.SetAutoResetImpulse(IsOnGround());
 | |
|   if (x814_attackDelayTimer > 0.f)
 | |
|     x814_attackDelayTimer -= dt;
 | |
|   if ((x824_predictPos - GetTranslation()).toVec2f().magSquared() > 0.1f * dt)
 | |
|     x820_posDeviationCounter += 1;
 | |
|   else
 | |
|     x820_posDeviationCounter = 0;
 | |
|   CPatterned::Think(dt, mgr);
 | |
|   x824_predictPos = x138_velocity * dt + GetTranslation();
 | |
| }
 | |
| 
 | |
| void CBeetle::SetupRetreatPoints(CStateManager& mgr) {
 | |
|   for (const auto& conn : GetConnectionList()) {
 | |
|     if (conn.x0_state == EScriptObjectState::Retreat && conn.x4_msg == EScriptObjectMessage::Follow) {
 | |
|       if (TCastToConstPtr<CScriptWaypoint> wp = mgr.GetObjectById(mgr.GetIdForScript(conn.x8_objId))) {
 | |
|         x6e0_retreatPoints.push_back(wp->GetTranslation());
 | |
|         if (x6e0_retreatPoints.size() == 8)
 | |
|           break;
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| void CBeetle::AcceptScriptMsg(EScriptObjectMessage msg, TUniqueId sender, CStateManager& mgr) {
 | |
|   bool forward = true;
 | |
|   switch (msg) {
 | |
|   case EScriptObjectMessage::Activate:
 | |
|     SquadAdd(mgr);
 | |
|     break;
 | |
|   case EScriptObjectMessage::Deactivate:
 | |
|   case EScriptObjectMessage::Deleted:
 | |
|     SquadRemove(mgr);
 | |
|     break;
 | |
|   case EScriptObjectMessage::OnFloor:
 | |
|     forward = false;
 | |
|     SetMomentumWR(zeus::CVector3f::skZero);
 | |
|     x328_27_onGround = true;
 | |
|     break;
 | |
|   case EScriptObjectMessage::Falling:
 | |
|     if (!x450_bodyController->IsFrozen()) {
 | |
|       SetMomentumWR({0.f, 0.f, -(GetGravityConstant() * xe8_mass)});
 | |
|       x328_27_onGround = false;
 | |
|     }
 | |
|     forward = false;
 | |
|     break;
 | |
|   case EScriptObjectMessage::InitializedInArea:
 | |
|     if (x570_aiMgr == kInvalidUniqueId) {
 | |
|       x570_aiMgr = CTeamAiMgr::GetTeamAiMgr(*this, mgr);
 | |
|       if (GetActive())
 | |
|         SquadAdd(mgr);
 | |
|     }
 | |
|     SetupRetreatPoints(mgr);
 | |
|     x5fc_pathFindSearch.SetArea(
 | |
|       mgr.GetWorld()->GetAreaAlways(GetAreaIdAlways())->GetPostConstructed()->x10bc_pathArea);
 | |
|     break;
 | |
|   default:
 | |
|     break;
 | |
|   }
 | |
|   if (forward)
 | |
|     CPatterned::AcceptScriptMsg(msg, sender, mgr);
 | |
| }
 | |
| 
 | |
| void CBeetle::PreRender(CStateManager& mgr, const zeus::CFrustum& frustum) {
 | |
|   if (x400_25_alive) {
 | |
|     switch (mgr.GetPlayerState()->GetActiveVisor(mgr)) {
 | |
|     case CPlayerState::EPlayerVisor::XRay:
 | |
|       x42c_color.a() = 76.5f / 255.f;
 | |
|       break;
 | |
|     case CPlayerState::EPlayerVisor::Thermal:
 | |
|       if (x838_25_burrowing)
 | |
|         x42c_color.a() = x830_intoGroundFactor;
 | |
|       else
 | |
|         x42c_color.a() = 1.f;
 | |
|       break;
 | |
|     default:
 | |
|       x42c_color.a() = 1.f;
 | |
|       break;
 | |
|     }
 | |
|   }
 | |
|   CPatterned::PreRender(mgr, frustum);
 | |
| }
 | |
| 
 | |
| void CBeetle::Render(const CStateManager& mgr) const {
 | |
|   if (x3fc_flavor == EFlavorType::One && x400_25_alive) {
 | |
|     zeus::CTransform tailXf = GetLctrTransform("Target_Tail"sv);
 | |
|     if (x428_damageCooldownTimer >= 0.f && x42c_color.a() == 1.f) {
 | |
|       if (x5ac_tailModel) {
 | |
|         CModelFlags flags(2, 0, 3, zeus::CColor::skWhite);
 | |
|         flags.addColor = x42c_color;
 | |
|         x5ac_tailModel->Render(mgr, tailXf, x90_actorLights.get(), flags);
 | |
|       }
 | |
|     } else if (x5ac_tailModel) {
 | |
|       CModelFlags flags(0, 0, 3, zeus::CColor::skWhite);
 | |
|       x5ac_tailModel->Render(mgr, tailXf, x90_actorLights.get(), flags);
 | |
|     }
 | |
|   }
 | |
|   CPatterned::Render(mgr);
 | |
| }
 | |
| 
 | |
| const CDamageVulnerability* CBeetle::GetDamageVulnerability() const {
 | |
|   if (x838_25_burrowing)
 | |
|     return &CDamageVulnerability::PassThroughVulnerabilty();
 | |
|   if (x3fc_flavor == EFlavorType::One)
 | |
|     return x450_bodyController->IsOnFire() ? &x7ac_tailVuln : &x744_platingVuln;
 | |
|   return CAi::GetDamageVulnerability();
 | |
| }
 | |
| 
 | |
| const CDamageVulnerability* CBeetle::GetDamageVulnerability(const zeus::CVector3f& pos,
 | |
|                                                             const zeus::CVector3f& dir,
 | |
|                                                             const CDamageInfo& dInfo) const {
 | |
|   if (x838_25_burrowing)
 | |
|     return &CDamageVulnerability::PassThroughVulnerabilty();
 | |
|   if (x3fc_flavor == EFlavorType::One) {
 | |
|     if (dInfo.GetWeaponMode().IsComboed() && dInfo.GetWeaponMode().GetType() == EWeaponType::Wave)
 | |
|       return &x7ac_tailVuln;
 | |
|     if (GetTransform().basis[1].dot(dir) > 0.f &&
 | |
|         GetTransform().basis[1].dot(zeus::CUnitVector3f(pos - GetBoundingBox().center())) < -0.5f)
 | |
|       return &x7ac_tailVuln;
 | |
|     else
 | |
|       return &x744_platingVuln;
 | |
|   }
 | |
|   return GetDamageVulnerability();
 | |
| }
 | |
| 
 | |
| zeus::CVector3f CBeetle::GetOrbitPosition(const CStateManager& mgr) const {
 | |
|   zeus::CVector3f ret = CPatterned::GetOrbitPosition(mgr);
 | |
|   ret.z() = float(GetBoundingBox().center().z());
 | |
|   return ret;
 | |
| }
 | |
| 
 | |
| zeus::CVector3f CBeetle::GetAimPosition(const CStateManager& mgr, float dt) const {
 | |
|   if (x3fc_flavor == EFlavorType::One || x3fc_flavor == EFlavorType::Two) {
 | |
|     zeus::CTransform tailXf = GetLctrTransform("Target_Tail"sv);
 | |
|     zeus::CVector3f scaleRange = tailXf * x574_tailAimReference - GetTranslation();
 | |
|     zeus::CAABox aabb = GetBoundingBox();
 | |
|     float minFactor = 10.f;
 | |
|     for (int i = 0; i < 3; ++i) {
 | |
|       if (scaleRange[i] < 0.f) {
 | |
|         float factor = (aabb.min[i] - GetTranslation()[i]) / scaleRange[i];
 | |
|         if (factor < minFactor)
 | |
|           minFactor = factor;
 | |
|       } else if (scaleRange[i] > 0.f) {
 | |
|         float factor = (aabb.max[i] - GetTranslation()[i]) / scaleRange[i];
 | |
|         if (factor < minFactor)
 | |
|           minFactor = factor;
 | |
|       }
 | |
|     }
 | |
|     return scaleRange * minFactor + GetTranslation();
 | |
|   } else {
 | |
|     return CPhysicsActor::GetAimPosition(mgr, dt);
 | |
|   }
 | |
| }
 | |
| 
 | |
| EWeaponCollisionResponseTypes CBeetle::GetCollisionResponseType(const zeus::CVector3f& pos,
 | |
|                                                                 const zeus::CVector3f& dir,
 | |
|                                                                 const CWeaponMode& wMode,
 | |
|                                                                 EProjectileAttrib attribs) const {
 | |
|   if (x450_bodyController->IsFrozen() && wMode.GetType() == EWeaponType::Ice)
 | |
|     return EWeaponCollisionResponseTypes::None;
 | |
|   if(x838_25_burrowing)
 | |
|     return EWeaponCollisionResponseTypes::Unknown69;
 | |
|   if (x3fc_flavor == EFlavorType::One) {
 | |
|     if (GetTransform().basis[1].dot(dir) > 0.f &&
 | |
|         GetTransform().basis[1].dot(zeus::CUnitVector3f(pos - GetBoundingBox().center())) < -0.5f)
 | |
|       return EWeaponCollisionResponseTypes::Unknown44;
 | |
|     if (!x744_platingVuln.WeaponHurts(wMode, false))
 | |
|       return EWeaponCollisionResponseTypes::Unknown69;
 | |
|   }
 | |
|   return EWeaponCollisionResponseTypes::Unknown19;
 | |
| }
 | |
| 
 | |
| void CBeetle::DoUserAnimEvent(CStateManager& mgr, const CInt32POINode& node, EUserEventType type, float dt) {
 | |
|   bool handled = false;
 | |
|   switch (type) {
 | |
|   case EUserEventType::ChangeMaterial:
 | |
|     AddMaterial(EMaterialTypes::Character, EMaterialTypes::Solid, mgr);
 | |
|     x328_25_verticalMovement = true;
 | |
|     RemoveMaterial(EMaterialTypes::GroundCollider, mgr);
 | |
|     handled = true;
 | |
|     break;
 | |
|   case EUserEventType::GenerateEnd:
 | |
|     x328_25_verticalMovement = false;
 | |
|     AddMaterial(EMaterialTypes::GroundCollider, mgr);
 | |
|     handled = true;
 | |
|     break;
 | |
|   case EUserEventType::DamageOn: {
 | |
|     zeus::CVector3f center =
 | |
|     GetTransform() * (x64_modelData->GetScale() * GetLocatorTransform("LCTR_GARMOUTH"sv).origin);
 | |
|     zeus::CVector3f extent = x64_modelData->GetScale() * zeus::CVector3f(2.f, 2.f, 0.5f);
 | |
|     if (zeus::CAABox(center - extent, center + extent).intersects(mgr.GetPlayer().GetBoundingBox())) {
 | |
|       mgr.ApplyDamage(GetUniqueId(), mgr.GetPlayer().GetUniqueId(), GetUniqueId(), x584_touchDamage,
 | |
|       CMaterialFilter::MakeIncludeExclude({EMaterialTypes::Solid}, {}), zeus::CVector3f::skZero);
 | |
|     }
 | |
|     handled = true;
 | |
|     break;
 | |
|   }
 | |
|   case EUserEventType::Delete:
 | |
|     handled = true;
 | |
|     break;
 | |
|   default:
 | |
|     break;
 | |
|   }
 | |
|   if (!handled)
 | |
|     CPatterned::DoUserAnimEvent(mgr, node, type, dt);
 | |
| }
 | |
| 
 | |
| void CBeetle::CollidedWith(TUniqueId uid, const CCollisionInfoList& list, CStateManager& mgr) {
 | |
|   static CMaterialList envList(EMaterialTypes::Ceiling, EMaterialTypes::Wall);
 | |
|   for (const auto& c : list) {
 | |
|     if (c.GetMaterialLeft().Intersection(envList) != 0 &&
 | |
|         x450_bodyController->GetCurrentStateId() == pas::EAnimationState::MeleeAttack) {
 | |
|       x450_bodyController->GetCommandMgr().DeliverCmd(CBodyStateCmd(EBodyStateCmd::NextState));
 | |
|       SetVelocityWR(zeus::CVector3f::skZero);
 | |
|     }
 | |
|   }
 | |
|   CPatterned::CollidedWith(uid, list, mgr);
 | |
| }
 | |
| 
 | |
| void CBeetle::Death(CStateManager& mgr, const zeus::CVector3f& direction, EScriptObjectState state) {
 | |
|   if (x400_25_alive) {
 | |
|     if (x3fc_flavor == EFlavorType::One) {
 | |
|       zeus::CTransform backupXf = GetTransform();
 | |
|       SetTransform(GetLctrTransform("Target_Tail"sv));
 | |
|       SendScriptMsgs(EScriptObjectState::DeathRattle, mgr, EScriptObjectMessage::None);
 | |
|       SetTransform(backupXf);
 | |
|     }
 | |
|     CPatterned::Death(mgr, direction, state);
 | |
|   }
 | |
| }
 | |
| 
 | |
| void CBeetle::TakeDamage(const zeus::CVector3f& direction, float magnitude) {
 | |
|   x428_damageCooldownTimer = 0.33f;
 | |
| }
 | |
| 
 | |
| bool CBeetle::IsListening() const {
 | |
|   return true;
 | |
| }
 | |
| 
 | |
| zeus::CVector3f CBeetle::GetOrigin(const CStateManager& mgr, const CTeamAiRole& role,
 | |
|                                    const zeus::CVector3f& aimPos) const {
 | |
|   float midRange = (x2fc_minAttackRange + x300_maxAttackRange) * 0.5f;
 | |
|   zeus::CVector3f playerToThis = GetTranslation() - aimPos;
 | |
|   if (playerToThis.canBeNormalized())
 | |
|     return aimPos + playerToThis.normalized() * midRange;
 | |
|   else
 | |
|     return aimPos + GetTransform().basis[1] * midRange;
 | |
| }
 | |
| 
 | |
| void CBeetle::FollowPattern(CStateManager& mgr, EStateMsg msg, float dt) {
 | |
|   switch (msg) {
 | |
|   case EStateMsg::Activate:
 | |
|     x568_stateProg = 1;
 | |
|     break;
 | |
|   case EStateMsg::Update:
 | |
|     switch (x568_stateProg) {
 | |
|     case 1:
 | |
|       if (x450_bodyController->GetCurrentStateId() == pas::EAnimationState::Step) {
 | |
|         x568_stateProg = 3;
 | |
|       } else if (IsOnGround()) {
 | |
|         x450_bodyController->GetCommandMgr().DeliverCmd(
 | |
|           CBCStepCmd(pas::EStepDirection::Left, pas::EStepType::Normal));
 | |
|       }
 | |
|       break;
 | |
|     case 3:
 | |
|       if (x450_bodyController->GetCurrentStateId() != pas::EAnimationState::Step && IsOnGround()) {
 | |
|         x450_bodyController->GetCommandMgr().DeliverCmd(
 | |
|           CBCStepCmd(pas::EStepDirection::Right, pas::EStepType::Normal));
 | |
|         x568_stateProg = 2;
 | |
|       }
 | |
|       break;
 | |
|     case 2:
 | |
|       if (x450_bodyController->GetCurrentStateId() != pas::EAnimationState::Step)
 | |
|         x568_stateProg = x814_attackDelayTimer <= 0.f ? 4 : 1;
 | |
|       break;
 | |
|     default:
 | |
|       break;
 | |
|     }
 | |
|     x450_bodyController->GetCommandMgr().DeliverTargetVector(
 | |
|       mgr.GetPlayer().GetTranslation() - GetTranslation());
 | |
|     break;
 | |
|   default:
 | |
|     break;
 | |
|   }
 | |
| }
 | |
| 
 | |
| void CBeetle::RefinePathFindDest(CStateManager& mgr, zeus::CVector3f& dest) {
 | |
|   dest = mgr.GetPlayer().GetTranslation();
 | |
|   if (CTeamAiRole* role = CTeamAiMgr::GetTeamAiRole(mgr, x570_aiMgr, GetUniqueId())) {
 | |
|     dest = role->GetTeamPosition();
 | |
|   } else {
 | |
|     zeus::CVector3f thisToDest = dest - GetTranslation();
 | |
|     dest += (thisToDest.canBeNormalized() ? thisToDest.normalized() : GetTransform().basis[1]) *
 | |
|       (0.5f * (x2fc_minAttackRange + x300_maxAttackRange));
 | |
|   }
 | |
| }
 | |
| 
 | |
| void CBeetle::SeparateFromMelees(CStateManager& mgr) {
 | |
|   for (CEntity* ent : mgr.GetListeningAiObjectList()) {
 | |
|     if (TCastToPtr<CPatterned> ai = ent) {
 | |
|       if (ai.GetPtr() != this && ai->GetAreaIdAlways() == GetAreaIdAlways()) {
 | |
|         float dist = 4.f;
 | |
|         if (CTeamAiRole* role = CTeamAiMgr::GetTeamAiRole(mgr, x570_aiMgr, ai->GetUniqueId()))
 | |
|           if (role->GetTeamAiRole() == CTeamAiRole::ETeamAiRole::Melee)
 | |
|             dist *= 2.f;
 | |
|         zeus::CVector3f move = x45c_steeringBehaviors.Separation(*this, ai->GetTranslation(), dist);
 | |
|         if (!move.isZero())
 | |
|           x450_bodyController->GetCommandMgr().DeliverCmd(CBCLocomotionCmd(move, zeus::CVector3f::skZero, 1.f));
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| void CBeetle::PathFind(CStateManager& mgr, EStateMsg msg, float dt) {
 | |
|   switch (msg) {
 | |
|   case EStateMsg::Activate:
 | |
|     if (GetSearchPath()) {
 | |
|       RefinePathFindDest(mgr, x2e0_destPos);
 | |
|       CPatterned::PathFind(mgr, msg, dt);
 | |
|     }
 | |
|     break;
 | |
|   case EStateMsg::Update: {
 | |
|     zeus::CVector3f dest = mgr.GetPlayer().GetTranslation();
 | |
|     RefinePathFindDest(mgr, dest);
 | |
|     zeus::CVector3f delta = dest - GetTranslation();
 | |
|     zeus::CVector3f move;
 | |
|     if (!PathShagged(mgr, 0.f) && !x5fc_pathFindSearch.IsOver()) {
 | |
|       CPatterned::PathFind(mgr, msg, dt);
 | |
|       move = x450_bodyController->GetCommandMgr().GetMoveVector();
 | |
|     } else {
 | |
|       move = x45c_steeringBehaviors.Arrival(*this, dest, 5.f);
 | |
|     }
 | |
|     if (GetTransform().basis[1].dot(move) >= 0.f || GetTransform().basis[1].dot(delta) <= 0.f) {
 | |
|       x450_bodyController->GetCommandMgr().DeliverCmd(CBCLocomotionCmd(move, zeus::CVector3f::skZero, 1.f));
 | |
|     } else {
 | |
|       zeus::CVector3f face = delta.canBeNormalized() ? delta.normalized() : GetTransform().basis[1];
 | |
|       x450_bodyController->GetCommandMgr().DeliverCmd(CBCLocomotionCmd(move, face, 1.f));
 | |
|     }
 | |
|     SeparateFromMelees(mgr);
 | |
|     break;
 | |
|   }
 | |
|   default:
 | |
|     break;
 | |
|   }
 | |
| }
 | |
| 
 | |
| void CBeetle::TargetPlayer(CStateManager& mgr, EStateMsg msg, float dt) {
 | |
|   if (msg == EStateMsg::Activate) {
 | |
|     if (CTeamAiRole* role = CTeamAiMgr::GetTeamAiRole(mgr, x570_aiMgr, GetUniqueId()))
 | |
|       x2e0_destPos = role->GetTeamPosition();
 | |
|     else
 | |
|       x2e0_destPos = mgr.GetPlayer().GetTranslation();
 | |
|     x2dc_destObj = mgr.GetPlayer().GetUniqueId();
 | |
|     x2ec_reflectedDestPos = GetTranslation();
 | |
|     x328_24_inPosition = false;
 | |
|   }
 | |
| }
 | |
| 
 | |
| void CBeetle::Generate(CStateManager& mgr, EStateMsg msg, float dt) {
 | |
|   switch (msg) {
 | |
|   case EStateMsg::Activate:
 | |
|     if (x56c_entranceType == EEntranceType::FacePlayer || x450_bodyController->GetActive()) {
 | |
|       x450_bodyController->GetCommandMgr().DeliverCmd(CBCGenerateCmd(pas::EGenerateType::Zero));
 | |
|       SetTransform(zeus::lookAt(GetTranslation(), mgr.GetPlayer().GetTranslation()));
 | |
|     } else {
 | |
|       x450_bodyController->GetCommandMgr().DeliverCmd(CBCGenerateCmd(pas::EGenerateType::Zero, x388_anim));
 | |
|     }
 | |
|     if (!x450_bodyController->GetActive())
 | |
|       x450_bodyController->Activate(mgr);
 | |
|     RemoveMaterial(EMaterialTypes::Character, EMaterialTypes::Solid, mgr);
 | |
|     x568_stateProg = 0;
 | |
|     break;
 | |
|   case EStateMsg::Update:
 | |
|     switch (x568_stateProg) {
 | |
|     case 0:
 | |
|       if (x450_bodyController->GetCurrentStateId() == pas::EAnimationState::Generate) {
 | |
|         x568_stateProg = 2;
 | |
|         x5a8_animTimeRem = x450_bodyController->GetAnimTimeRemaining();
 | |
|       } else {
 | |
|         x450_bodyController->GetCommandMgr().DeliverCmd(CBCGenerateCmd(pas::EGenerateType::Zero));
 | |
|       }
 | |
|       break;
 | |
|     case 2:
 | |
|       if (x450_bodyController->GetCurrentStateId() != pas::EAnimationState::Generate) {
 | |
|         x568_stateProg = 4;
 | |
|       } else if (x68_material.HasMaterial(EMaterialTypes::Solid) && x5a8_animTimeRem > 0.f) {
 | |
|         rstl::reserved_vector<TUniqueId, 1024> nearList;
 | |
|         mgr.BuildNearList(nearList, zeus::CAABox(GetTranslation() - 5.f, GetTranslation() + 5.f),
 | |
|                           CMaterialFilter::MakeInclude({EMaterialTypes::Solid}), this);
 | |
|         if (!nearList.empty()) {
 | |
|           zeus::CVector3f totalSep;
 | |
|           float sepFactor = 5.f * dt / x5a8_animTimeRem;
 | |
|           for (TUniqueId id : nearList) {
 | |
|             if (CActor* act = static_cast<CActor*>(mgr.ObjectById(id))) {
 | |
|               zeus::CVector3f sep = x45c_steeringBehaviors.Separation(*this, act->GetTranslation(), 5.f);
 | |
|               sep.z() = std::max(0.f, float(sep.z()));
 | |
|               totalSep += sep * sepFactor;
 | |
|             }
 | |
|           }
 | |
|           SetTranslation(GetTranslation() + totalSep);
 | |
|         }
 | |
|       }
 | |
|       break;
 | |
|     default:
 | |
|       break;
 | |
|     }
 | |
|     break;
 | |
|   case EStateMsg::Deactivate:
 | |
|     AddMaterial(EMaterialTypes::Character, EMaterialTypes::Solid, EMaterialTypes::GroundCollider, mgr);
 | |
|     x328_25_verticalMovement = false;
 | |
|     x838_25_burrowing = false;
 | |
|     if (x328_26_solidCollision)
 | |
|       DeathDelete(mgr);
 | |
|     break;
 | |
|   default:
 | |
|     break;
 | |
|   }
 | |
| }
 | |
| 
 | |
| void CBeetle::Deactivate(CStateManager& mgr, EStateMsg msg, float dt) {
 | |
|   switch (msg) {
 | |
|   case EStateMsg::Activate:
 | |
|     x568_stateProg = 0;
 | |
|     SquadRemove(mgr);
 | |
|     x818_stateFinishTimer = 0.f;
 | |
|     break;
 | |
|   case EStateMsg::Update:
 | |
|     switch (x568_stateProg) {
 | |
|     case 0:
 | |
|       if (x450_bodyController->GetCurrentStateId() == pas::EAnimationState::Generate) {
 | |
|         RemoveMaterial(EMaterialTypes::Character, EMaterialTypes::Solid,
 | |
|                        EMaterialTypes::Target, EMaterialTypes::Orbit, mgr);
 | |
|         mgr.GetPlayer().SetOrbitRequestForTarget(GetUniqueId(), CPlayer::EPlayerOrbitRequest::ActivateOrbitSource, mgr);
 | |
|         x838_25_burrowing = true;
 | |
|         x5a8_animTimeRem = x450_bodyController->GetAnimTimeRemaining();
 | |
|         x568_stateProg = 2;
 | |
|       } else if (IsOnGround()) {
 | |
|         x450_bodyController->GetCommandMgr().DeliverCmd(CBCGenerateCmd(pas::EGenerateType::One));
 | |
|       } else {
 | |
|         x450_bodyController->GetCommandMgr().DeliverCmd(CBCLocomotionCmd(
 | |
|           x45c_steeringBehaviors.Seek(*this, mgr.GetPlayer().GetTranslation()), zeus::CVector3f::skZero, 1.f));
 | |
|       }
 | |
|       break;
 | |
|     case 2:
 | |
|       if (x450_bodyController->GetCurrentStateId() != pas::EAnimationState::Generate) {
 | |
|         x568_stateProg = 3;
 | |
|         x830_intoGroundFactor = 0.f;
 | |
|         auto aabb = GetBoundingBox();
 | |
|         SetTranslation((aabb.max.z() - aabb.min.z()) * 3.f * zeus::CVector3f::skDown + GetTranslation());
 | |
|       } else {
 | |
|         float remTime = x450_bodyController->GetAnimTimeRemaining();
 | |
|         x830_intoGroundFactor = x5a8_animTimeRem > 0.f ? remTime / x5a8_animTimeRem : 0.f;
 | |
|       }
 | |
|       break;
 | |
|     case 3:
 | |
|       x818_stateFinishTimer += dt;
 | |
|       if (x818_stateFinishTimer >= 0.75f) {
 | |
|         SendScriptMsgs(EScriptObjectState::DeactivateState, mgr, EScriptObjectMessage::None);
 | |
|         mgr.FreeScriptObject(GetUniqueId());
 | |
|         x568_stateProg = 4;
 | |
|         x830_intoGroundFactor = 0.f;
 | |
|       } else {
 | |
|         auto aabb = GetBoundingBox();
 | |
|         SetTranslation((aabb.max.z() - aabb.min.z()) * 4.f * zeus::CVector3f::skDown * dt + GetTranslation());
 | |
|       }
 | |
|       break;
 | |
|     default:
 | |
|       break;
 | |
|     }
 | |
|   default:
 | |
|     break;
 | |
|   }
 | |
| }
 | |
| 
 | |
| void CBeetle::Attack(CStateManager& mgr, EStateMsg msg, float dt) {
 | |
|   switch (msg) {
 | |
|   case EStateMsg::Activate:
 | |
|     x838_26_canSkid = false;
 | |
|     x838_24_hitSomething = false;
 | |
|     x2e0_destPos = mgr.GetPlayer().GetTranslation();
 | |
|     x2e0_destPos.z() = GetTranslation().z();
 | |
|     x568_stateProg = 0;
 | |
|     break;
 | |
|   case EStateMsg::Update:
 | |
|     switch (x568_stateProg) {
 | |
|     case 0:
 | |
|       if (x450_bodyController->GetCurrentStateId() == pas::EAnimationState::MeleeAttack) {
 | |
|         x568_stateProg = 1;
 | |
|         x838_26_canSkid = true;
 | |
|       } else if (IsOnGround()) {
 | |
|         zeus::CVector3f aimPos = mgr.GetPlayer().GetAimPosition(mgr, 0.f);
 | |
|         aimPos.z() = GetTranslation().z();
 | |
|         zeus::CVector3f aimDelta = aimPos - GetTranslation();
 | |
|         aimDelta.z() = 0.f;
 | |
|         if (aimDelta.canBeNormalized())
 | |
|           aimPos += aimDelta.normalized() * 5.f;
 | |
|         x450_bodyController->GetCommandMgr().DeliverCmd(CBCMeleeAttackCmd(pas::ESeverity::One, aimPos));
 | |
|       }
 | |
|       break;
 | |
|     case 1:
 | |
|       if (x450_bodyController->GetCurrentStateId() != pas::EAnimationState::MeleeAttack) {
 | |
|         x568_stateProg = 4;
 | |
|       } else if (IsOnGround()) {
 | |
|         x450_bodyController->GetCommandMgr().DeliverTargetVector(x2e0_destPos - GetTranslation());
 | |
|       } else {
 | |
|         x568_stateProg = 2;
 | |
|       }
 | |
|       break;
 | |
|     case 2:
 | |
|       if (x450_bodyController->GetCurrentStateId() != pas::EAnimationState::MeleeAttack)
 | |
|         x568_stateProg = 4;
 | |
|       break;
 | |
|     default:
 | |
|       break;
 | |
|     }
 | |
|     if (x328_26_solidCollision)
 | |
|       x838_24_hitSomething = true;
 | |
|     break;
 | |
|   case EStateMsg::Deactivate:
 | |
|     CTeamAiMgr::ResetTeamAiRole(CTeamAiMgr::EAttackType::Melee, mgr, x570_aiMgr, GetUniqueId(), true);
 | |
|     x814_attackDelayTimer = mgr.GetActiveRandom()->Float() * x308_attackTimeVariation + x304_averageAttackTime;
 | |
|     break;
 | |
|   }
 | |
| }
 | |
| 
 | |
| void CBeetle::JumpBack(CStateManager&, EStateMsg msg, float dt) {
 | |
|   switch (msg) {
 | |
|   case EStateMsg::Activate:
 | |
|     x568_stateProg = 0;
 | |
|     break;
 | |
|   case EStateMsg::Update:
 | |
|     switch (x568_stateProg) {
 | |
|     case 0:
 | |
|       if (x450_bodyController->GetCurrentStateId() == pas::EAnimationState::Step) {
 | |
|         x568_stateProg = 2;
 | |
|       } else if (IsOnGround()) {
 | |
|         x450_bodyController->GetCommandMgr().DeliverCmd(
 | |
|           CBCStepCmd(pas::EStepDirection::Backward, pas::EStepType::Normal));
 | |
|       }
 | |
|       break;
 | |
|     case 2:
 | |
|       if (x450_bodyController->GetCurrentStateId() != pas::EAnimationState::Step)
 | |
|         x568_stateProg = 4;
 | |
|       break;
 | |
|     default:
 | |
|       break;
 | |
|     }
 | |
|     break;
 | |
|   default:
 | |
|     break;
 | |
|   }
 | |
| }
 | |
| 
 | |
| void CBeetle::DoubleSnap(CStateManager&, EStateMsg msg, float dt) {
 | |
|   switch (msg) {
 | |
|   case EStateMsg::Activate:
 | |
|     x568_stateProg = 0;
 | |
|     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::Zero));
 | |
|       }
 | |
|       break;
 | |
|     case 2:
 | |
|       if (x450_bodyController->GetCurrentStateId() != pas::EAnimationState::MeleeAttack)
 | |
|         x568_stateProg = 4;
 | |
|       else
 | |
|         x450_bodyController->GetCommandMgr().DeliverTargetVector(x2e0_destPos - GetTranslation());
 | |
|       break;
 | |
|     default:
 | |
|       break;
 | |
|     }
 | |
|     break;
 | |
|   default:
 | |
|     break;
 | |
|   }
 | |
| }
 | |
| 
 | |
| void CBeetle::Shuffle(CStateManager& mgr, EStateMsg msg, float dt) {
 | |
|   switch (msg) {
 | |
|   case EStateMsg::Activate:
 | |
|     x328_24_inPosition = false;
 | |
|     break;
 | |
|   case EStateMsg::Update: {
 | |
|     zeus::CVector3f playerPos = mgr.GetPlayer().GetTranslation();
 | |
|     zeus::CVector3f playerToThis = GetTranslation() - playerPos;
 | |
|     float midRange = 0.5f * (x2fc_minAttackRange + x300_maxAttackRange);
 | |
|     zeus::CVector3f playerLimit;
 | |
|     if (playerToThis.canBeNormalized())
 | |
|       playerLimit = playerToThis.normalized() * midRange + playerPos;
 | |
|     else
 | |
|       playerLimit = GetTransform().basis[1] * midRange + playerPos;
 | |
|     if ((GetTranslation() - playerLimit).magSquared() > 4.f) {
 | |
|       pas::EStepDirection dir = GetStepDirection(-playerToThis);
 | |
|       switch (dir) {
 | |
|       case pas::EStepDirection::Forward:
 | |
|       case pas::EStepDirection::Backward: {
 | |
|         zeus::CVector3f move = x45c_steeringBehaviors.Arrival(*this, x2e0_destPos, x300_maxAttackRange);
 | |
|         if (GetTransform().basis[1].dot(move) >= 0.f || GetTransform().basis[1].dot(playerToThis) >= 0.f) {
 | |
|           x450_bodyController->GetCommandMgr().DeliverCmd(CBCLocomotionCmd(move, zeus::CVector3f::skZero, 1.f));
 | |
|         } else {
 | |
|           x450_bodyController->GetCommandMgr().DeliverCmd(CBCLocomotionCmd(move,
 | |
|           playerToThis.canBeNormalized() ? -playerToThis.normalized() : GetTransform().basis[1], 1.f));
 | |
|         }
 | |
|         break;
 | |
|       }
 | |
|       case pas::EStepDirection::Left:
 | |
|       case pas::EStepDirection::Right: {
 | |
|         x450_bodyController->GetCommandMgr().DeliverCmd(CBCStepCmd(dir, pas::EStepType::Normal));
 | |
|         break;
 | |
|       }
 | |
|       default:
 | |
|         break;
 | |
|       }
 | |
|       x450_bodyController->GetCommandMgr().DeliverTargetVector(-playerToThis);
 | |
|     } else {
 | |
|       x328_24_inPosition = true;
 | |
|     }
 | |
|     break;
 | |
|   }
 | |
|   default:
 | |
|     break;
 | |
|   }
 | |
| }
 | |
| 
 | |
| void CBeetle::TurnAround(CStateManager& mgr, EStateMsg msg, float dt) {
 | |
|   switch (msg) {
 | |
|   case EStateMsg::Activate:
 | |
|     x568_stateProg = 0;
 | |
|     break;
 | |
|   case EStateMsg::Update:
 | |
|     switch (x568_stateProg) {
 | |
|     case 0:
 | |
|       if (x450_bodyController->GetCurrentStateId() == pas::EAnimationState::Turn) {
 | |
|         x568_stateProg = 2;
 | |
|       } else {
 | |
|         zeus::CVector3f thisToPlayer = mgr.GetPlayer().GetTranslation() - GetTranslation();
 | |
|         x450_bodyController->GetCommandMgr().DeliverCmd(CBCLocomotionCmd(zeus::CVector3f::skZero,
 | |
|         (thisToPlayer.magnitude() > FLT_EPSILON) ? thisToPlayer.normalized() : zeus::CVector3f::skZero, 1.f));
 | |
|       }
 | |
|       break;
 | |
|     case 2:
 | |
|       if (x450_bodyController->GetCurrentStateId() != pas::EAnimationState::Turn)
 | |
|         x568_stateProg = 4;
 | |
|       break;
 | |
|     default:
 | |
|       break;
 | |
|     }
 | |
|     break;
 | |
|   default:
 | |
|     break;
 | |
|   }
 | |
| }
 | |
| 
 | |
| void CBeetle::Skid(CStateManager&, EStateMsg msg, float dt) {
 | |
|   switch (msg) {
 | |
|   case EStateMsg::Activate:
 | |
|     if (IsOnGround() && x838_26_canSkid) {
 | |
|       x450_bodyController->GetCommandMgr().DeliverCmd(CBCSlideCmd(pas::ESlideType::Zero, GetTransform().basis[1]));
 | |
|       x568_stateProg = 2;
 | |
|     }
 | |
|     break;
 | |
|   case EStateMsg::Update:
 | |
|     if (x450_bodyController->GetCurrentStateId() != pas::EAnimationState::Slide)
 | |
|       x568_stateProg = 4;
 | |
|     break;
 | |
|   case EStateMsg::Deactivate:
 | |
|     x838_26_canSkid = false;
 | |
|     break;
 | |
|   default:
 | |
|     break;
 | |
|   }
 | |
| }
 | |
| 
 | |
| void CBeetle::Taunt(CStateManager& mgr, EStateMsg msg, float dt) {
 | |
|   switch (msg) {
 | |
|   case EStateMsg::Activate:
 | |
|     x450_bodyController->GetCommandMgr().DeliverCmd(
 | |
|       CBCTauntCmd(mgr.GetActiveRandom()->Float() < 0.75f ? pas::ETauntType::One : pas::ETauntType::Zero));
 | |
|     x568_stateProg = 2;
 | |
|     break;
 | |
|   case EStateMsg::Update:
 | |
|     if (x450_bodyController->GetCurrentStateId() != pas::EAnimationState::Taunt)
 | |
|       x568_stateProg = 4;
 | |
|     else
 | |
|       x450_bodyController->GetCommandMgr().DeliverTargetVector(mgr.GetPlayer().GetTranslation() - GetTranslation());
 | |
|     break;
 | |
|   default:
 | |
|     break;
 | |
|   }
 | |
| }
 | |
| 
 | |
| s32 CBeetle::FindFurthestRetreatPoint(CStateManager& mgr) const {
 | |
|   s32 ret = -1;
 | |
|   if (x6e0_retreatPoints.empty())
 | |
|     return ret;
 | |
|   zeus::CVector2f playerPos = mgr.GetPlayer().GetTranslation().toVec2f();
 | |
|   ret = mgr.GetActiveRandom()->Range(0, s32(x6e0_retreatPoints.size() - 1));
 | |
|   float maxDist = (playerPos - x6e0_retreatPoints[ret].toVec2f()).magSquared();
 | |
|   if (maxDist < 100.f) {
 | |
|     s32 i = 0;
 | |
|     for (const auto& v : x6e0_retreatPoints) {
 | |
|       float dist = (playerPos - v.toVec2f()).magSquared();
 | |
|       if (dist > maxDist) {
 | |
|         maxDist = dist;
 | |
|         ret = i;
 | |
|       }
 | |
|       ++i;
 | |
|     }
 | |
|   }
 | |
|   return ret;
 | |
| }
 | |
| 
 | |
| void CBeetle::Retreat(CStateManager& mgr, EStateMsg msg, float dt) {
 | |
|   switch (msg) {
 | |
|   case EStateMsg::Activate:
 | |
|     x568_stateProg = 0;
 | |
|     SquadRemove(mgr);
 | |
|     x818_stateFinishTimer = 0.f;
 | |
|     x3b4_speed = 2.f * x81c_;
 | |
|     break;
 | |
|   case EStateMsg::Update:
 | |
|     switch (x568_stateProg) {
 | |
|     case 0:
 | |
|       if (x450_bodyController->GetCurrentStateId() == pas::EAnimationState::Generate) {
 | |
|         RemoveMaterial(EMaterialTypes::Character, EMaterialTypes::Solid,
 | |
|                        EMaterialTypes::Target, EMaterialTypes::Orbit, mgr);
 | |
|         mgr.GetPlayer().SetOrbitRequestForTarget(GetUniqueId(),
 | |
|           CPlayer::EPlayerOrbitRequest::ActivateOrbitSource, mgr);
 | |
|         x838_25_burrowing = true;
 | |
|         x5a8_animTimeRem = x450_bodyController->GetAnimTimeRemaining();
 | |
|         x568_stateProg = 2;
 | |
|       } else if (IsOnGround()) {
 | |
|         x450_bodyController->GetCommandMgr().DeliverCmd(CBCGenerateCmd(pas::EGenerateType::One));
 | |
|       } else {
 | |
|         x450_bodyController->GetCommandMgr().DeliverCmd(CBCLocomotionCmd(
 | |
|         x45c_steeringBehaviors.Seek(*this, mgr.GetPlayer().GetTranslation()), zeus::CVector3f::skZero, 1.f));
 | |
|       }
 | |
|       break;
 | |
|     case 2:
 | |
|       if (x450_bodyController->GetCurrentStateId() != pas::EAnimationState::Generate) {
 | |
|         x568_stateProg = 3;
 | |
|         x830_intoGroundFactor = 0.f;
 | |
|         auto aabb = GetBoundingBox();
 | |
|         SetTranslation((aabb.max.z() - aabb.min.z()) * 3.f * zeus::CVector3f::skDown + GetTranslation());
 | |
|       } else {
 | |
|         float remTime = x450_bodyController->GetAnimTimeRemaining();
 | |
|         x830_intoGroundFactor = (x5a8_animTimeRem > 0.f) ? remTime / x5a8_animTimeRem : 0.f;
 | |
|       }
 | |
|       break;
 | |
|     case 3:
 | |
|       x818_stateFinishTimer += dt;
 | |
|       if (x818_stateFinishTimer >= x834_retreatTime) {
 | |
|         x568_stateProg = 4;
 | |
|         x830_intoGroundFactor = 0.f;
 | |
|       } else {
 | |
|         auto aabb = GetBoundingBox();
 | |
|         zeus::CVector3f downVec = (aabb.max.z() - aabb.min.z()) * 3.f * zeus::CVector3f::skDown;
 | |
|         SetTranslation(((x834_retreatTime > 0.f) ?
 | |
|                        (1.f / x834_retreatTime) * downVec : downVec) * dt + GetTranslation());
 | |
|       }
 | |
|       break;
 | |
|     default:
 | |
|       break;
 | |
|     }
 | |
|     break;
 | |
|   case EStateMsg::Deactivate: {
 | |
|     s32 idx = FindFurthestRetreatPoint(mgr);
 | |
|     if (idx != -1) {
 | |
|       SetTranslation(x6e0_retreatPoints[idx]);
 | |
|       AddMaterial(EMaterialTypes::Character, EMaterialTypes::Solid,
 | |
|                   EMaterialTypes::Target, EMaterialTypes::Orbit, mgr);
 | |
|     } else {
 | |
|       SendScriptMsgs(EScriptObjectState::DeactivateState, mgr, EScriptObjectMessage::None);
 | |
|       mgr.FreeScriptObject(GetUniqueId());
 | |
|     }
 | |
|     x400_24_hitByPlayerProjectile = false;
 | |
|     x3b4_speed = x81c_;
 | |
|     x838_25_burrowing = false;
 | |
|     break;
 | |
|   }
 | |
|   }
 | |
| }
 | |
| 
 | |
| bool CBeetle::InAttackPosition(CStateManager& mgr, float arg) {
 | |
|   float distSq = (mgr.GetPlayer().GetTranslation() - GetTranslation()).magSquared();
 | |
|   if (distSq > x2fc_minAttackRange * x2fc_minAttackRange && distSq < x300_maxAttackRange * x300_maxAttackRange)
 | |
|     if (SpotPlayer(mgr, x3c8_leashRadius))
 | |
|       return true;
 | |
|   return false;
 | |
| }
 | |
| 
 | |
| bool CBeetle::PathShagged(CStateManager&, float arg) {
 | |
|   return false;
 | |
| }
 | |
| 
 | |
| bool CBeetle::InRange(CStateManager& mgr, float arg) {
 | |
|   zeus::CVector3f targetPos = mgr.GetPlayer().GetTranslation();
 | |
|   if (CTeamAiRole* role = CTeamAiMgr::GetTeamAiRole(mgr, x570_aiMgr, GetUniqueId())) {
 | |
|     if (role->GetTeamAiRole() == CTeamAiRole::ETeamAiRole::Melee)
 | |
|       targetPos = role->GetTeamPosition();
 | |
|   }
 | |
|   return (targetPos - GetTranslation()).magSquared() < 100.f;
 | |
| }
 | |
| 
 | |
| bool CBeetle::PatternOver(CStateManager& mgr, float arg) {
 | |
|   return AnimOver(mgr, arg);
 | |
| }
 | |
| 
 | |
| bool CBeetle::HasAttackPattern(CStateManager&, float arg) {
 | |
|   return true;
 | |
| }
 | |
| 
 | |
| bool CBeetle::AnimOver(CStateManager&, float arg) {
 | |
|   return x568_stateProg == 4;
 | |
| }
 | |
| 
 | |
| bool CBeetle::ShouldAttack(CStateManager& mgr, float arg) {
 | |
|   if (x814_attackDelayTimer <= 0.f) {
 | |
|     if (TCastToPtr<CTeamAiMgr> tmgr = mgr.ObjectById(x570_aiMgr)) {
 | |
|       if (tmgr->HasTeamAiRole(GetUniqueId()))
 | |
|         return tmgr->AddMeleeAttacker(GetUniqueId());
 | |
|     }
 | |
|     return true;
 | |
|   }
 | |
|   return false;
 | |
| }
 | |
| 
 | |
| bool CBeetle::ShouldDoubleSnap(CStateManager& mgr, float arg) {
 | |
|   if (!GetSearchPath() && IsOnGround()) {
 | |
|     zeus::CVector3f targetPos = mgr.GetPlayer().GetTranslation();
 | |
|     float dist = x5a0_headbuttDist + x300_maxAttackRange;
 | |
|     if (CTeamAiRole* role = CTeamAiMgr::GetTeamAiRole(mgr, x570_aiMgr, GetUniqueId()))
 | |
|       targetPos = role->GetTeamPosition();
 | |
|     zeus::CVector3f delta = targetPos - GetTranslation();
 | |
|     if (delta.magSquared() > dist * dist && GetTransform().basis[1].dot(delta.normalized()) > 0.98f) {
 | |
|       rstl::reserved_vector<TUniqueId, 1024> nearList;
 | |
|       mgr.BuildNearList(nearList, GetTranslation(), GetTransform().basis[1], x5a0_headbuttDist,
 | |
|         CMaterialFilter::MakeInclude({EMaterialTypes::Character}), this);
 | |
|       TUniqueId bestId = kInvalidUniqueId;
 | |
|       CRayCastResult res = mgr.RayWorldIntersection(bestId, GetTranslation(), GetTransform().basis[1],
 | |
|         x5a0_headbuttDist, CMaterialFilter::MakeInclude({EMaterialTypes::Solid}), nearList);
 | |
|       if (res.IsInvalid())
 | |
|         return true;
 | |
|     }
 | |
|   }
 | |
|   return false;
 | |
| }
 | |
| 
 | |
| bool CBeetle::ShouldTurn(CStateManager& mgr, float arg) {
 | |
|   return zeus::CVector2f::getAngleDiff(GetTransform().basis[1].toVec2f(),
 | |
|     (mgr.GetPlayer().GetTranslation() - GetTranslation()).toVec2f()) > zeus::degToRad(30.f);
 | |
| }
 | |
| 
 | |
| bool CBeetle::HitSomething(CStateManager&, float arg) {
 | |
|   return x838_24_hitSomething;
 | |
| }
 | |
| 
 | |
| bool CBeetle::ShouldJumpBack(CStateManager& mgr, float arg) {
 | |
|   zeus::CVector3f backDir = -GetTransform().basis[1];
 | |
|   const auto& aabb = GetBaseBoundingBox();
 | |
|   zeus::CVector3f pos = GetTranslation() + zeus::CVector3f(0.f, 0.f, (aabb.max.z() - aabb.min.z()) * 0.5f);
 | |
|   rstl::reserved_vector<TUniqueId, 1024> nearList;
 | |
|   mgr.BuildNearList(nearList, pos, backDir, x5a4_jumpBackwardDist,
 | |
|     CMaterialFilter::MakeInclude({EMaterialTypes::Character}), this);
 | |
|   TUniqueId bestId = kInvalidUniqueId;
 | |
|   CRayCastResult res = mgr.RayWorldIntersection(bestId, pos, backDir, x5a4_jumpBackwardDist,
 | |
|     CMaterialFilter::MakeInclude({EMaterialTypes::Solid}), nearList);
 | |
|   return res.IsInvalid();
 | |
| }
 | |
| 
 | |
| bool CBeetle::Stuck(CStateManager&, float arg) {
 | |
|   return x820_posDeviationCounter > 30;
 | |
| }
 | |
| 
 | |
| bool CBeetle::NoPathNodes(CStateManager&, float arg) {
 | |
|   return false;
 | |
| }
 | |
| 
 | |
| bool CBeetle::ShouldTaunt(CStateManager& mgr, float arg) {
 | |
|   if (CTeamAiRole* role = CTeamAiMgr::GetTeamAiRole(mgr, x570_aiMgr, GetUniqueId())) {
 | |
|     if (role->GetTeamAiRole() == CTeamAiRole::ETeamAiRole::Unknown ||
 | |
|         role->GetTeamAiRole() == CTeamAiRole::ETeamAiRole::Unassigned) {
 | |
|       return (role->GetTeamPosition() - GetTranslation()).magSquared() < 100.f;
 | |
|     }
 | |
|   }
 | |
|   return false;
 | |
| }
 | |
| 
 | |
| bool CBeetle::ShotAt(CStateManager&, float arg) {
 | |
|   if (x3fc_flavor == EFlavorType::Two && x6e0_retreatPoints.size() > 0)
 | |
|     return x400_24_hitByPlayerProjectile;
 | |
|   return false;
 | |
| }
 | |
| 
 | |
| void CBeetle::Burn(float duration, float damage) {
 | |
|   CDamageVulnerability dVuln = *GetDamageVulnerability();
 | |
|   if (!x838_25_burrowing && x3fc_flavor == EFlavorType::One)
 | |
|     dVuln = x7ac_tailVuln;
 | |
|   switch (dVuln.GetVulnerability(CWeaponMode(EWeaponType::Wave), false)) {
 | |
|   case EVulnerability::Weak:
 | |
|     x450_bodyController->SetOnFire(1.5f * duration);
 | |
|     x3ec_pendingFireDamage = 1.5f * damage;
 | |
|     break;
 | |
|   case EVulnerability::Normal:
 | |
|     x450_bodyController->SetOnFire(duration);
 | |
|     x3ec_pendingFireDamage = damage;
 | |
|     break;
 | |
|   default:
 | |
|     break;
 | |
|   }
 | |
| }
 | |
| 
 | |
| void CBeetle::Shock(CStateManager& mgr, float duration, float damage) {
 | |
|   CDamageVulnerability dVuln = *GetDamageVulnerability();
 | |
|   if (!x838_25_burrowing && x3fc_flavor == EFlavorType::One)
 | |
|     dVuln = x7ac_tailVuln;
 | |
|   switch (dVuln.GetVulnerability(CWeaponMode(EWeaponType::Wave), false)) {
 | |
|   case EVulnerability::Weak:
 | |
|     x450_bodyController->SetElectrocuting(1.5f * duration);
 | |
|     x3f0_pendingShockDamage = 1.5f * damage;
 | |
|     break;
 | |
|   case EVulnerability::Normal:
 | |
|     x450_bodyController->SetElectrocuting(duration);
 | |
|     x3f0_pendingShockDamage = damage;
 | |
|     break;
 | |
|   default:
 | |
|     break;
 | |
|   }
 | |
| }
 | |
| 
 | |
| CPathFindSearch* CBeetle::GetSearchPath() {
 | |
|   return &x5fc_pathFindSearch;
 | |
| }
 | |
| 
 | |
| float CBeetle::GetGravityConstant() const {
 | |
|   return 4.f * 24.525f;
 | |
| }
 | |
| 
 | |
| } // namespace urde::MP1
 |