diff --git a/asm/MetroidPrime/BodyState/CBSLoopAttack.s b/asm/MetroidPrime/BodyState/CBSLoopAttack.s index 20a2eb66..6c087c2c 100644 --- a/asm/MetroidPrime/BodyState/CBSLoopAttack.s +++ b/asm/MetroidPrime/BodyState/CBSLoopAttack.s @@ -3,8 +3,8 @@ .section .data .balign 8 -.global lbl_803E17C8 -lbl_803E17C8: +.global __vt__13CBSLoopAttack +__vt__13CBSLoopAttack: # ROM: 0x3DE7C8 .4byte 0 .4byte 0 @@ -31,8 +31,8 @@ __dt__13CBSLoopAttackFv: /* 8013AED8 00137E38 93 E1 00 0C */ stw r31, 0xc(r1) /* 8013AEDC 00137E3C 7C 7F 1B 79 */ or. r31, r3, r3 /* 8013AEE0 00137E40 41 82 00 30 */ beq lbl_8013AF10 -/* 8013AEE4 00137E44 3C 60 80 3E */ lis r3, lbl_803E17C8@ha -/* 8013AEE8 00137E48 38 03 17 C8 */ addi r0, r3, lbl_803E17C8@l +/* 8013AEE4 00137E44 3C 60 80 3E */ lis r3, __vt__13CBSLoopAttack@ha +/* 8013AEE8 00137E48 38 03 17 C8 */ addi r0, r3, __vt__13CBSLoopAttack@l /* 8013AEEC 00137E4C 90 1F 00 00 */ stw r0, 0(r31) /* 8013AEF0 00137E50 41 82 00 10 */ beq lbl_8013AF00 /* 8013AEF4 00137E54 3C 60 80 3E */ lis r3, __vt__10CBodyState@ha @@ -701,11 +701,11 @@ lbl_8013B81C: .global __ct__13CBSLoopAttackFv __ct__13CBSLoopAttackFv: /* 8013B838 00138798 3C A0 80 3E */ lis r5, __vt__10CBodyState@ha -/* 8013B83C 0013879C 3C 80 80 3E */ lis r4, lbl_803E17C8@ha +/* 8013B83C 0013879C 3C 80 80 3E */ lis r4, __vt__13CBSLoopAttack@ha /* 8013B840 001387A0 38 C5 13 18 */ addi r6, r5, __vt__10CBodyState@l /* 8013B844 001387A4 38 00 FF FF */ li r0, -1 /* 8013B848 001387A8 90 C3 00 00 */ stw r6, 0(r3) -/* 8013B84C 001387AC 38 A4 17 C8 */ addi r5, r4, lbl_803E17C8@l +/* 8013B84C 001387AC 38 A4 17 C8 */ addi r5, r4, __vt__13CBSLoopAttack@l /* 8013B850 001387B0 38 80 00 00 */ li r4, 0 /* 8013B854 001387B4 90 A3 00 00 */ stw r5, 0(r3) /* 8013B858 001387B8 90 03 00 04 */ stw r0, 4(r3) diff --git a/configure.py b/configure.py index d3030a1b..e6b902c8 100755 --- a/configure.py +++ b/configure.py @@ -194,7 +194,7 @@ LIBS = [ ["MetroidPrime/BodyState/CBSStep", True], "MetroidPrime/BodyState/CBSTurn", "MetroidPrime/BodyState/CBodyController", - "MetroidPrime/BodyState/CBSLoopAttack", + ["MetroidPrime/BodyState/CBSLoopAttack", False], ["MetroidPrime/Weapons/CTargetableProjectile", False], "MetroidPrime/BodyState/CBSLoopReaction", "MetroidPrime/CSteeringBehaviors", diff --git a/include/MetroidPrime/BodyState/CBSLoopAttack.hpp b/include/MetroidPrime/BodyState/CBSLoopAttack.hpp new file mode 100644 index 00000000..b862a6c0 --- /dev/null +++ b/include/MetroidPrime/BodyState/CBSLoopAttack.hpp @@ -0,0 +1,28 @@ +#ifndef _CBSLOOPATTACK +#define _CBSLOOPATTACK + +#include "MetroidPrime/BodyState/CBodyState.hpp" + +class CBSLoopAttack : public CBodyState { +public: + CBSLoopAttack(); + + bool CanShoot() const override { return true; } + void Start(CBodyController& bc, CStateManager& mgr) override; + pas::EAnimationState UpdateBody(float dt, CBodyController& bc, CStateManager& mgr) override; + void Shutdown(CBodyController&) override; + + pas::ELoopState GetState() const { return x4_state; } + pas::ELoopAttackType GetAttackType() const { return x8_loopAttackType; } + bool GetAdvance() const { return xc_25_advance; } + +private: + pas::ELoopState x4_state; + pas::ELoopAttackType x8_loopAttackType; + bool xc_24_waitForAnimOver : 1; + bool xc_25_advance : 1; + + pas::EAnimationState GetBodyStateTransition(float dt, CBodyController& bc); +}; + +#endif // _CBSLOOPATTACK diff --git a/include/MetroidPrime/BodyState/CBodyStateCmdMgr.hpp b/include/MetroidPrime/BodyState/CBodyStateCmdMgr.hpp index 6c95cdd5..366d4a07 100644 --- a/include/MetroidPrime/BodyState/CBodyStateCmdMgr.hpp +++ b/include/MetroidPrime/BodyState/CBodyStateCmdMgr.hpp @@ -252,11 +252,11 @@ public: : CBodyStateCmd(kBSC_LoopAttack), x8_type(type), xc_waitForAnimOver(waitForAnimOver) {} pas::ELoopAttackType GetAttackType() const { return x8_type; } - bool WaitForAnimOver() const { return xc_waitForAnimOver; } + int WaitForAnimOver() const { return xc_waitForAnimOver; } private: pas::ELoopAttackType x8_type; - bool xc_waitForAnimOver; + int xc_waitForAnimOver; }; // @@ -423,6 +423,7 @@ public: CBodyStateCmd* GetCmd(EBodyStateCmd cmd); const CVector3f& GetMoveVector() const { return x0_move; } + const CVector3f& GetFaceVector() const { return xc_face; } const CVector3f& GetTargetVector() const { return x18_target; } const CVector3f& GetAdditiveTargetVector() const { return x24_additiveTarget; } diff --git a/src/MetroidPrime/BodyState/CBSLoopAttack.cpp b/src/MetroidPrime/BodyState/CBSLoopAttack.cpp new file mode 100644 index 00000000..633135b3 --- /dev/null +++ b/src/MetroidPrime/BodyState/CBSLoopAttack.cpp @@ -0,0 +1,143 @@ +#include "MetroidPrime/BodyState/CBSLoopAttack.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" + +CBSLoopAttack::CBSLoopAttack() +: x4_state(pas::kLS_Invalid) +, x8_loopAttackType(pas::kLAT_Invalid) +, xc_24_waitForAnimOver(false) +, xc_25_advance(false) + +{} + +void CBSLoopAttack::Start(CBodyController& bc, CStateManager& mgr) { + const CBCLoopAttackCmd* cmd = + static_cast< const CBCLoopAttackCmd* >(bc.CommandMgr().GetCmd(kBSC_LoopAttack)); + + x8_loopAttackType = cmd->GetAttackType(); + xc_24_waitForAnimOver = cmd->WaitForAnimOver() == 1; + xc_25_advance = false; + + if (bc.GetLocomotionType() == pas::kLT_Crouch) { + x4_state = pas::kLS_Loop; + const CPASAnimParmData parms(pas::kAS_LoopAttack, CPASAnimParm::FromEnum(x4_state), + CPASAnimParm::FromEnum(x8_loopAttackType)); + bc.LoopBestAnimation(parms, *mgr.Random()); + + } else { + x4_state = pas::kLS_Begin; + const CPASAnimParmData parms(pas::kAS_LoopAttack, CPASAnimParm::FromEnum(x4_state), + CPASAnimParm::FromEnum(x8_loopAttackType)); + const rstl::pair< float, int > best = + bc.GetPASDatabase().FindBestAnimation(parms, *mgr.Random(), -1); + if (best.first > FLT_EPSILON) { + const CAnimPlaybackParms playParms(best.second, -1, 1.f, true); + bc.SetCurrentAnimation(playParms, false, false); + } else { + x4_state = pas::kLS_Loop; + const CPASAnimParmData loopParms(pas::kAS_LoopAttack, CPASAnimParm::FromEnum(x4_state), + CPASAnimParm::FromEnum(x8_loopAttackType)); + bc.LoopBestAnimation(loopParms, *mgr.Random()); + } + } +} + +pas::EAnimationState CBSLoopAttack::UpdateBody(float dt, CBodyController& bc, CStateManager& mgr) { + pas::EAnimationState st = GetBodyStateTransition(dt, bc); + + if (st == pas::kAS_Invalid) { + CBodyStateCmdMgr& commandMgr = bc.CommandMgr(); + CBodyStateCmd* cmd = commandMgr.GetCmd(kBSC_ExitState); + xc_25_advance = ((GetAdvance() | cmd != nullptr) << 4) & 0x10; + + switch (x4_state) { + case pas::kLS_Begin: + if (xc_25_advance && (!xc_24_waitForAnimOver || bc.IsAnimationOver())) { + x4_state = pas::kLS_Invalid; + st = pas::kAS_Locomotion; + } else if (bc.IsAnimationOver()) { + const CPASAnimParmData parms(pas::kAS_LoopAttack, CPASAnimParm::FromEnum(1), + CPASAnimParm::FromEnum(x8_loopAttackType)); + bc.LoopBestAnimation(parms, *mgr.Random()); + x4_state = pas::kLS_Loop; + } else { + if (commandMgr.GetTargetVector().IsNonZero()) { + bc.FaceDirection(commandMgr.GetTargetVector(), dt); + } + } + break; + case pas::kLS_Loop: + if (xc_25_advance && (!xc_24_waitForAnimOver || bc.IsAnimationOver())) { + if (bc.GetLocomotionType() != pas::kLT_Crouch) { + const CPASAnimParmData parms(pas::kAS_LoopAttack, CPASAnimParm::FromEnum(2), + CPASAnimParm::FromEnum(x8_loopAttackType)); + bc.PlayBestAnimation(parms, *mgr.Random()); + x4_state = pas::kLS_End; + } else { + x4_state = pas::kLS_Invalid; + st = pas::kAS_Locomotion; + } + } + break; + case pas::kLS_End: + if (bc.IsAnimationOver()) { + x4_state = pas::kLS_Invalid; + st = pas::kAS_Locomotion; + } + break; + default: + break; + } + } + return st; +} + +void CBSLoopAttack::Shutdown(CBodyController&) {} + +pas::EAnimationState CBSLoopAttack::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; + } + + if (x4_state == pas::kLS_End) { + if (cmdMgr.GetCmd(kBSC_MeleeAttack)) { + return pas::kAS_MeleeAttack; + } + if (cmdMgr.GetCmd(kBSC_ProjectileAttack)) { + return pas::kAS_ProjectileAttack; + } + if (cmdMgr.GetCmd(kBSC_LoopAttack)) { + return pas::kAS_LoopAttack; + } + if (cmdMgr.GetCmd(kBSC_Step)) { + return pas::kAS_Step; + } + if (cmdMgr.GetMoveVector().IsNonZero()) { + return pas::kAS_Locomotion; + } + if (cmdMgr.GetFaceVector().IsNonZero()) { + return pas::kAS_Turn; + } + } + + return pas::kAS_Invalid; +}