prime/src/MetroidPrime/BodyState/CBodyController.cpp

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);
}