diff --git a/Runtime/MP1/World/CMetroid.cpp b/Runtime/MP1/World/CMetroid.cpp index 8adbdbff1..e52ddb1cf 100644 --- a/Runtime/MP1/World/CMetroid.cpp +++ b/Runtime/MP1/World/CMetroid.cpp @@ -1,4 +1,10 @@ #include "Runtime/MP1/World/CMetroid.hpp" + +#include "Runtime/CStateManager.hpp" +#include "Runtime/Character/CPASAnimParmData.hpp" +#include "Runtime/Weapon/CGameProjectile.hpp" +#include "Runtime/World/CTeamAiMgr.hpp" +#include "Runtime/World/CWorld.hpp" #include "Runtime/World/ScriptLoader.hpp" namespace urde::MP1 { @@ -16,13 +22,102 @@ CMetroidData::CMetroidData(CInputStream& in) xf8_animParms2 = ScriptLoader::LoadAnimationParameters(in); x108_animParms3 = ScriptLoader::LoadAnimationParameters(in); x118_animParms4 = ScriptLoader::LoadAnimationParameters(in); - x128_24_ = in.readBool(); + x128_24_startsInWall = in.readBool(); } CMetroid::CMetroid(TUniqueId uid, std::string_view name, EFlavorType flavor, const CEntityInfo& info, const zeus::CTransform& xf, CModelData&& mData, const CPatternedInfo& pInfo, - const CActorParameters& aParms, const CMetroidData& metroidData, TUniqueId) + const CActorParameters& aParms, const CMetroidData& metroidData, TUniqueId other) : CPatterned(ECharacter::Metroid, uid, name, flavor, info, xf, std::move(mData), pInfo, EMovementType::Flyer, - EColliderType::One, EBodyType::Flyer, aParms, EKnockBackVariant::Medium) {} + EColliderType::One, EBodyType::Flyer, aParms, EKnockBackVariant::Medium) +, x56c_data(metroidData) +, x6a0_collisionPrimitive(zeus::CSphere{zeus::skZero3f, 0.9f * GetModelData()->GetScale().y()}, GetMaterialList()) +, x6c0_pathFindSearch(nullptr, 3, pInfo.GetPathfindingIndex(), 1.f, 1.f) +, x7cc_animParmsidx(flavor == EFlavorType::Two ? 0 : 1) +, x7d0_scale1(GetModelData()->GetScale()) +, x7dc_scale2(GetModelData()->GetScale()) +, x7e8_scale3(GetModelData()->GetScale()) +, x81c_patternedInfo(pInfo) +, x954_actParams(aParms) +, x9bc_(other) { + x808_loopAttackDistance = + GetAnimationDistance(CPASAnimParmData{9, CPASAnimParm::FromEnum(2), CPASAnimParm::FromEnum(3)}); + UpdateTouchBounds(); + SetCoefficientOfRestitutionModifier(0.9f); + x460_knockBackController.SetX82_24(false); + x460_knockBackController.SetEnableBurn(false); + x460_knockBackController.SetEnableBurnDeath(false); + x460_knockBackController.SetEnableShock(false); + if (flavor == CPatterned::EFlavorType::Two) { + x460_knockBackController.SetEnableFreeze(false); + } + x81c_patternedInfo.SetActive(true); +} + +void CMetroid::AcceptScriptMsg(EScriptObjectMessage msg, TUniqueId uid, CStateManager& mgr) { + CPatterned::AcceptScriptMsg(msg, uid, mgr); + switch (msg) { + case EScriptObjectMessage::Registered: + x450_bodyController->Activate(mgr); + UpdateVolume(); + break; + case EScriptObjectMessage::Alert: + x9bf_24_alert = true; + break; + case EScriptObjectMessage::Deactivate: + SwarmRemove(mgr); + break; + case EScriptObjectMessage::Damage: + if (TCastToConstPtr projectile = mgr.GetObjectById(uid)) { + const CDamageInfo& damageInfo = projectile->GetDamageInfo(); + if (GetDamageVulnerability()->WeaponHits(damageInfo.GetWeaponMode(), false)) { + ApplyGrowth(damageInfo.GetDamage()); + } + } + x9bf_24_alert = true; + break; + case EScriptObjectMessage::InitializedInArea: + if (x698_teamAiMgrId == kInvalidUniqueId) { + x698_teamAiMgrId = CTeamAiMgr::GetTeamAiMgr(*this, mgr); + } + x6c0_pathFindSearch.SetArea(mgr.GetWorld()->GetAreaAlways(GetAreaIdAlways())->GetPostConstructed()->x10bc_pathArea); + break; + default: + break; + } +} + +void CMetroid::Think(float dt, CStateManager& mgr) { + if (!GetActive()) { + return; + } + if (CTeamAiMgr::GetTeamAiRole(mgr, x698_teamAiMgrId, GetUniqueId()) == nullptr) { + SwarmAdd(mgr); + } + UpdateAttackChance(mgr, dt); + SuckEnergyFromTarget(mgr, dt); + PreventWorldCollisions(mgr, dt); + UpdateTouchBounds(); + RestoreSolidCollision(mgr); + CPatterned::Think(dt, mgr); +} + +void CMetroid::DoUserAnimEvent(CStateManager& mgr, const CInt32POINode& node, EUserEventType eType, float dt) { + if (eType == EUserEventType::GenerateEnd) { + AddMaterial(EMaterialTypes::Solid, mgr); + } else { + CPatterned::DoUserAnimEvent(mgr, node, eType, dt); + } +} + +EWeaponCollisionResponseTypes CMetroid::GetCollisionResponseType(const zeus::CVector3f& vec1, + const zeus::CVector3f& vec2, const CWeaponMode& mode, + EProjectileAttrib attribute) const { + EWeaponCollisionResponseTypes types = EWeaponCollisionResponseTypes::Unknown33; + if (!GetDamageVulnerability()->WeaponHurts(mode, false) && x450_bodyController->GetPercentageFrozen() <= 0.f) { + types = EWeaponCollisionResponseTypes::Unknown58; + } + return types; +} } // namespace urde::MP1 diff --git a/Runtime/MP1/World/CMetroid.hpp b/Runtime/MP1/World/CMetroid.hpp index adbe84b62..c2035bd07 100644 --- a/Runtime/MP1/World/CMetroid.hpp +++ b/Runtime/MP1/World/CMetroid.hpp @@ -3,12 +3,18 @@ #include #include +#include "Runtime/Collision/CCollidableSphere.hpp" +#include "Runtime/World/CActorParameters.hpp" #include "Runtime/World/CAnimationParameters.hpp" +#include "Runtime/World/CPathFindSearch.hpp" #include "Runtime/World/CPatterned.hpp" +#include "Runtime/World/CPatternedInfo.hpp" +#include "Runtime/MP1/World/CSpacePirate.hpp" namespace urde::MP1 { class CMetroidData { +private: static constexpr u32 skNumProperties = 20; CDamageVulnerability x0_dVuln1; CDamageVulnerability x68_dVuln2; @@ -22,22 +28,168 @@ class CMetroidData { std::optional xf8_animParms2; std::optional x108_animParms3; std::optional x118_animParms4; - bool x128_24_ : 1; + bool x128_24_startsInWall : 1; public: explicit CMetroidData(CInputStream& in); static u32 GetNumProperties() { return skNumProperties; } + bool GetStartsInWall() { return x128_24_startsInWall; } }; class CMetroid : public CPatterned { - bool x9bf_29_ : 1 = false; +private: + enum class EState { + Invalid = -1, + Zero, + One, + Two, + Over, + } x568_state = EState::Invalid; + CMetroidData x56c_data; + TUniqueId x698_teamAiMgrId = kInvalidUniqueId; + CCollidableSphere x6a0_collisionPrimitive; + CPathFindSearch x6c0_pathFindSearch; + zeus::CVector3f x7a4_; + TUniqueId x7b0_attackTarget = kInvalidUniqueId; + float x7b4_attackChance = 0.f; + float x7b8_telegraphAttackTime = 0.f; + float x7bc_ = 0.f; + float x7c0_ = 0.f; + float x7c4_ = 0.f; + enum class EUnknown { + Zero, + One, + Two, + Three, + } x7c8_ = EUnknown::Zero; + int x7cc_animParmsidx; + zeus::CVector3f x7d0_scale1; + zeus::CVector3f x7dc_scale2; + zeus::CVector3f x7e8_scale3; + float x7f4_ = 0.f; + float x7f8_ = 0.f; + float x7fc_ = 0.f; + float x800_ = 0.f; + float x804_ = 0.f; + float x808_loopAttackDistance = 0.f; + zeus::CVector3f x80c_; + pas::EStepDirection x818_dodgeDirection = pas::EStepDirection::Invalid; + CPatternedInfo x81c_patternedInfo; + CActorParameters x954_actParams; + TUniqueId x9bc_; + u8 x9be_ = 0; + bool x9bf_24_alert : 1 = false; + bool x9bf_25_ : 1 = false; + bool x9bf_26_shotAt : 1 = false; + bool x9bf_27_ : 1 = false; + bool x9bf_28_ : 1 = false; + bool x9bf_29_isAttacking : 1 = false; + bool x9bf_30_ : 1 = false; + bool x9bf_31_ : 1 = false; + bool x9c0_24_ : 1 = false; public: DEFINE_PATTERNED(Metroid) CMetroid(TUniqueId uid, std::string_view name, EFlavorType flavor, const CEntityInfo& info, const zeus::CTransform& xf, CModelData&& mData, const CPatternedInfo& pInfo, const CActorParameters& aParms, const CMetroidData& metroidData, TUniqueId); - bool GetX9BF_29() const { return x9bf_29_; } + + void Accept(IVisitor& visitor) override { visitor.Visit(this); } + void Think(float dt, CStateManager& mgr) override; + void AcceptScriptMsg(EScriptObjectMessage msg, TUniqueId uid, CStateManager& mgr) override; + void DoUserAnimEvent(CStateManager& mgr, const CInt32POINode& node, EUserEventType eType, float dt) override; + const CCollisionPrimitive* GetCollisionPrimitive() const override { return &x6a0_collisionPrimitive; } + EWeaponCollisionResponseTypes GetCollisionResponseType(const zeus::CVector3f& vec1, const zeus::CVector3f& vec2, + const CWeaponMode& mode, + EProjectileAttrib attribute) const override; + const CDamageVulnerability* GetDamageVulnerability() const override; + const CDamageVulnerability* GetDamageVulnerability(const zeus::CVector3f& vec1, const zeus::CVector3f& vec2, + const CDamageInfo& dInfo) const override { + return GetDamageVulnerability(); + } + zeus::CVector3f GetOrigin(const CStateManager& mgr, const CTeamAiRole& role, + const zeus::CVector3f& aimPos) const override; + CPathFindSearch* GetSearchPath() override { return &x6c0_pathFindSearch; } + std::optional GetTouchBounds() const override { + return x6a0_collisionPrimitive.CalculateAABox(GetTransform()); + } + bool IsListening() const override { return true; } + void Render(CStateManager& mgr) override { return CPatterned::Render(mgr); } + void SelectTarget(CStateManager& mgr, EStateMsg msg, float arg) override; + void Touch(CActor& act, CStateManager& mgr) override; + + void Attack(CStateManager& mgr, EStateMsg msg, float dt) override; + void Dodge(CStateManager& mgr, EStateMsg msg, float dt) override; + void Death(CStateManager& mgr, const zeus::CVector3f& direction, EScriptObjectState state) override; + void Generate(CStateManager& mgr, EStateMsg msg, float arg) override; + void KnockBack(const zeus::CVector3f&, CStateManager&, const CDamageInfo& info, EKnockBackType type, bool inDeferred, + float magnitude) override; + void PathFind(CStateManager& mgr, EStateMsg msg, float arg) override; + void Patrol(CStateManager& mgr, EStateMsg msg, float arg) override; + void TargetPatrol(CStateManager& mgr, EStateMsg msg, float dt) override; + void TelegraphAttack(CStateManager& mgr, EStateMsg msg, float dt) override; + void TurnAround(CStateManager& mgr, EStateMsg msg, float dt) override; + void WallHang(CStateManager& mgr, EStateMsg msg, float dt) override; + + bool AnimOver(CStateManager&, float arg) override { return x568_state == EState::Over; } + bool AggressionCheck(CStateManager& mgr, float arg) override; + bool Attacked(CStateManager& mgr, float arg) override; + bool AttackOver(CStateManager& mgr, float arg) override; + bool InAttackPosition(CStateManager& mgr, float arg) override; + bool InDetectionRange(CStateManager& mgr, float arg) override; + bool InPosition(CStateManager& mgr, float arg) override; + bool InRange(CStateManager& mgr, float arg) override; + bool Inside(CStateManager& mgr, float arg) override; + bool Leash(CStateManager& mgr, float arg) override; + bool LostInterest(CStateManager& mgr, float arg) override; + bool PatternShagged(CStateManager& mgr, float arg) override; + bool ShotAt(CStateManager& mgr, float arg) override { return x9bf_26_shotAt; } + bool ShouldAttack(CStateManager& mgr, float arg) override; + bool ShouldDodge(CStateManager& mgr, float arg) override; + bool ShouldTurn(CStateManager& mgr, float arg) override; + bool ShouldWallHang(CStateManager& mgr, float arg) override { return x56c_data.GetStartsInWall(); } + bool SpotPlayer(CStateManager& mgr, float arg) override; + + bool IsAttacking() const { return x9bf_29_isAttacking; } + +private: + float ComputeMorphingPlayerSuckZPos(const CPlayer& player) const; + bool IsPirateValidTarget(CSpacePirate* target, CStateManager& mgr); + bool CanAttack(CStateManager& mgr); + void UpdateAttackChance(CStateManager& mgr, float dt); + bool IsPlayerUnderwater(CStateManager& mgr); + bool IsHunterAttacking(CStateManager& mgr); + bool IsAttackInProgress(CStateManager& mgr); + void ComputeSuckPiratePosRot(CStateManager& mgr, zeus::CVector3f& outVec, zeus::CQuaternion& outQuat); + int GetRandomAnimParmsIdx(CStateManager& mgr, int prevIdx); + void SpawnGammaMetroid(CStateManager& mgr); + bool ShouldSpawnGammaMetroid(); + void ComputeSuckPlayerPosRot(CStateManager& mgr, zeus::CVector3f& outVec, zeus::CQuaternion& outQuat); + void ComputeSuckTargetPosRot(CStateManager& mgr, zeus::CVector3f& outVec, zeus::CQuaternion& outQuat); + void InterpolateToPosRot(CStateManager& mgr, float dt); + void SuckEnergyFromTarget(CStateManager& mgr, float dt); + bool ShouldReleaseFromTarget(CStateManager& mgr); + void DisableSolidCollision(CMetroid* target); + void RestoreSolidCollision(CStateManager& mgr); + void PreventWorldCollisions(CStateManager& mgr, float dt); + void SetupExitFaceHugDirection(CActor& actor, CStateManager& mgr, const zeus::CVector3f& vec, + const zeus::CTransform& xf); + void DetachFromTarget(CStateManager& mgr); + bool AttachToTarget(CStateManager& mgr); + void SwarmRemove(CStateManager& mgr); + void SwarmAdd(CStateManager& mgr); + void ApplySplitGammas(CStateManager& mgr, float arg); + void ApplyForwardSteering(CStateManager& mgr, const zeus::CVector3f& vec); + void ApplySeparationBehavior(CStateManager& mgr, float arg); + void SetUpPathFindBehavior(CStateManager& mgr); + void ApplyGrowth(float arg); + bool PreDamageSpacePirate(CStateManager& mgr); + float GetDamageMultiplier() { return 0.5f * (GetGrowthStage() - 1.f) + 1.f; } + float GetGrowthStage(); + zeus::CVector3f GetAttackTargetPos(CStateManager& mgr); + bool IsSuckingEnergy(); + void UpdateVolume(); + void UpdateTouchBounds(); }; } // namespace urde::MP1 diff --git a/Runtime/MP1/World/CMetroidBeta.cpp b/Runtime/MP1/World/CMetroidBeta.cpp index fac634659..2109323cf 100644 --- a/Runtime/MP1/World/CMetroidBeta.cpp +++ b/Runtime/MP1/World/CMetroidBeta.cpp @@ -79,6 +79,7 @@ void CMetroidBeta::Think(float dt, CStateManager& mgr) { // sub801c0da4(dt, mgr); // sub801c21b4(dt, mgr); } + void CMetroidBeta::AcceptScriptMsg(EScriptObjectMessage msg, TUniqueId uid, CStateManager& mgr) { CPatterned::AcceptScriptMsg(msg, uid, mgr); switch (msg) { diff --git a/Runtime/MP1/World/CMetroidBeta.hpp b/Runtime/MP1/World/CMetroidBeta.hpp index c8b41efe0..9f34b793b 100644 --- a/Runtime/MP1/World/CMetroidBeta.hpp +++ b/Runtime/MP1/World/CMetroidBeta.hpp @@ -13,7 +13,7 @@ namespace urde { class CCollisionActorManager; class CElementGen; class CParticleSwoosh; -} +} // namespace urde namespace urde::MP1 { @@ -40,6 +40,7 @@ class CMetroidBetaData { public: explicit CMetroidBetaData(CInputStream&); }; + class CMetroidBeta : public CPatterned { s32 x568_progState = -1; CMetroidBetaData x56c_metroidBetaData; @@ -89,12 +90,14 @@ class CMetroidBeta : public CPatterned { void SetCollisionActorHealthAndVulnerability(CStateManager& mgr); void RemoveFromTeam(CStateManager& mgr); void AddToTeam(CStateManager& mgr); + public: DEFINE_PATTERNED(MetroidBeta) CMetroidBeta(TUniqueId uid, std::string_view name, const CEntityInfo& info, const zeus::CTransform& xf, CModelData&& mData, const CPatternedInfo& pInfo, const CActorParameters& aParms, const CMetroidBetaData& metroidData); + void Accept(IVisitor& visitor) override { visitor.Visit(this); } void Think(float dt, CStateManager& mgr) override; void AcceptScriptMsg(EScriptObjectMessage msg, TUniqueId uid, CStateManager& mgr) override; void AddToRenderer(const zeus::CFrustum& frustum, CStateManager& mgr) override; diff --git a/Runtime/MP1/World/CSpacePirate.cpp b/Runtime/MP1/World/CSpacePirate.cpp index 4af6dfb6b..48d5c9ea7 100644 --- a/Runtime/MP1/World/CSpacePirate.cpp +++ b/Runtime/MP1/World/CSpacePirate.cpp @@ -821,7 +821,7 @@ void CSpacePirate::Think(float dt, CStateManager& mgr) { x400_27_fadeToDeath = true; AddMaterial(EMaterialTypes::ProjectilePassthrough, mgr); x3e8_alphaDelta = -0.333333f; - x638_30_ragdollOver = true; + x638_30_allEnergyDrained = true; SetMomentumWR(zeus::skZero3f); CPhysicsActor::Stop(); } @@ -1070,7 +1070,7 @@ void CSpacePirate::DoUserAnimEvent(CStateManager& mgr, const CInt32POINode& node switch (type) { case EUserEventType::BeginAction: RemoveMaterial(EMaterialTypes::Solid, mgr); - x638_30_ragdollOver = true; + x638_30_allEnergyDrained = true; handled = true; break; case EUserEventType::EndAction: @@ -2360,7 +2360,7 @@ bool CSpacePirate::ShouldDodge(CStateManager& mgr, float arg) { } if (!ret) { if (const CMetroid* metroid = CPatterned::CastTo(mgr.GetObjectById(x7c0_targetId))) { - if (metroid->GetX9BF_29() && + if (metroid->IsAttacking() && (GetTranslation() - metroid->GetTranslation()).dot(metroid->GetTransform().basis[1]) > 0.f) { ret = true; } diff --git a/Runtime/MP1/World/CSpacePirate.hpp b/Runtime/MP1/World/CSpacePirate.hpp index 137f92eeb..e1e8be21f 100644 --- a/Runtime/MP1/World/CSpacePirate.hpp +++ b/Runtime/MP1/World/CSpacePirate.hpp @@ -136,7 +136,7 @@ private: bool x638_27_coverCheck : 1 = false; bool x638_28_enableDodge : 1 = false; bool x638_29_noPlayerDodge : 1 = false; - bool x638_30_ragdollOver : 1 = false; + bool x638_30_allEnergyDrained : 1 = false; bool x638_31_mayStartAttack : 1 = false; bool x639_24_ : 1 = false; bool x639_25_useJumpBackJump : 1 = false; @@ -329,5 +329,6 @@ public: float GetGravityConstant() const override; CProjectileInfo* GetProjectileInfo() override; bool GetEnableAim() const { return x637_25_enableAim; } + bool AllEnergyDrained() const { return x638_30_allEnergyDrained; } }; } // namespace urde::MP1 diff --git a/Runtime/World/CPatternedInfo.hpp b/Runtime/World/CPatternedInfo.hpp index ee29b63b2..fa9f05252 100644 --- a/Runtime/World/CPatternedInfo.hpp +++ b/Runtime/World/CPatternedInfo.hpp @@ -69,5 +69,6 @@ public: const CAnimationParameters& GetAnimationParameters() const { return xec_animParams; } u32 GetPathfindingIndex() const { return x10c_pathfindingIndex; } bool GetActive() const { return xf8_active; } + void SetActive(bool active) { xf8_active = active; } }; } // namespace urde