mirror of https://github.com/PrimeDecomp/prime.git
248 lines
7.9 KiB
C++
248 lines
7.9 KiB
C++
|
#include "MetroidPrime/BodyState/CBodyController.hpp"
|
||
|
|
||
|
#include "MetroidPrime/BodyState/CAdditiveBodyState.hpp"
|
||
|
#include "MetroidPrime/BodyState/CBodyState.hpp"
|
||
|
#include "MetroidPrime/CActor.hpp"
|
||
|
#include "MetroidPrime/CAnimData.hpp"
|
||
|
#include "MetroidPrime/CAnimPlaybackParms.hpp"
|
||
|
#include "MetroidPrime/CModelData.hpp"
|
||
|
#include "MetroidPrime/CPhysicsActor.hpp"
|
||
|
#include "MetroidPrime/TCastTo.hpp"
|
||
|
|
||
|
#include "rstl/math.hpp"
|
||
|
|
||
|
#include "Kyoto/Animation/CPASDatabase.hpp"
|
||
|
|
||
|
CBodyController::CBodyController(CActor& actor, float turnSpeed, EBodyType bodyType)
|
||
|
: x0_actor(&actor)
|
||
|
, x2a4_bodyStateInfo(actor, bodyType)
|
||
|
, x2dc_rot(CQuaternion::NoRotation())
|
||
|
, x2ec_locomotionType(pas::kLT_Relaxed)
|
||
|
, x2f0_fallState(pas::kFS_Zero)
|
||
|
, x2f4_bodyType(bodyType)
|
||
|
, x2f8_curAnim(-1)
|
||
|
, x2fc_turnSpeed(turnSpeed)
|
||
|
, x300_24_animationOver(false)
|
||
|
, x300_25_active(false)
|
||
|
, x300_26_frozen(false)
|
||
|
, x300_27_hasBeenFrozen(false)
|
||
|
, x300_28_playDeathAnims(true)
|
||
|
, x304_intoFreezeDur(0.f)
|
||
|
, x308_frozenDur(0.f)
|
||
|
, x30c_breakoutDur(0.f)
|
||
|
, x310_timeFrozen(0.f)
|
||
|
, x314_backedUpForce(CVector3f::Zero())
|
||
|
, x320_fireDur(0.f)
|
||
|
, x324_electrocutionDur(0.f)
|
||
|
, x328_timeOnFire(0.f)
|
||
|
, x32c_timeElectrocuting(0.f)
|
||
|
, x330_restrictedFlyerMoveSpeed(0.f) {
|
||
|
x2a4_bodyStateInfo.SetBodyController(this);
|
||
|
}
|
||
|
|
||
|
void CBodyController::Activate(CStateManager& mgr) {
|
||
|
x300_25_active = true;
|
||
|
x2a4_bodyStateInfo.SetState(pas::EAnimationState(GetPASDatabase().GetDefaultState()));
|
||
|
x2a4_bodyStateInfo.GetCurrentState()->Start(*this, mgr);
|
||
|
x2a4_bodyStateInfo.GetCurrentAdditiveState()->Start(*this, mgr);
|
||
|
}
|
||
|
|
||
|
void CBodyController::Update(float dt, CStateManager& mgr) {
|
||
|
SetPlaybackRate(1.f);
|
||
|
|
||
|
if (!x300_25_active) {
|
||
|
return;
|
||
|
}
|
||
|
x300_24_animationOver = !GetOwner().GetModelData()->GetAnimationData()->IsAnimTimeRemaining(
|
||
|
dt, rstl::string_l("Whole Body"));
|
||
|
x4_cmdMgr.BlendSteeringCmds();
|
||
|
x2dc_rot = CQuaternion::NoRotation();
|
||
|
UpdateBody(dt, mgr);
|
||
|
if (CPhysicsActor* act = TCastToPtr< CPhysicsActor >(x0_actor))
|
||
|
act->RotateInOneFrameOR(x2dc_rot, dt);
|
||
|
x4_cmdMgr.Reset();
|
||
|
}
|
||
|
|
||
|
bool CBodyController::HasBodyState(pas::EAnimationState state) const {
|
||
|
return GetOwner().GetModelData()->GetAnimationData()->GetPASDatabase().HasState((int)state);
|
||
|
}
|
||
|
|
||
|
pas::EFallState CBodyController::GetFallState() const { return x2f0_fallState; }
|
||
|
|
||
|
void CBodyController::SetFallState(pas::EFallState state) { x2f0_fallState = state; }
|
||
|
|
||
|
void CBodyController::UpdateBody(float dt, CStateManager& mgr) {
|
||
|
UpdateFrozenInfo(dt, mgr);
|
||
|
if (x320_fireDur > 0.f) {
|
||
|
if (x328_timeOnFire > x320_fireDur) {
|
||
|
x328_timeOnFire = 0.f;
|
||
|
x320_fireDur = 0.f;
|
||
|
} else {
|
||
|
x328_timeOnFire += dt;
|
||
|
}
|
||
|
} else if (x324_electrocutionDur > 0.f) {
|
||
|
if (x32c_timeElectrocuting > x324_electrocutionDur) {
|
||
|
x32c_timeElectrocuting = 0.f;
|
||
|
x324_electrocutionDur = 0.f;
|
||
|
} else {
|
||
|
x32c_timeElectrocuting += dt;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (GetPercentageFrozen() < 1.f && x300_28_playDeathAnims) {
|
||
|
pas::EAnimationState nextState =
|
||
|
x2a4_bodyStateInfo.GetCurrentState()->UpdateBody(dt, *this, mgr);
|
||
|
if (nextState != pas::kAS_Invalid) {
|
||
|
x2a4_bodyStateInfo.GetCurrentState()->Shutdown(*this);
|
||
|
x2a4_bodyStateInfo.SetState(nextState);
|
||
|
x2a4_bodyStateInfo.GetCurrentState()->Start(*this, mgr);
|
||
|
}
|
||
|
|
||
|
nextState = x2a4_bodyStateInfo.GetCurrentAdditiveState()->UpdateBody(dt, *this, mgr);
|
||
|
if (nextState != pas::kAS_Invalid) {
|
||
|
x2a4_bodyStateInfo.GetCurrentAdditiveState()->Shutdown(*this);
|
||
|
x2a4_bodyStateInfo.SetAdditiveState(nextState);
|
||
|
x2a4_bodyStateInfo.GetCurrentAdditiveState()->Start(*this, mgr);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void CBodyController::SetLocomotionType(pas::ELocomotionType type) { x2ec_locomotionType = type; }
|
||
|
|
||
|
void CBodyController::SetTurnSpeed(float speed) { x2fc_turnSpeed = rstl::max_val(0.f, speed); }
|
||
|
|
||
|
void CBodyController::EnableAnimation(bool enable) {
|
||
|
GetOwner().ModelData()->AnimationData()->SetIsAnimating(enable);
|
||
|
}
|
||
|
|
||
|
void CBodyController::SetCurrentAnimation(const CAnimPlaybackParms& parms, bool loop,
|
||
|
bool noTrans) {
|
||
|
GetOwner().ModelData()->AnimationData()->SetAnimation(parms, noTrans);
|
||
|
GetOwner().ModelData()->EnableLooping(loop);
|
||
|
x2f8_curAnim = parms.GetAnimationId();
|
||
|
}
|
||
|
|
||
|
float CBodyController::GetAnimTimeRemaining() const {
|
||
|
return GetOwner().GetModelData()->GetAnimationData()->GetAnimTimeRemaining(
|
||
|
rstl::string_l("Whole Body"));
|
||
|
}
|
||
|
|
||
|
void CBodyController::SetPlaybackRate(float rate) {
|
||
|
GetOwner().ModelData()->AnimationData()->SetPlaybackRate(rate);
|
||
|
}
|
||
|
|
||
|
void CBodyController::MultiplyPlaybackRate(float mul) {
|
||
|
GetOwner().ModelData()->AnimationData()->MultiplyPlaybackRate(mul);
|
||
|
}
|
||
|
|
||
|
void CBodyController::SetDeltaRotation(const CQuaternion& q) { x2dc_rot = x2dc_rot * q; }
|
||
|
|
||
|
void CBodyController::FaceDirection(const CVector3f& v0, float dt) {}
|
||
|
|
||
|
void CBodyController::FaceDirection3D(const CVector3f& v0, float dt) {}
|
||
|
|
||
|
const CPASDatabase& CBodyController::GetPASDatabase() const {
|
||
|
return GetOwner().GetModelData()->GetAnimationData()->GetPASDatabase();
|
||
|
}
|
||
|
|
||
|
void CBodyController::PlayBestAnimation(const CPASAnimParmData& parms, CRandom16& r) {
|
||
|
rstl::pair< float, int > best = GetPASDatabase().FindBestAnimation(parms, r, -1);
|
||
|
CAnimPlaybackParms playParms(best.second, -1, 1.f, true);
|
||
|
SetCurrentAnimation(playParms, false, false);
|
||
|
}
|
||
|
|
||
|
void CBodyController::LoopBestAnimation(const CPASAnimParmData& parms, CRandom16& r) {
|
||
|
rstl::pair< float, int > best = GetPASDatabase().FindBestAnimation(parms, r, -1);
|
||
|
CAnimPlaybackParms playParms(best.second, -1, 1.f, true);
|
||
|
SetCurrentAnimation(playParms, true, false);
|
||
|
}
|
||
|
|
||
|
void CBodyController::Freeze(float intoFreezeDur, float frozenDur, float breakoutDur) {
|
||
|
x304_intoFreezeDur = intoFreezeDur;
|
||
|
x308_frozenDur = frozenDur;
|
||
|
x30c_breakoutDur = breakoutDur;
|
||
|
x300_26_frozen = true;
|
||
|
x300_27_hasBeenFrozen = true;
|
||
|
|
||
|
CPhysicsActor* act = TCastToPtr< CPhysicsActor >(GetOwner());
|
||
|
x314_backedUpForce = act->GetConstantForceWR();
|
||
|
act->SetConstantForceWR(CVector3f::Zero());
|
||
|
act->SetMomentumWR(CVector3f::Zero());
|
||
|
|
||
|
x320_fireDur = 0.f;
|
||
|
x328_timeOnFire = 0.f;
|
||
|
x310_timeFrozen = 0.f;
|
||
|
}
|
||
|
|
||
|
void CBodyController::FrozenBreakout() {
|
||
|
if (x300_26_frozen) {
|
||
|
float timeToBreakout = x304_intoFreezeDur + x308_frozenDur;
|
||
|
if (x310_timeFrozen < timeToBreakout)
|
||
|
x310_timeFrozen = timeToBreakout;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void CBodyController::UnFreeze() {
|
||
|
SetPlaybackRate(1.f);
|
||
|
x300_26_frozen = false;
|
||
|
x304_intoFreezeDur = 0.f;
|
||
|
x308_frozenDur = 0.f;
|
||
|
x30c_breakoutDur = 0.f;
|
||
|
x310_timeFrozen = 0.f;
|
||
|
x0_actor->SetVolume(127);
|
||
|
CPhysicsActor* act = TCastToPtr< CPhysicsActor >(GetOwner());
|
||
|
act->SetConstantForceWR(x314_backedUpForce);
|
||
|
act->SetVelocityWR(x314_backedUpForce * (1.f / act->GetMass()));
|
||
|
}
|
||
|
|
||
|
float CBodyController::GetPercentageFrozen() const {
|
||
|
float sum = x304_intoFreezeDur + x308_frozenDur + x30c_breakoutDur;
|
||
|
if (x310_timeFrozen == 0.f || sum == 0.f)
|
||
|
return 0.f;
|
||
|
|
||
|
if (x310_timeFrozen <= x304_intoFreezeDur && x304_intoFreezeDur > 0.f)
|
||
|
return x310_timeFrozen / x304_intoFreezeDur;
|
||
|
|
||
|
if (x310_timeFrozen < sum - x30c_breakoutDur)
|
||
|
return 1.f;
|
||
|
if (x30c_breakoutDur <= 0.f)
|
||
|
return 1.f;
|
||
|
|
||
|
return 1.f - (x310_timeFrozen - (x308_frozenDur + x304_intoFreezeDur)) / x30c_breakoutDur;
|
||
|
}
|
||
|
|
||
|
void CBodyController::SetOnFire(float duration) {
|
||
|
x320_fireDur = duration;
|
||
|
x328_timeOnFire = 0.f;
|
||
|
if (IsFrozen()) {
|
||
|
UnFreeze();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void CBodyController::DouseFlames() {
|
||
|
if (x320_fireDur > 0.f) {
|
||
|
x320_fireDur = 0.f;
|
||
|
x328_timeOnFire = 0.f;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void CBodyController::SetElectrocuting(float duration) {
|
||
|
if (!IsElectrocuting()) {
|
||
|
CBCAdditiveReactionCmd reaction(pas::kART_Electrocution, 1.f, true);
|
||
|
x4_cmdMgr.DeliverCmd(reaction);
|
||
|
}
|
||
|
x324_electrocutionDur = duration;
|
||
|
x32c_timeElectrocuting = 0.f;
|
||
|
if (IsFrozen())
|
||
|
UnFreeze();
|
||
|
else if (IsOnFire())
|
||
|
DouseFlames();
|
||
|
}
|
||
|
|
||
|
void CBodyController::StopElectrocution() {
|
||
|
x324_electrocutionDur = 0.f;
|
||
|
x32c_timeElectrocuting = 0.f;
|
||
|
CBodyStateCmd cmd(kBSC_StopReaction);
|
||
|
x4_cmdMgr.DeliverCmd(cmd);
|
||
|
}
|