diff --git a/asm/MetroidPrime/BodyState/CBSGroundHit.s b/asm/MetroidPrime/BodyState/CBSGroundHit.s index 18796e35..2a7b5e94 100644 --- a/asm/MetroidPrime/BodyState/CBSGroundHit.s +++ b/asm/MetroidPrime/BodyState/CBSGroundHit.s @@ -3,8 +3,8 @@ .section .data .balign 8 -.global lbl_803E18B0 -lbl_803E18B0: +.global __vt__12CBSGroundHit +__vt__12CBSGroundHit: # ROM: 0x3DE8B0 .4byte 0 .4byte 0 @@ -31,8 +31,8 @@ __dt__12CBSGroundHitFv: /* 8013ED68 0013BCC8 93 E1 00 0C */ stw r31, 0xc(r1) /* 8013ED6C 0013BCCC 7C 7F 1B 79 */ or. r31, r3, r3 /* 8013ED70 0013BCD0 41 82 00 30 */ beq lbl_8013EDA0 -/* 8013ED74 0013BCD4 3C 60 80 3E */ lis r3, lbl_803E18B0@ha -/* 8013ED78 0013BCD8 38 03 18 B0 */ addi r0, r3, lbl_803E18B0@l +/* 8013ED74 0013BCD4 3C 60 80 3E */ lis r3, __vt__12CBSGroundHit@ha +/* 8013ED78 0013BCD8 38 03 18 B0 */ addi r0, r3, __vt__12CBSGroundHit@l /* 8013ED7C 0013BCDC 90 1F 00 00 */ stw r0, 0(r31) /* 8013ED80 0013BCE0 41 82 00 10 */ beq lbl_8013ED90 /* 8013ED84 0013BCE4 3C 60 80 3E */ lis r3, __vt__10CBodyState@ha @@ -407,11 +407,11 @@ lbl_8013F2A4: .global __ct__12CBSGroundHitFv __ct__12CBSGroundHitFv: /* 8013F2C0 0013C220 3C A0 80 3E */ lis r5, __vt__10CBodyState@ha -/* 8013F2C4 0013C224 3C 80 80 3E */ lis r4, lbl_803E18B0@ha +/* 8013F2C4 0013C224 3C 80 80 3E */ lis r4, __vt__12CBSGroundHit@ha /* 8013F2C8 0013C228 38 05 13 18 */ addi r0, r5, __vt__10CBodyState@l /* 8013F2CC 0013C22C C0 02 9B 28 */ lfs f0, lbl_805AB848@sda21(r2) /* 8013F2D0 0013C230 90 03 00 00 */ stw r0, 0(r3) -/* 8013F2D4 0013C234 38 84 18 B0 */ addi r4, r4, lbl_803E18B0@l +/* 8013F2D4 0013C234 38 84 18 B0 */ addi r4, r4, __vt__12CBSGroundHit@l /* 8013F2D8 0013C238 38 00 FF FF */ li r0, -1 /* 8013F2DC 0013C23C 90 83 00 00 */ stw r4, 0(r3) /* 8013F2E0 0013C240 D0 03 00 04 */ stfs f0, 4(r3) diff --git a/configure.py b/configure.py index 6b19a274..c8b7e22b 100755 --- a/configure.py +++ b/configure.py @@ -198,7 +198,7 @@ LIBS = [ "MetroidPrime/Weapons/CTargetableProjectile", "MetroidPrime/BodyState/CBSLoopReaction", "MetroidPrime/CSteeringBehaviors", - "MetroidPrime/BodyState/CBSGroundHit", + ["MetroidPrime/BodyState/CBSGroundHit", False], "MetroidPrime/Enemies/CChozoGhost", "MetroidPrime/Enemies/CFireFlea", ["MetroidPrime/BodyState/CBSSlide", False], diff --git a/include/MetroidPrime/BodyState/CBSGroundHit.hpp b/include/MetroidPrime/BodyState/CBSGroundHit.hpp new file mode 100644 index 00000000..d9f44161 --- /dev/null +++ b/include/MetroidPrime/BodyState/CBSGroundHit.hpp @@ -0,0 +1,23 @@ +#ifndef _CBSGROUNDHIT +#define _CBSGROUNDHIT + +#include "MetroidPrime/BodyState/CBodyState.hpp" + +class CBSGroundHit : public CBodyState { +public: +CBSGroundHit(); +~CBSGroundHit(); + + void Start(CBodyController& bc, CStateManager& mgr) override; + pas::EAnimationState UpdateBody(float dt, CBodyController& bc, CStateManager& mgr) override; + void Shutdown(CBodyController& bc) override; + +private: + float x4_rotateSpeed; + float x8_remTime; + pas::EFallState xc_fallState; + + pas::EAnimationState GetBodyStateTransition(float dt, CBodyController& bc); +}; + +#endif // _CBSGROUNDHIT diff --git a/include/MetroidPrime/BodyState/CBodyStateCmdMgr.hpp b/include/MetroidPrime/BodyState/CBodyStateCmdMgr.hpp index 30753e53..6f3baf51 100644 --- a/include/MetroidPrime/BodyState/CBodyStateCmdMgr.hpp +++ b/include/MetroidPrime/BodyState/CBodyStateCmdMgr.hpp @@ -186,6 +186,24 @@ private: // +class CBCKnockBackCmd : public CBodyStateCmd { +public: + explicit CBCKnockBackCmd() + : CBodyStateCmd(kBSC_KnockBack), x8_dir(0.f, 0.f, 0.f), x14_severity(pas::kS_Invalid) {} + + explicit CBCKnockBackCmd(const CVector3f& vec, pas::ESeverity severity) + : CBodyStateCmd(kBSC_KnockBack), x8_dir(vec), x14_severity(severity) {} + + const CVector3f& GetHitDirection() const { return x8_dir; } + pas::ESeverity GetHitSeverity() const { return x14_severity; } + +private: + CVector3f x8_dir; + pas::ESeverity x14_severity; +}; + +// + class CBodyStateCmdMgr { public: CBodyStateCmd* GetCmd(EBodyStateCmd cmd); diff --git a/src/MetroidPrime/BodyState/CBSFall.cpp b/src/MetroidPrime/BodyState/CBSFall.cpp index 29416d59..1cf61f8c 100644 --- a/src/MetroidPrime/BodyState/CBSFall.cpp +++ b/src/MetroidPrime/BodyState/CBSFall.cpp @@ -7,20 +7,20 @@ #include "Kyoto/Animation/CPASAnimParmData.hpp" #include "Kyoto/Animation/CPASDatabase.hpp" -#include "Kyoto/Math/CRelAngle.hpp" #include "Kyoto/Math/CAbsAngle.hpp" +#include "Kyoto/Math/CRelAngle.hpp" + #include "math.h" #include "rstl/math.hpp" - CBSFall::CBSFall() : x4_rotateSpeed(0.f), x8_remTime(0.f), xc_fallState(pas::kFS_Invalid) {} void CBSFall::Start(CBodyController& bc, CStateManager& mgr) { const CBCKnockDownCmd* cmd = static_cast< const CBCKnockDownCmd* >(bc.CommandMgr().GetCmd(kBSC_KnockDown)); CVector3f localDir = bc.GetOwner().GetTransform().TransposeRotate(cmd->GetHitDirection()); - CAbsAngle angle = CAbsAngle::FromRadians(atan2(localDir.GetY(), localDir.GetZ())); + CAbsAngle angle = CAbsAngle::FromRadians(atan2(localDir.GetY(), localDir.GetZ())); // TODO: x const CPASDatabase& db = bc.GetPASDatabase(); const CPASAnimParmData parms(pas::kAS_Fall, CPASAnimParm::FromReal32(angle.AsDegrees()), @@ -34,11 +34,16 @@ void CBSFall::Start(CBodyController& bc, CStateManager& mgr) { if (!knockdownParm2.GetBoolValue()) { CPASAnimParm knockdownParm0(knockdownState->GetAnimParmData(best.second, 0)); float knockdownAngle = knockdownParm0.GetReal32Value(); - float delta1 = CAbsAngle::FromRadians(angle.AsRadians() - CRelAngle::FromDegrees(knockdownAngle).AsRadians()).AsRadians(); - float delta2 = CAbsAngle::FromRadians(CRelAngle::FromDegrees(knockdownAngle).AsRadians() - angle.AsRadians()).AsRadians(); + float delta1 = CAbsAngle::FromRadians(angle.AsRadians() - + CRelAngle::FromDegrees(knockdownAngle).AsRadians()) + .AsRadians(); + float delta2 = CAbsAngle::FromRadians(CRelAngle::FromDegrees(knockdownAngle).AsRadians() - + angle.AsRadians()) + .AsRadians(); float minAngle = rstl::min_val(delta1, delta2); // There's a missing `if (delta1 < 0) { delta1 += M_2PIF; }` here // But it's not exactly delta1, but a temporary from inside the FromRadians call?! + // Same problem in CBSGroundHit const float flippedAngle = (delta1 > M_PIF) ? -minAngle : minAngle; x8_remTime = 0.15f * bc.GetAnimTimeRemaining(); x4_rotateSpeed = (x8_remTime > FLT_EPSILON) ? flippedAngle / x8_remTime : flippedAngle; diff --git a/src/MetroidPrime/BodyState/CBSGroundHit.cpp b/src/MetroidPrime/BodyState/CBSGroundHit.cpp new file mode 100644 index 00000000..9a4c80d8 --- /dev/null +++ b/src/MetroidPrime/BodyState/CBSGroundHit.cpp @@ -0,0 +1,81 @@ +#include "MetroidPrime/BodyState/CBSGroundHit.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" +#include "Kyoto/Math/CRelAngle.hpp" + +#include "math.h" +#include "rstl/math.hpp" + +CBSGroundHit::CBSGroundHit() +: x4_rotateSpeed(0.f), x8_remTime(0.f), xc_fallState(pas::kFS_Invalid) {} + +void CBSGroundHit::Start(CBodyController& bc, CStateManager& mgr) { + const CBCKnockBackCmd* cmd = + static_cast< const CBCKnockBackCmd* >(bc.CommandMgr().GetCmd(kBSC_KnockBack)); + + CVector3f localDir = bc.GetOwner().GetTransform().TransposeRotate(cmd->GetHitDirection()); + CAbsAngle angle = CAbsAngle::FromRadians(atan2(localDir.GetY(), localDir.GetX())); + + int fallState = bc.GetFallState(); + const CPASDatabase& db = bc.GetPASDatabase(); + + const CPASAnimParmData parms(pas::kAS_GroundHit, CPASAnimParm::FromEnum(fallState), + CPASAnimParm::FromReal32(angle.AsDegrees())); + const rstl::pair< float, int > best = db.FindBestAnimation(parms, *mgr.Random(), -1); + const CAnimPlaybackParms playParms(best.second, -1, 1.f, true); + bc.SetCurrentAnimation(playParms, false, false); + + const CPASAnimState* groundHitState = db.GetAnimState(pas::kAS_GroundHit); + CPASAnimParm parm2(groundHitState->GetAnimParmData(best.second, 2)); + if (!parm2.GetBoolValue()) { + CPASAnimParm parm1(groundHitState->GetAnimParmData(best.second, 1)); + float knockdownAngle = parm1.GetReal32Value(); + float delta1 = CAbsAngle::FromRadians(angle.AsRadians() - + CRelAngle::FromDegrees(knockdownAngle).AsRadians()) + .AsRadians(); + float delta2 = CAbsAngle::FromRadians(CRelAngle::FromDegrees(knockdownAngle).AsRadians() - + angle.AsRadians()) + .AsRadians(); + float minAngle = rstl::min_val(delta1, delta2); + // There's missing code here. Same problem in CBSFall, see there for details + const float flippedAngle = (delta1 > M_PIF) ? -minAngle : minAngle; + x8_remTime = 0.15f * bc.GetAnimTimeRemaining(); + x4_rotateSpeed = (x8_remTime > FLT_EPSILON) ? flippedAngle / x8_remTime : flippedAngle; + } else { + x8_remTime = 0.f; + x4_rotateSpeed = 0.f; + } + CPASAnimParm parm3(groundHitState->GetAnimParmData(best.second, 3)); + xc_fallState = pas::EFallState(parm3.GetEnumValue()); +} + +pas::EAnimationState CBSGroundHit::UpdateBody(float dt, CBodyController& bc, CStateManager& mgr) { + const pas::EAnimationState st = GetBodyStateTransition(dt, bc); + if (st == pas::kAS_Invalid && x8_remTime > 0.f) { + bc.SetDeltaRotation(CQuaternion::ZRotation(CRelAngle::FromRadians(x4_rotateSpeed * dt))); + x8_remTime -= dt; + } + return st; +} + +void CBSGroundHit::Shutdown(CBodyController& bc) { bc.SetFallState(xc_fallState); } + +pas::EAnimationState CBSGroundHit::GetBodyStateTransition(float dt, CBodyController& bc) { + CBodyStateCmdMgr& cmdMgr = bc.CommandMgr(); + if (bc.IsAnimationOver()) { + if (cmdMgr.GetCmd(kBSC_Die)) { + return pas::kAS_Death; + } + return pas::kAS_LieOnGround; + } + return pas::kAS_Invalid; +} + +CBSGroundHit::~CBSGroundHit() {}