mirror of
				https://github.com/AxioDL/metaforce.git
				synced 2025-10-27 15:30:23 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			325 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			325 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| #include "CSpankWeed.hpp"
 | |
| #include "World/CPatternedInfo.hpp"
 | |
| #include "World/CPlayer.hpp"
 | |
| #include "Collision/CCollisionActor.hpp"
 | |
| #include "CStateManager.hpp"
 | |
| #include <logvisor/logvisor.hpp>
 | |
| 
 | |
| namespace urde::MP1 {
 | |
| logvisor::Module SpankLog("urde::MP1::SpankWeed");
 | |
| CSpankWeed::CSpankWeed(TUniqueId uid, std::string_view name, const CEntityInfo& info, const zeus::CTransform& xf,
 | |
|                        CModelData&& mData, const CActorParameters& actParms, const CPatternedInfo& pInfo,
 | |
|                        float maxDetectionRange, float maxHearingRange, float maxSightRange, float hideTime)
 | |
| : CPatterned(ECharacter::SpankWeed, uid, name, EFlavorType::Zero, info, xf, std::move(mData), pInfo,
 | |
|              EMovementType::Flyer, EColliderType::One, EBodyType::Restricted, actParms, EKnockBackVariant::Medium)
 | |
| , x568_maxDetectionRange(maxDetectionRange)
 | |
| , x56c_detectionHeightRange(pInfo.GetDetectionHeightRange())
 | |
| , x570_maxHearingRange(maxHearingRange)
 | |
| , x574_maxSightRange(maxSightRange)
 | |
| , x578_hideTime(hideTime)
 | |
| , x584_retreatOrigin(xf.origin) {
 | |
|   SetCallTouch(false);
 | |
|   CreateShadow(false);
 | |
| 
 | |
|   zeus::CVector3f modelScale = GetModelData()->GetScale();
 | |
|   if (modelScale.x() != modelScale.y() || modelScale.x() != modelScale.z()) {
 | |
|     float scale = modelScale.magnitude() / std::sqrt(3.f);
 | |
| 
 | |
|     ModelData()->SetScale(zeus::CVector3f(scale));
 | |
|     zeus::simd_floats scaleF(modelScale.mSimd);
 | |
|     SpankLog.report(logvisor::Level::Warning,
 | |
|                     "WARNING: Non-uniform scale (%.2f, %.2f, %.2f) applied to Spank Weed"
 | |
|                     "...changing scale to (%.2f, %.2f, %.2f)\n",
 | |
|                     scaleF[0], scaleF[1], scaleF[2], scale, scale, scale);
 | |
|   }
 | |
|   CMaterialList list = GetMaterialFilter().GetExcludeList();
 | |
|   list.Add(EMaterialTypes::Character);
 | |
|   list.Add(EMaterialTypes::Player);
 | |
|   SetMaterialFilter(CMaterialFilter::MakeIncludeExclude(GetMaterialFilter().GetIncludeList(), list));
 | |
| 
 | |
|   CSegId segId = GetModelData()->GetAnimationData()->GetLocatorSegId("lockon_target_LCTR"sv);
 | |
|   if (segId != 0xFF) {
 | |
|     zeus::CTransform locatorXf = GetTransform() * zeus::CTransform::Scale(GetModelData()->GetScale()) *
 | |
|                                  GetModelData()->GetAnimationData()->GetLocatorTransform(segId, nullptr);
 | |
|     x5a8_lockonTarget = locatorXf.origin;
 | |
|     x59c_lockonOffset = locatorXf.origin - GetTranslation();
 | |
|   }
 | |
|   x460_knockBackController.SetAutoResetImpulse(false);
 | |
| }
 | |
| 
 | |
| static const SSphereJointInfo kArmCollision[] = {{"Arm_4", 1.5f},      {"Arm_6", 1.f}, {"Arm_7", 1.f},
 | |
|                                                         {"Arm_8", 1.f},       {"Arm_9", 1.f}, {"Arm_11", 1.f},
 | |
|                                                         {"Swoosh_LCTR", 1.5f}};
 | |
| 
 | |
| void CSpankWeed::AcceptScriptMsg(EScriptObjectMessage msg, TUniqueId uid, CStateManager& mgr) {
 | |
|   bool oldActive = GetActive();
 | |
|   if (msg == EScriptObjectMessage::Activate) {
 | |
|     if (x90_actorLights)
 | |
|       x90_actorLights->SetDirty();
 | |
|   } else if (msg == EScriptObjectMessage::Decrement) {
 | |
|     if (x5b4_ != 0 && x5b4_ != 5 && x5b4_ != 6 && x5b4_ != 4) {
 | |
|       x400_24_hitByPlayerProjectile = true;
 | |
|       x428_damageCooldownTimer = x424_damageWaitTime;
 | |
|     }
 | |
|   } else if (msg == EScriptObjectMessage::Registered) {
 | |
|     if (!x450_bodyController->GetActive()) {
 | |
|       x450_bodyController->Activate(mgr);
 | |
|       zeus::CVector3f extents = GetBoundingBox().extents();
 | |
| 
 | |
|       SetBoundingBox({-extents, extents});
 | |
|     }
 | |
| 
 | |
|     std::vector<CJointCollisionDescription> joints;
 | |
|     joints.reserve(12);
 | |
| 
 | |
|     for (const SSphereJointInfo& joint : kArmCollision) {
 | |
|       CSegId id = GetModelData()->GetAnimationData()->GetLocatorSegId(joint.name);
 | |
|       if (id != 0xFF)
 | |
|         joints.push_back(CJointCollisionDescription::SphereCollision(id, joint.radius, joint.name, 0.001f));
 | |
|     }
 | |
| 
 | |
|     x594_collisionMgr.reset(new CCollisionActorManager(mgr, GetUniqueId(), GetAreaIdAlways(), joints, GetActive()));
 | |
|     CMaterialList list;
 | |
|     list.Add(EMaterialTypes::CameraPassthrough);
 | |
|     list.Add(EMaterialTypes::Immovable);
 | |
|     x594_collisionMgr->AddMaterial(mgr, list);
 | |
|     if (x90_actorLights) {
 | |
|       x90_actorLights->SetDirty();
 | |
|       zeus::CVector3f swooshOrigin = GetScaledLocatorTransform("swoosh_LCTR"sv).origin;
 | |
|       x90_actorLights->SetActorPositionBias(GetTransform().buildMatrix3f() * swooshOrigin);
 | |
|     }
 | |
|   } else if (msg == EScriptObjectMessage::Touched) {
 | |
|     if (TCastToPtr<CCollisionActor> colAct = mgr.ObjectById(uid)) {
 | |
|       if (TCastToPtr<CPlayer> player = mgr.ObjectById(colAct->GetLastTouchedObject())) {
 | |
|         if (x420_curDamageRemTime <= 0.f && x5b4_ != 4 && x5b4_ != 6) {
 | |
|           mgr.ApplyDamage(GetUniqueId(), player->GetUniqueId(), GetUniqueId(), GetContactDamage(),
 | |
|                           CMaterialFilter::MakeIncludeExclude({EMaterialTypes::Solid}, {}), {});
 | |
|           x420_curDamageRemTime = x424_damageWaitTime;
 | |
|         }
 | |
|       }
 | |
|     }
 | |
|   } else if (msg == EScriptObjectMessage::Deleted) {
 | |
|     mgr.FreeScriptObject(x590_);
 | |
|     x594_collisionMgr->Destroy(mgr);
 | |
|   } else if (msg == EScriptObjectMessage::SuspendedMove) {
 | |
|     x594_collisionMgr->SetMovable(mgr, false);
 | |
|   }
 | |
| 
 | |
|   CPatterned::AcceptScriptMsg(msg, uid, mgr);
 | |
| 
 | |
|   if (GetActive() != oldActive)
 | |
|     x594_collisionMgr.reset();
 | |
| }
 | |
| 
 | |
| void CSpankWeed::Think(float dt, CStateManager& mgr) {
 | |
|   if (!GetActive())
 | |
|     return;
 | |
| 
 | |
|   HealthInfo(mgr)->SetHP(1000000.0f);
 | |
| 
 | |
|   if (!x598_isHiding) {
 | |
|     zeus::CVector3f eyeOrigin = GetLocatorTransform("Eye"sv).origin;
 | |
|     MoveCollisionPrimitive(GetModelData()->GetScale() * eyeOrigin);
 | |
|     x594_collisionMgr->Update(dt, mgr, CCollisionActorManager::EUpdateOptions::ObjectSpace);
 | |
|     xe4_27_notInSortedLists = true;
 | |
|   }
 | |
| 
 | |
|   CPatterned::Think(dt, mgr);
 | |
| }
 | |
| 
 | |
| zeus::CVector3f CSpankWeed::GetOrbitPosition(const CStateManager& mgr) const {
 | |
|   zeus::CVector3f ret = CPatterned::GetOrbitPosition(mgr);
 | |
|   float delay = std::max(1.f, x330_stateMachineState.GetDelay());
 | |
|   if (x5b4_ == 3 && x5b8_ == 2) {
 | |
|     return (ret * (1.f - delay) + ((GetTranslation() + x59c_lockonOffset) * delay));
 | |
|   } else if (x5b4_ == 2 && x5b8_ == 3) {
 | |
|     return (GetTranslation() + x59c_lockonOffset) * (1.f - delay) + (ret * delay);
 | |
|   }
 | |
|   return ret;
 | |
| }
 | |
| 
 | |
| zeus::CVector3f CSpankWeed::GetAimPosition(const CStateManager&, float dt) const {
 | |
|   zeus::CVector3f pos = (dt > 0.f ? PredictMotion(dt).x0_translation : zeus::CVector3f::skZero);
 | |
| 
 | |
|   CSegId id = GetModelData()->GetAnimationData()->GetLocatorSegId("lockon_target_LCTR"sv);
 | |
|   if (id != 0xFF) {
 | |
|     zeus::CVector3f lockonOff = GetModelData()->GetAnimationData()->GetLocatorTransform(id, nullptr).origin;
 | |
|     return pos + (GetTransform() * (GetModelData()->GetScale() * lockonOff));
 | |
|   }
 | |
| 
 | |
|   return pos + GetBoundingBox().center();
 | |
| }
 | |
| 
 | |
| void CSpankWeed::Flinch(CStateManager& mgr, EStateMsg msg, float) {
 | |
|   if (msg == EStateMsg::Activate) {
 | |
|     x5bc_ = 0;
 | |
|     x5b4_ = 0;
 | |
|     RemoveMaterial(EMaterialTypes::Orbit, EMaterialTypes::Target, mgr);
 | |
|   } else if (msg == EStateMsg::Update) {
 | |
|     if (x5bc_ == 0) {
 | |
|       if (x450_bodyController->GetBodyStateInfo().GetCurrentStateId() == pas::EAnimationState::KnockBack)
 | |
|         x5bc_ = 2;
 | |
|       else
 | |
|         x450_bodyController->GetCommandMgr().DeliverCmd(CBCKnockBackCmd({}, pas::ESeverity::Zero));
 | |
|     } else if (x5bc_ == 2 &&
 | |
|                x450_bodyController->GetBodyStateInfo().GetCurrentStateId() != pas::EAnimationState::KnockBack)
 | |
|       x5bc_ = 3;
 | |
|   } else if (msg == EStateMsg::Deactivate) {
 | |
|     x5b8_ = 4;
 | |
|   }
 | |
| }
 | |
| 
 | |
| bool CSpankWeed::Delay(CStateManager&, float) {
 | |
|   if (x400_24_hitByPlayerProjectile) {
 | |
|     if (x330_stateMachineState.GetTime() > x578_hideTime) {
 | |
|       x400_24_hitByPlayerProjectile = false;
 | |
|       return true;
 | |
|     }
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|   return true;
 | |
| }
 | |
| 
 | |
| bool CSpankWeed::InRange(CStateManager& mgr, float) {
 | |
|   float playerDist = GetPlayerDistance(mgr);
 | |
|   if (x56c_detectionHeightRange > 0.f) {
 | |
|     return std::fabs(mgr.GetPlayer().GetTranslation().z() - GetTranslation().z()) < x56c_detectionHeightRange &&
 | |
|            playerDist < (x574_maxSightRange * x574_maxSightRange);
 | |
|   }
 | |
| 
 | |
|   return playerDist < (x574_maxSightRange * x574_maxSightRange);
 | |
| }
 | |
| 
 | |
| bool CSpankWeed::HearPlayer(CStateManager& mgr, float) {
 | |
|   float playerDist = GetPlayerDistance(mgr);
 | |
|   if (x56c_detectionHeightRange > 0.f) {
 | |
|     return std::fabs(mgr.GetPlayer().GetTranslation().z() - GetTranslation().z()) < x56c_detectionHeightRange &&
 | |
|            playerDist < (x570_maxHearingRange * x570_maxHearingRange);
 | |
|   }
 | |
| 
 | |
|   return playerDist < (x570_maxHearingRange * x570_maxHearingRange);
 | |
| }
 | |
| 
 | |
| bool CSpankWeed::InDetectionRange(CStateManager& mgr, float) {
 | |
|   float playerDist = GetPlayerDistance(mgr);
 | |
|   if (x56c_detectionHeightRange > 0.f) {
 | |
|     return std::fabs(mgr.GetPlayer().GetTranslation().z() - GetTranslation().z()) < x56c_detectionHeightRange &&
 | |
|            playerDist < (x568_maxDetectionRange * x568_maxDetectionRange);
 | |
|   }
 | |
| 
 | |
|   return playerDist < (x568_maxDetectionRange * x568_maxDetectionRange);
 | |
| }
 | |
| 
 | |
| void CSpankWeed::Attack(CStateManager&, EStateMsg msg, float) {
 | |
|   if (msg == EStateMsg::Activate) {
 | |
|     x450_bodyController->GetCommandMgr().DeliverCmd(CBCMeleeAttackCmd(pas::ESeverity::Zero));
 | |
|   } else if (msg == EStateMsg::Update) {
 | |
|     if (x450_bodyController->GetBodyStateInfo().GetCurrentStateId() == pas::EAnimationState::MeleeAttack)
 | |
|       return;
 | |
| 
 | |
|     x450_bodyController->GetCommandMgr().DeliverCmd(CBCMeleeAttackCmd(pas::ESeverity::Zero));
 | |
|   } else if (msg == EStateMsg::Deactivate) {
 | |
|     x5b8_ = 3;
 | |
|   }
 | |
| }
 | |
| 
 | |
| void CSpankWeed::TargetPatrol(CStateManager& mgr, EStateMsg msg, float) {
 | |
|   if (msg == EStateMsg::Activate) {
 | |
|     x450_bodyController->SetLocomotionType(pas::ELocomotionType::Combat);
 | |
|     RemoveMaterial(EMaterialTypes::Solid, mgr);
 | |
|     x5b4_ = 2;
 | |
|   } else if (msg == EStateMsg::Deactivate) {
 | |
|     x5b8_ = 2;
 | |
|   }
 | |
| }
 | |
| 
 | |
| void CSpankWeed::Lurk(CStateManager& mgr, EStateMsg msg, float) {
 | |
|   if (msg == EStateMsg::Activate) {
 | |
|     x460_knockBackController.SetEnableFreeze(true);
 | |
|     x450_bodyController->SetLocomotionType(pas::ELocomotionType::Lurk);
 | |
|     RemoveMaterial(EMaterialTypes::Solid, mgr);
 | |
|     x5b4_ = 1;
 | |
|   } else if (msg == EStateMsg::Deactivate) {
 | |
|     x5b8_ = 1;
 | |
|   }
 | |
| }
 | |
| 
 | |
| void CSpankWeed::FadeOut(CStateManager&, EStateMsg msg, float) {
 | |
|   if (msg == EStateMsg::Activate) {
 | |
|     x5bc_ = 0;
 | |
|     x57c_canKnockBack = false;
 | |
|     x5b4_ = 6;
 | |
|   } else if (msg == EStateMsg::Update) {
 | |
|     if (x5bc_ == 0) {
 | |
|       if (x450_bodyController->GetBodyStateInfo().GetCurrentStateId() == pas::EAnimationState::Step)
 | |
|         x5bc_ = 2;
 | |
|       else
 | |
|         x450_bodyController->GetCommandMgr().DeliverCmd(
 | |
|             CBCStepCmd(pas::EStepDirection::Backward, pas::EStepType::Normal));
 | |
| 
 | |
|     } else if (x5bc_ == 2) {
 | |
|       if (x450_bodyController->GetBodyStateInfo().GetCurrentStateId() != pas::EAnimationState::Step)
 | |
|         x5bc_ = 3;
 | |
|     }
 | |
|   } else if (msg == EStateMsg::Deactivate) {
 | |
|     x5b8_ = 6;
 | |
|   }
 | |
| }
 | |
| 
 | |
| void CSpankWeed::FadeIn(CStateManager& mgr, EStateMsg msg, float) {
 | |
|   if (msg == EStateMsg::Activate) {
 | |
|     x5bc_ = 0;
 | |
|     x57c_canKnockBack = true;
 | |
|     x5b4_ = 5;
 | |
|   } else if (msg == EStateMsg::Update) {
 | |
|     if (x5bc_ == 0) {
 | |
|       if (x450_bodyController->GetBodyStateInfo().GetCurrentStateId() == pas::EAnimationState::Step)
 | |
|         x5bc_ = 2;
 | |
|       else
 | |
|         x450_bodyController->GetCommandMgr().DeliverCmd(
 | |
|             CBCStepCmd(pas::EStepDirection::Forward, pas::EStepType::Normal));
 | |
| 
 | |
|     } else if (x5bc_ == 2) {
 | |
|       if (x450_bodyController->GetBodyStateInfo().GetCurrentStateId() != pas::EAnimationState::Step)
 | |
|         x5bc_ = 3;
 | |
|     }
 | |
|   } else if (msg == EStateMsg::Deactivate) {
 | |
|     x5b8_ = 5;
 | |
|   }
 | |
| 
 | |
|   xe7_28_worldLightingDirty = true;
 | |
| }
 | |
| 
 | |
| void CSpankWeed::Patrol(CStateManager& mgr, EStateMsg msg, float) {
 | |
|   if (msg == EStateMsg::Activate) {
 | |
|     x460_knockBackController.SetEnableFreeze(false);
 | |
|     x450_bodyController->SetLocomotionType(pas::ELocomotionType::Relaxed);
 | |
|     RemoveMaterial(EMaterialTypes::Solid, EMaterialTypes::Scannable, mgr);
 | |
|     RemoveMaterial(EMaterialTypes::Orbit, EMaterialTypes::Target, mgr);
 | |
|     x594_collisionMgr->SetActive(mgr, false);
 | |
|     x598_isHiding = true;
 | |
|     x5b4_ = 0;
 | |
|   } else if (msg == EStateMsg::Deactivate) {
 | |
|     AddMaterial(EMaterialTypes::Orbit, EMaterialTypes::Target, EMaterialTypes::Scannable, mgr);
 | |
|     SetTranslation(x584_retreatOrigin);
 | |
|     x594_collisionMgr->SetActive(mgr, true);
 | |
|     x598_isHiding = false;
 | |
|     x460_knockBackController.SetEnableFreeze(true);
 | |
|     x5b8_ = 0;
 | |
|   }
 | |
| }
 | |
| 
 | |
| void CSpankWeed::KnockBack(const zeus::CVector3f& backVec, CStateManager& mgr, const CDamageInfo& info,
 | |
|                            EKnockBackType type, bool inDeferred, float magnitude) {
 | |
|   if (!x57c_canKnockBack)
 | |
|     return;
 | |
|   CPatterned::KnockBack(backVec, mgr, info, type, inDeferred, magnitude);
 | |
|   x57c_canKnockBack = false;
 | |
| }
 | |
| 
 | |
| float CSpankWeed::GetPlayerDistance(CStateManager& mgr) const {
 | |
|   return (mgr.GetPlayer().GetTranslation() - x5a8_lockonTarget).magSquared();
 | |
| }
 | |
| } // namespace urde::MP1
 |