diff --git a/asm/MetroidPrime/BodyState/CBSProjectileAttack.s b/asm/MetroidPrime/BodyState/CBSProjectileAttack.s index e6f8c46f..ef5ddbc4 100644 --- a/asm/MetroidPrime/BodyState/CBSProjectileAttack.s +++ b/asm/MetroidPrime/BodyState/CBSProjectileAttack.s @@ -3,8 +3,8 @@ .section .data .balign 8 -.global lbl_803E27D0 -lbl_803E27D0: +.global __vt__19CBSProjectileAttack +__vt__19CBSProjectileAttack: # ROM: 0x3DF7D0 .4byte 0 .4byte 0 @@ -31,8 +31,8 @@ __dt__19CBSProjectileAttackFv: /* 8014D3D0 0014A330 93 E1 00 0C */ stw r31, 0xc(r1) /* 8014D3D4 0014A334 7C 7F 1B 79 */ or. r31, r3, r3 /* 8014D3D8 0014A338 41 82 00 30 */ beq lbl_8014D408 -/* 8014D3DC 0014A33C 3C 60 80 3E */ lis r3, lbl_803E27D0@ha -/* 8014D3E0 0014A340 38 03 27 D0 */ addi r0, r3, lbl_803E27D0@l +/* 8014D3DC 0014A33C 3C 60 80 3E */ lis r3, __vt__19CBSProjectileAttack@ha +/* 8014D3E0 0014A340 38 03 27 D0 */ addi r0, r3, __vt__19CBSProjectileAttack@l /* 8014D3E4 0014A344 90 1F 00 00 */ stw r0, 0(r31) /* 8014D3E8 0014A348 41 82 00 10 */ beq lbl_8014D3F8 /* 8014D3EC 0014A34C 3C 60 80 3E */ lis r3, __vt__10CBodyState@ha @@ -413,10 +413,10 @@ lbl_8014D908: .global __ct__19CBSProjectileAttackFv __ct__19CBSProjectileAttackFv: /* 8014D92C 0014A88C 3C A0 80 3E */ lis r5, __vt__10CBodyState@ha -/* 8014D930 0014A890 3C 80 80 3E */ lis r4, lbl_803E27D0@ha +/* 8014D930 0014A890 3C 80 80 3E */ lis r4, __vt__19CBSProjectileAttack@ha /* 8014D934 0014A894 38 A5 13 18 */ addi r5, r5, __vt__10CBodyState@l /* 8014D938 0014A898 90 A3 00 00 */ stw r5, 0(r3) -/* 8014D93C 0014A89C 38 04 27 D0 */ addi r0, r4, lbl_803E27D0@l +/* 8014D93C 0014A89C 38 04 27 D0 */ addi r0, r4, __vt__19CBSProjectileAttack@l /* 8014D940 0014A8A0 90 03 00 00 */ stw r0, 0(r3) /* 8014D944 0014A8A4 4E 80 00 20 */ blr diff --git a/configure.py b/configure.py index e9d84139..ed763e18 100755 --- a/configure.py +++ b/configure.py @@ -213,7 +213,7 @@ LIBS = [ ["MetroidPrime/BodyState/CBSScripted", True], "MetroidPrime/Enemies/CPuddleToadGamma", ["MetroidPrime/ScriptObjects/CScriptDistanceFog", False], - "MetroidPrime/BodyState/CBSProjectileAttack", + ["MetroidPrime/BodyState/CBSProjectileAttack", True], "MetroidPrime/Weapons/CPowerBomb", ["MetroidPrime/Enemies/CMetaree", False], ["MetroidPrime/ScriptObjects/CScriptDockAreaChange", False], diff --git a/include/MetroidPrime/BodyState/CBSProjectileAttack.hpp b/include/MetroidPrime/BodyState/CBSProjectileAttack.hpp new file mode 100644 index 00000000..6c9d85a4 --- /dev/null +++ b/include/MetroidPrime/BodyState/CBSProjectileAttack.hpp @@ -0,0 +1,20 @@ +#ifndef _CBSPROJECTILEATTACK +#define _CBSPROJECTILEATTACK + +#include "MetroidPrime/BodyState/CBodyState.hpp" + +class CBSProjectileAttack : public CBodyState { +public: + CBSProjectileAttack(); + ~CBSProjectileAttack(); + + 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; + +private: + pas::EAnimationState GetBodyStateTransition(float dt, CBodyController& bc); +}; + +#endif // _CBSPROJECTILEATTACK diff --git a/obj_files.mk b/obj_files.mk index fae76cb7..b4707a54 100644 --- a/obj_files.mk +++ b/obj_files.mk @@ -178,7 +178,7 @@ METROIDPRIME :=\ $(BUILD_DIR)/src/MetroidPrime/BodyState/CBSScripted.o\ $(BUILD_DIR)/asm/MetroidPrime/Enemies/CPuddleToadGamma.o\ $(BUILD_DIR)/asm/MetroidPrime/ScriptObjects/CScriptDistanceFog.o\ - $(BUILD_DIR)/asm/MetroidPrime/BodyState/CBSProjectileAttack.o\ + $(BUILD_DIR)/src/MetroidPrime/BodyState/CBSProjectileAttack.o\ $(BUILD_DIR)/asm/MetroidPrime/Weapons/CPowerBomb.o\ $(BUILD_DIR)/asm/MetroidPrime/Enemies/CMetaree.o\ $(BUILD_DIR)/asm/MetroidPrime/ScriptObjects/CScriptDockAreaChange.o\ diff --git a/src/MetroidPrime/BodyState/CBSProjectileAttack.cpp b/src/MetroidPrime/BodyState/CBSProjectileAttack.cpp new file mode 100644 index 00000000..97aaf544 --- /dev/null +++ b/src/MetroidPrime/BodyState/CBSProjectileAttack.cpp @@ -0,0 +1,94 @@ +#include "MetroidPrime/BodyState/CBSProjectileAttack.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/CAbsAngle.hpp" + +CBSProjectileAttack::CBSProjectileAttack() {} + +void CBSProjectileAttack::Start(CBodyController& bc, CStateManager& mgr) override { + const CBCProjectileAttackCmd* cmd = + static_cast< const CBCProjectileAttackCmd* >(bc.CommandMgr().GetCmd(kBSC_ProjectileAttack)); + + CActor& actor = bc.GetOwner(); + CVector3f diff = cmd->GetTargetPosition() - actor.GetTranslation(); + CVector3f localDelta = actor.GetTransform().TransposeRotate(diff); + + CAbsAngle angle = CAbsAngle::FromRadians(atan2(localDelta.GetY(), localDelta.GetX())); + const float attackAngle = angle.AsDegrees(); + + const CPASDatabase& db = bc.GetPASDatabase(); + + const CPASAnimParmData parms( + pas::kAS_ProjectileAttack, CPASAnimParm::FromEnum(cmd->GetAttackSeverity()), + CPASAnimParm::FromReal32(angle.AsDegrees()), CPASAnimParm::FromEnum(bc.GetLocomotionType())); + + rstl::pair< float, int > best1 = db.FindBestAnimation(parms, *mgr.Random(), -1); + if (cmd->BlendTwoClosest()) { + rstl::pair< float, int > best2 = db.FindBestAnimation(parms, *mgr.Random(), best1.second); + const CPASAnimState* projAttackState = db.GetAnimState(pas::kAS_ProjectileAttack); + + CPASAnimParm parmData1(projAttackState->GetAnimParmData(best1.second, 1)); + CPASAnimParm parmData2(projAttackState->GetAnimParmData(best2.second, 1)); + + float angle1 = parmData1.GetReal32Value(); + float angle2 = parmData2.GetReal32Value(); + if (angle1 - angle2 > 180.f) { + angle2 = angle2 + 360.f; + } else if (angle2 - angle1 > 180.f) { + angle1 = angle1 + 360.f; + } + const CAnimPlaybackParms playParms(best1.second, best2.second, + (angle1 - attackAngle) / (angle1 - angle2), true); + bc.SetCurrentAnimation(playParms, false, false); + } else { + const CAnimPlaybackParms playParms(best1.second, -1, 1.f, true); + bc.SetCurrentAnimation(playParms, false, false); + } +} + +pas::EAnimationState CBSProjectileAttack::UpdateBody(float dt, CBodyController& bc, + CStateManager& mgr) override { + const pas::EAnimationState st = GetBodyStateTransition(dt, bc); + if (st == pas::kAS_Invalid) { + CBodyStateCmdMgr& cmdMgr = bc.CommandMgr(); + if (cmdMgr.GetTargetVector().IsNonZero()) { + bc.FaceDirection(cmdMgr.GetTargetVector(), dt); + } + } + return st; +} + +void CBSProjectileAttack::Shutdown(CBodyController&) override {} + +pas::EAnimationState CBSProjectileAttack::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 (bc.IsAnimationOver() || cmdMgr.GetCmd(kBSC_NextState)) { + return pas::kAS_Locomotion; + } + return pas::kAS_Invalid; +} + +bool CBSProjectileAttack::CanShoot() const override { return true; } + +CBSProjectileAttack::~CBSProjectileAttack() {}