diff --git a/asm/MetroidPrime/BodyState/CBSCover.s b/asm/MetroidPrime/BodyState/CBSCover.s index 597ad711..70b59f08 100644 --- a/asm/MetroidPrime/BodyState/CBSCover.s +++ b/asm/MetroidPrime/BodyState/CBSCover.s @@ -3,8 +3,8 @@ .section .data .balign 8 -.global lbl_803E3E48 -lbl_803E3E48: +.global __vt__8CBSCover +__vt__8CBSCover: # ROM: 0x3E0E48 .4byte 0 .4byte 0 @@ -31,8 +31,8 @@ __dt__8CBSCoverFv: /* 80175CE4 00172C44 93 E1 00 0C */ stw r31, 0xc(r1) /* 80175CE8 00172C48 7C 7F 1B 79 */ or. r31, r3, r3 /* 80175CEC 00172C4C 41 82 00 30 */ beq lbl_80175D1C -/* 80175CF0 00172C50 3C 60 80 3E */ lis r3, lbl_803E3E48@ha -/* 80175CF4 00172C54 38 03 3E 48 */ addi r0, r3, lbl_803E3E48@l +/* 80175CF0 00172C50 3C 60 80 3E */ lis r3, __vt__8CBSCover@ha +/* 80175CF4 00172C54 38 03 3E 48 */ addi r0, r3, __vt__8CBSCover@l /* 80175CF8 00172C58 90 1F 00 00 */ stw r0, 0(r31) /* 80175CFC 00172C5C 41 82 00 10 */ beq lbl_80175D0C /* 80175D00 00172C60 3C 60 80 3E */ lis r3, __vt__10CBodyState@ha @@ -273,7 +273,6 @@ lbl_80175FEC: /* 80176024 00172F84 48 16 D0 59 */ bl NoParameter__12CPASAnimParmFv /* 80176028 00172F88 38 61 00 78 */ addi r3, r1, 0x78 /* 8017602C 00172F8C 48 16 D0 51 */ bl NoParameter__12CPASAnimParmFv -.global lbl_80176030 lbl_80176030: /* 80176030 00172F90 7F 64 DB 78 */ mr r4, r27 /* 80176034 00172F94 38 61 00 80 */ addi r3, r1, 0x80 @@ -581,11 +580,11 @@ lbl_80176484: .global __ct__8CBSCoverFv __ct__8CBSCoverFv: /* 80176498 001733F8 3C C0 80 3E */ lis r6, __vt__10CBodyState@ha -/* 8017649C 001733FC 3C A0 80 3E */ lis r5, lbl_803E3E48@ha +/* 8017649C 001733FC 3C A0 80 3E */ lis r5, __vt__8CBSCover@ha /* 801764A0 00173400 38 06 13 18 */ addi r0, r6, __vt__10CBodyState@l /* 801764A4 00173404 38 80 FF FF */ li r4, -1 /* 801764A8 00173408 90 03 00 00 */ stw r0, 0(r3) -/* 801764AC 0017340C 38 A5 3E 48 */ addi r5, r5, lbl_803E3E48@l +/* 801764AC 0017340C 38 A5 3E 48 */ addi r5, r5, __vt__8CBSCover@l /* 801764B0 00173410 38 00 00 00 */ li r0, 0 /* 801764B4 00173414 90 A3 00 00 */ stw r5, 0(r3) /* 801764B8 00173418 90 83 00 04 */ stw r4, 4(r3) diff --git a/configure.py b/configure.py index 99edf770..c5cc1a81 100755 --- a/configure.py +++ b/configure.py @@ -237,7 +237,7 @@ LIBS = [ ["MetroidPrime/ScriptObjects/CScriptRipple", False], "MetroidPrime/CBoneTracking", ["MetroidPrime/Player/CFaceplateDecoration", False], - "MetroidPrime/BodyState/CBSCover", + ["MetroidPrime/BodyState/CBSCover", False], ["MetroidPrime/ScriptObjects/CScriptBallTrigger", True], "MetroidPrime/Weapons/CPlasmaProjectile", "MetroidPrime/Player/CPlayerOrbit", diff --git a/include/Kyoto/Math/CQuaternion.hpp b/include/Kyoto/Math/CQuaternion.hpp index 5737d658..a5cffda6 100644 --- a/include/Kyoto/Math/CQuaternion.hpp +++ b/include/Kyoto/Math/CQuaternion.hpp @@ -33,6 +33,7 @@ public: // Slerp__11CQuaternionFRC11CQuaternionRC11CQuaternionf // ShortestRotationArc__11CQuaternionFRC9CVector3fRC9CVector3f // LookAt__11CQuaternionFRC13CUnitVector3fRC13CUnitVector3fRC9CRelAngle + static CQuaternion LookAt(const CUnitVector3f&, const CUnitVector3f&, const CRelAngle&); // normalize_angle__Ff // IsValidQuaternion__11CQuaternionCFf // SlerpLocal__11CQuaternionFRC11CQuaternionRC11CQuaternionf diff --git a/include/Kyoto/Math/CUnitVector3f.hpp b/include/Kyoto/Math/CUnitVector3f.hpp index 36eda9f9..0538e982 100644 --- a/include/Kyoto/Math/CUnitVector3f.hpp +++ b/include/Kyoto/Math/CUnitVector3f.hpp @@ -16,8 +16,14 @@ public: Normalize(); } } - CUnitVector3f(const CVector3f& vec); // : CVector3f(vec) { Normalize(); } + CUnitVector3f( + const CVector3f& vec); // : CVector3f(vec.IsNonZero() ? vec.AsNormalized() : Zero()) {} // TODO + + static CUnitVector3f Forward() { + return CUnitVector3f(CVector3f::Forward().GetX(), CVector3f::Forward().GetY(), + CVector3f::Forward().GetZ(), kN_No); + } }; CHECK_SIZEOF(CUnitVector3f, 0xc) diff --git a/include/MetroidPrime/BodyState/CBSCover.hpp b/include/MetroidPrime/BodyState/CBSCover.hpp new file mode 100644 index 00000000..9953d01d --- /dev/null +++ b/include/MetroidPrime/BodyState/CBSCover.hpp @@ -0,0 +1,27 @@ +#ifndef _CBSCOVER +#define _CBSCOVER + +#include "MetroidPrime/BodyState/CBodyState.hpp" + +class CBSCover : public CBodyState { +public: + CBSCover(); + bool IsMoving() const override { return true; } + bool ApplyHeadTracking() const override { return false; } + bool CanShoot() const override; + void Start(CBodyController& bc, CStateManager& mgr) override; + pas::EAnimationState UpdateBody(float dt, CBodyController& bc, CStateManager& mgr) override; + void Shutdown(CBodyController&) override; + + pas::ECoverDirection GetCoverDirection() const { return x8_coverDirection; } + bool GetNeedsExit() const { return xc_needsExit; } + +private: + pas::ECoverState x4_state; + pas::ECoverDirection x8_coverDirection; + bool xc_needsExit; + + pas::EAnimationState GetBodyStateTransition(float dt, CBodyController& bc); +}; + +#endif // _CBSCOVER diff --git a/include/MetroidPrime/BodyState/CBodyController.hpp b/include/MetroidPrime/BodyState/CBodyController.hpp index 0b4efaa5..405680e9 100644 --- a/include/MetroidPrime/BodyState/CBodyController.hpp +++ b/include/MetroidPrime/BodyState/CBodyController.hpp @@ -26,6 +26,7 @@ public: void FaceDirection(const CVector3f& v0, float dt); void EnableAnimation(bool enable); void PlayBestAnimation(const CPASAnimParmData& parms, CRandom16& r); + void LoopBestAnimation(const CPASAnimParmData& parms, CRandom16& r); bool HasIceBreakoutState(); void Activate(CStateManager& mgr); void SetLocomotionType(pas::ELocomotionType type); diff --git a/include/MetroidPrime/BodyState/CBodyStateCmdMgr.hpp b/include/MetroidPrime/BodyState/CBodyStateCmdMgr.hpp index 21a75a1c..2fe778d1 100644 --- a/include/MetroidPrime/BodyState/CBodyStateCmdMgr.hpp +++ b/include/MetroidPrime/BodyState/CBodyStateCmdMgr.hpp @@ -7,6 +7,7 @@ #include "Kyoto/Animation/CharacterCommon.hpp" #include "Kyoto/Math/CVector3f.hpp" +#include "Kyoto/Math/CUnitVector3f.hpp" enum ESteeringBlendMode { kSBM_Normal, @@ -341,12 +342,12 @@ public: pas::ECoverDirection GetDirection() const { return x8_dir; } const CVector3f& GetTarget() const { return xc_targetPos; } - const CVector3f& GetAlignDirection() const { return x18_alignDir; } + CUnitVector3f GetAlignDirection() const { return x18_alignDir; } private: pas::ECoverDirection x8_dir; CVector3f xc_targetPos; - CVector3f x18_alignDir; + CUnitVector3f x18_alignDir; }; // diff --git a/src/MetroidPrime/BodyState/CBSCover.cpp b/src/MetroidPrime/BodyState/CBSCover.cpp new file mode 100644 index 00000000..a6ba8ef2 --- /dev/null +++ b/src/MetroidPrime/BodyState/CBSCover.cpp @@ -0,0 +1,112 @@ +#include "MetroidPrime/BodyState/CBSCover.hpp" + +#include "MetroidPrime/BodyState/CBodyController.hpp" +#include "MetroidPrime/CActor.hpp" +#include "MetroidPrime/CAnimPlaybackParms.hpp" +#include "MetroidPrime/CStateManager.hpp" + +#include "Kyoto/Animation/CPASAnimParmData.hpp" +#include "Kyoto/Animation/CPASDatabase.hpp" +#include "Kyoto/Math/CRelAngle.hpp" +#include "Kyoto/Math/CUnitVector3f.hpp" + +bool CBSCover::CanShoot() const { return x4_state == pas::kCS_Lean; } + +CBSCover::CBSCover() +: x4_state(pas::kCS_Invalid), x8_coverDirection(pas::kCD_Invalid), xc_needsExit(false) {} + +void CBSCover::Start(CBodyController& bc, CStateManager& mgr) { + const CBCCoverCmd* cmd = static_cast< const CBCCoverCmd* >(bc.CommandMgr().GetCmd(kBSC_Cover)); + x8_coverDirection = cmd->GetDirection(); + x4_state = pas::kCS_IntoCover; + const CPASAnimParmData parms(pas::kAS_Cover, CPASAnimParm::FromEnum(x4_state), + CPASAnimParm::FromEnum(GetCoverDirection())); + + const rstl::pair< float, int > best = + bc.GetPASDatabase().FindBestAnimation(parms, *mgr.Random(), -1); + + CVector3f scale = bc.GetOwner().GetModelData()->GetScale(); + CRelAngle lookAtMaxAngle = CRelAngle::FromRadians(M_2PIF); + const CUnitVector3f& lookAtDest = cmd->GetAlignDirection(); + const CQuaternion orientDelta = CQuaternion::LookAt(CUnitVector3f::Forward(), lookAtDest, lookAtMaxAngle); + + const CAnimPlaybackParms playParms(best.second, &orientDelta, &cmd->GetTarget(), + &bc.GetOwner().GetTransform(), &scale, false); + bc.SetCurrentAnimation(playParms, false, false); + xc_needsExit = false; + if (bc.CommandMgr().GetCmd(kBSC_ExitState)) { + xc_needsExit = true; + } +} + +pas::EAnimationState CBSCover::UpdateBody(float dt, CBodyController& bc, CStateManager& mgr) { + pas::EAnimationState st = GetBodyStateTransition(dt, bc); + if (st == pas::kAS_Invalid) { + switch (x4_state) { + case pas::kCS_Lean: + case pas::kCS_IntoCover: + if (bc.IsAnimationOver()) { + x4_state = pas::kCS_Cover; + const CPASAnimParmData parms(pas::kAS_Cover, CPASAnimParm::FromEnum(x4_state), + CPASAnimParm::FromEnum(GetCoverDirection())); + bc.LoopBestAnimation(parms, *mgr.Random()); + } + if (bc.CommandMgr().GetCmd(kBSC_ExitState)) { + xc_needsExit = true; + } + break; + case pas::kCS_Cover: { + CBodyStateCmdMgr& commandMgr = bc.CommandMgr(); + if (commandMgr.GetTargetVector().IsNonZero()) { + bc.FaceDirection(commandMgr.GetTargetVector(), dt); + } + if (commandMgr.GetCmd(kBSC_ExitState) || GetNeedsExit()) { + xc_needsExit = false; + x4_state = pas::kCS_OutOfCover; + const CPASAnimParmData parms(pas::kAS_Cover, CPASAnimParm::FromEnum(x4_state), + CPASAnimParm::FromEnum(GetCoverDirection())); + bc.PlayBestAnimation(parms, *mgr.Random()); + + } else if (commandMgr.GetCmd(kBSC_LeanFromCover)) { + x4_state = pas::kCS_Lean; + const CPASAnimParmData parms(pas::kAS_Cover, CPASAnimParm::FromEnum(x4_state), + CPASAnimParm::FromEnum(GetCoverDirection())); + bc.PlayBestAnimation(parms, *mgr.Random()); + } + break; + } + case pas::kCS_OutOfCover: + if (bc.IsAnimationOver()) { + x4_state = pas::kCS_Invalid; + st = pas::kAS_Locomotion; + } + break; + default: + break; + } + } + return st; +} + +void CBSCover::Shutdown(CBodyController&) {} + +pas::EAnimationState CBSCover::GetBodyStateTransition(float dt, CBodyController& bc) { + CBodyStateCmdMgr& cmdMgr = bc.CommandMgr(); + + if (cmdMgr.GetCmd(kBSC_Hurled)) { + return pas::kAS_Hurled; + } + if (cmdMgr.GetCmd(kBSC_KnockDown)) { + return pas::kAS_Fall; + } + if (cmdMgr.GetCmd(kBSC_LoopHitReaction)) { + return pas::kAS_LoopReaction; + } + if (cmdMgr.GetCmd(kBSC_KnockBack)) { + return pas::kAS_KnockBack; + } + if (cmdMgr.GetCmd(kBSC_Locomotion)) { + return pas::kAS_Locomotion; + } + return pas::kAS_Invalid; +}