diff --git a/asm/MetroidPrime/BodyState/CBSLoopReaction.s b/asm/MetroidPrime/BodyState/CBSLoopReaction.s index c86223db..d43555b6 100644 --- a/asm/MetroidPrime/BodyState/CBSLoopReaction.s +++ b/asm/MetroidPrime/BodyState/CBSLoopReaction.s @@ -3,8 +3,8 @@ .section .data .balign 8 -.global lbl_803E1878 -lbl_803E1878: +.global __vt__15CBSLoopReaction +__vt__15CBSLoopReaction: # ROM: 0x3DE878 .4byte 0 .4byte 0 @@ -31,8 +31,8 @@ __dt__15CBSLoopReactionFv: /* 8013C054 00138FB4 93 E1 00 0C */ stw r31, 0xc(r1) /* 8013C058 00138FB8 7C 7F 1B 79 */ or. r31, r3, r3 /* 8013C05C 00138FBC 41 82 00 30 */ beq lbl_8013C08C -/* 8013C060 00138FC0 3C 60 80 3E */ lis r3, lbl_803E1878@ha -/* 8013C064 00138FC4 38 03 18 78 */ addi r0, r3, lbl_803E1878@l +/* 8013C060 00138FC0 3C 60 80 3E */ lis r3, __vt__15CBSLoopReaction@ha +/* 8013C064 00138FC4 38 03 18 78 */ addi r0, r3, __vt__15CBSLoopReaction@l /* 8013C068 00138FC8 90 1F 00 00 */ stw r0, 0(r31) /* 8013C06C 00138FCC 41 82 00 10 */ beq lbl_8013C07C /* 8013C070 00138FD0 3C 60 80 3E */ lis r3, __vt__10CBodyState@ha @@ -734,11 +734,11 @@ lbl_8013CA00: .global __ct__15CBSLoopReactionFv __ct__15CBSLoopReactionFv: /* 8013CA20 00139980 3C A0 80 3E */ lis r5, __vt__10CBodyState@ha -/* 8013CA24 00139984 3C 80 80 3E */ lis r4, lbl_803E1878@ha +/* 8013CA24 00139984 3C 80 80 3E */ lis r4, __vt__15CBSLoopReaction@ha /* 8013CA28 00139988 38 C5 13 18 */ addi r6, r5, __vt__10CBodyState@l /* 8013CA2C 0013998C 38 00 FF FF */ li r0, -1 /* 8013CA30 00139990 90 C3 00 00 */ stw r6, 0(r3) -/* 8013CA34 00139994 38 A4 18 78 */ addi r5, r4, lbl_803E1878@l +/* 8013CA34 00139994 38 A4 18 78 */ addi r5, r4, __vt__15CBSLoopReaction@l /* 8013CA38 00139998 38 80 00 00 */ li r4, 0 /* 8013CA3C 0013999C 90 A3 00 00 */ stw r5, 0(r3) /* 8013CA40 001399A0 90 03 00 04 */ stw r0, 4(r3) diff --git a/configure.py b/configure.py index c58da97a..6676c724 100755 --- a/configure.py +++ b/configure.py @@ -196,7 +196,7 @@ LIBS = [ "MetroidPrime/BodyState/CBodyController", ["MetroidPrime/BodyState/CBSLoopAttack", False], ["MetroidPrime/Weapons/CTargetableProjectile", False], - "MetroidPrime/BodyState/CBSLoopReaction", + ["MetroidPrime/BodyState/CBSLoopReaction", False], "MetroidPrime/CSteeringBehaviors", ["MetroidPrime/BodyState/CBSGroundHit", False], "MetroidPrime/Enemies/CChozoGhost", diff --git a/include/MetroidPrime/BodyState/CBSLoopReaction.hpp b/include/MetroidPrime/BodyState/CBSLoopReaction.hpp new file mode 100644 index 00000000..20105e65 --- /dev/null +++ b/include/MetroidPrime/BodyState/CBSLoopReaction.hpp @@ -0,0 +1,23 @@ +#ifndef _CBSLOOPREACTION +#define _CBSLOOPREACTION + +#include "MetroidPrime/BodyState/CBodyState.hpp" + +class CBSLoopReaction : public CBodyState { +public: + CBSLoopReaction(); + + void Start(CBodyController& bc, CStateManager& mgr) override; + pas::EAnimationState UpdateBody(float dt, CBodyController& bc, CStateManager& mgr) override; + void Shutdown(CBodyController&) override {} + +private: + pas::ELoopState x4_state; + pas::EReactionType x8_reactionType; + bool xc_24_loopHit : 1; + + pas::EAnimationState GetBodyStateTransition(float dt, CBodyController& bc); + bool PlayExitAnimation(CBodyController& bc, CStateManager& mgr) const; +}; + +#endif // _CBSLOOPREACTION diff --git a/src/MetroidPrime/BodyState/CBSLoopReaction.cpp b/src/MetroidPrime/BodyState/CBSLoopReaction.cpp new file mode 100644 index 00000000..6447fe22 --- /dev/null +++ b/src/MetroidPrime/BodyState/CBSLoopReaction.cpp @@ -0,0 +1,143 @@ +#include "MetroidPrime/BodyState/CBSLoopReaction.hpp" + +#include "MetroidPrime/BodyState/CBodyController.hpp" +#include "MetroidPrime/CAnimPlaybackParms.hpp" +#include "MetroidPrime/CStateManager.hpp" + +#include "Kyoto/Animation/CPASAnimParmData.hpp" +#include "Kyoto/Animation/CPASDatabase.hpp" + +CBSLoopReaction::CBSLoopReaction() +: x4_state(pas::kLS_Invalid), x8_reactionType(pas::kRT_Invalid), xc_24_loopHit(false) {} + +void CBSLoopReaction::Start(CBodyController& bc, CStateManager& mgr) { + CBodyStateCmdMgr& commandMgr = bc.CommandMgr(); + + CBodyStateCmd* cmd = commandMgr.GetCmd(kBSC_LoopReaction); + if (cmd) { + x8_reactionType = static_cast< const CBCLoopReactionCmd* >(cmd)->GetReactionType(); + xc_24_loopHit = false; + } else { + cmd = commandMgr.GetCmd(kBSC_LoopHitReaction); + x8_reactionType = static_cast< const CBCLoopHitReactionCmd* >(cmd)->GetReactionType(); + xc_24_loopHit = true; + } + + x4_state = pas::kLS_Begin; + const CPASAnimParmData parms(pas::kAS_LoopReaction, CPASAnimParm::FromEnum(x8_reactionType), + CPASAnimParm::FromEnum(x4_state)); + 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; + CPASAnimParmData loopParms(pas::kAS_LoopReaction, CPASAnimParm::FromEnum(x8_reactionType), + CPASAnimParm::FromEnum(x4_state)); + bc.LoopBestAnimation(parms, *mgr.Random()); + } +} + +pas::EAnimationState CBSLoopReaction::UpdateBody(float dt, CBodyController& bc, + CStateManager& mgr) { + pas::EAnimationState st = GetBodyStateTransition(dt, bc); + if (st == pas::kAS_Invalid) { + CBodyStateCmdMgr& commandMgr = bc.CommandMgr(); + + switch (x4_state) { + case pas::kLS_Begin: + if (commandMgr.GetCmd(kBSC_ExitState)) { + if (PlayExitAnimation(bc, mgr)) { + x4_state = pas::kLS_End; + } else { + x4_state = pas::kLS_Invalid; + st = pas::kAS_Locomotion; + } + } else { + if (bc.IsAnimationOver()) { + const CPASAnimParmData parms(pas::kAS_LoopReaction, + CPASAnimParm::FromEnum(s32(x8_reactionType)), + CPASAnimParm::FromEnum(1)); + 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 (commandMgr.GetCmd(kBSC_ExitState)) { + if (PlayExitAnimation(bc, mgr)) { + x4_state = pas::kLS_End; + } else { + x4_state = pas::kLS_Invalid; + st = pas::kAS_Locomotion; + } + } else if (commandMgr.GetTargetVector().IsNonZero()) { + bc.FaceDirection(commandMgr.GetTargetVector(), dt); + } + break; + case pas::kLS_End: + if (bc.IsAnimationOver()) { + x4_state = pas::kLS_Invalid; + st = pas::kAS_Locomotion; + } + break; + default: + break; + } + } + return st; +} + +bool CBSLoopReaction::PlayExitAnimation(CBodyController& bc, CStateManager& mgr) const { + const CPASDatabase& db = bc.GetPASDatabase(); + + const CPASAnimParmData parms(pas::kAS_LoopReaction, CPASAnimParm::FromEnum(int(x8_reactionType)), + CPASAnimParm::FromEnum(2)); + const rstl::pair< float, int > best = db.FindBestAnimation(parms, *mgr.Random(), -1); + if (best.first > 0.f) { + const CAnimPlaybackParms playParms(best.second, -1, 1.f, true); + bc.SetCurrentAnimation(playParms, false, false); + return true; + } + return false; +} +pas::EAnimationState CBSLoopReaction::GetBodyStateTransition(float dt, CBodyController& bc) { + CBodyStateCmdMgr& commandMgr = bc.CommandMgr(); + if (commandMgr.GetCmd(kBSC_Hurled)) { + return pas::kAS_Hurled; + } + if (commandMgr.GetCmd(kBSC_KnockDown)) { + return pas::kAS_Fall; + } + if (commandMgr.GetCmd(kBSC_KnockBack)) { + return pas::kAS_KnockBack; + } + if (!xc_24_loopHit && commandMgr.GetCmd(kBSC_Locomotion)) { + return pas::kAS_Locomotion; + } + + if (x4_state == pas::kLS_End) { + if (commandMgr.GetCmd(kBSC_MeleeAttack)) { + return pas::kAS_MeleeAttack; + } + if (commandMgr.GetCmd(kBSC_ProjectileAttack)) { + return pas::kAS_ProjectileAttack; + } + if (commandMgr.GetCmd(kBSC_LoopAttack)) { + return pas::kAS_LoopAttack; + } + if (commandMgr.GetCmd(kBSC_Step)) { + return pas::kAS_Step; + } + if (commandMgr.GetMoveVector().IsNonZero()) { + return pas::kAS_Locomotion; + } + if (commandMgr.GetFaceVector().IsNonZero()) { + return pas::kAS_Turn; + } + } + return pas::kAS_Invalid; +}