#include "Runtime/Character/CBodyController.hpp" #include "Runtime/CStateManager.hpp" #include "Runtime/Character/CPASAnimParm.hpp" #include "Runtime/Character/CPASAnimParmData.hpp" #include "Runtime/World/CActor.hpp" #include "Runtime/World/CActorModelParticles.hpp" #include "Runtime/World/CPhysicsActor.hpp" #include "TCastTo.hpp" // Generated file, do not modify include path namespace metaforce { CBodyController::CBodyController(CActor& actor, float turnSpeed, EBodyType bodyType) : x0_actor(actor) , x2a4_bodyStateInfo(actor, bodyType) , x2f4_bodyType(bodyType) , x2fc_turnSpeed(turnSpeed) { x2a4_bodyStateInfo.x18_bodyController = this; } void CBodyController::EnableAnimation(bool enable) { x0_actor.GetModelData()->GetAnimationData()->EnableAnimation(enable); } 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::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::EAnimationState::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::EAnimationState::Invalid) { x2a4_bodyStateInfo.GetCurrentAdditiveState()->Shutdown(*this); x2a4_bodyStateInfo.SetAdditiveState(nextState); x2a4_bodyStateInfo.GetCurrentAdditiveState()->Start(*this, mgr); } } } void CBodyController::SetTurnSpeed(float speed) { x2fc_turnSpeed = std::max(0.f, speed); } void CBodyController::Update(float dt, CStateManager& mgr) { SetPlaybackRate(1.f); if (x300_25_active) { x300_24_animationOver = !x0_actor.GetModelData()->GetAnimationData()->IsAnimTimeRemaining(dt, "Whole Body"sv); x4_cmdMgr.BlendSteeringCmds(); x2dc_rot = zeus::CQuaternion(); UpdateBody(dt, mgr); if (TCastToPtr act = x0_actor) act->RotateInOneFrameOR(x2dc_rot, dt); x4_cmdMgr.Reset(); } } bool CBodyController::HasBodyState(pas::EAnimationState state) const { return GetPASDatabase().HasState(state); } void CBodyController::SetCurrentAnimation(const CAnimPlaybackParms& parms, bool loop, bool noTrans) { x0_actor.GetModelData()->GetAnimationData()->SetAnimation(parms, noTrans); x0_actor.GetModelData()->EnableLooping(loop); x2f8_curAnim = parms.GetAnimationId(); } float CBodyController::GetAnimTimeRemaining() const { return x0_actor.GetModelData()->GetAnimationData()->GetAnimTimeRemaining("Whole Body"); } void CBodyController::SetPlaybackRate(float rate) { x0_actor.GetModelData()->GetAnimationData()->SetPlaybackRate(rate); } // GX uses a HW approximation of 3/8 + 5/8 instead of 1/3 + 2/3. const CPASDatabase& CBodyController::GetPASDatabase() const { return x0_actor.GetModelData()->GetAnimationData()->GetCharacterInfo().GetPASDatabase(); } void CBodyController::MultiplyPlaybackRate(float mul) { x0_actor.GetModelData()->GetAnimationData()->MultiplyPlaybackRate(mul); } void CBodyController::FaceDirection(const zeus::CVector3f& v0, float dt) { if (x300_26_frozen) return; zeus::CVector3f noZ = v0; noZ.z() = 0.f; if (noZ.canBeNormalized()) { if (TCastToPtr act = x0_actor) { zeus::CQuaternion rot = zeus::CQuaternion::lookAt(act->GetTransform().basis[1], noZ.normalized(), zeus::degToRad(dt * x2fc_turnSpeed)); rot.setImaginary(act->GetTransform().transposeRotate(rot.getImaginary())); act->RotateInOneFrameOR(rot, dt); } } } void CBodyController::FaceDirection3D(const zeus::CVector3f& v0, const zeus::CVector3f& v1, float dt) { if (x300_26_frozen) return; if (v0.canBeNormalized() && v1.canBeNormalized()) { if (TCastToPtr act = x0_actor) { zeus::CUnitVector3f uv0 = v0; zeus::CUnitVector3f uv1 = v1; float dot = uv0.dot(uv1); if (!zeus::close_enough(dot, 1.f)) { if (dot < -0.9999f) { zeus::CQuaternion rot = zeus::CQuaternion::fromAxisAngle(act->GetTransform().basis[2], zeus::degToRad(dt * x2fc_turnSpeed)); rot.setImaginary(act->GetTransform().transposeRotate(rot.getImaginary())); act->RotateInOneFrameOR(rot, dt); } else { zeus::CQuaternion rot = zeus::CQuaternion::clampedRotateTo(uv1, uv0, zeus::degToRad(dt * x2fc_turnSpeed)); rot.setImaginary(x0_actor.GetTransform().transposeRotate(rot.getImaginary())); act->RotateInOneFrameOR(rot, dt); } } } } } bool CBodyController::HasBodyInfo(const CActor& actor) { return actor.GetModelData()->GetAnimationData()->GetCharacterInfo().GetPASDatabase().GetNumAnimStates() != 0; } void CBodyController::PlayBestAnimation(const CPASAnimParmData& parms, CRandom16& r) { std::pair 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) { std::pair 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; if (TCastToPtr act = x0_actor) { x314_backedUpForce = act->GetConstantForce(); act->SetConstantForce(zeus::skZero3f); act->SetMomentumWR(zeus::skZero3f); } x320_fireDur = 0.f; x328_timeOnFire = 0.f; x310_timeFrozen = 0.f; } 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(1.f); if (TCastToPtr act = x0_actor) { act->SetConstantForce(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) return; x320_fireDur = 0.f; x328_timeOnFire = 0.f; } void CBodyController::SetElectrocuting(float duration) { if (!IsElectrocuting()) { CBCAdditiveReactionCmd reaction(pas::EAdditiveReactionType::Electrocution, 1.f, true); x4_cmdMgr.DeliverCmd(reaction); } x324_electrocutionDur = duration; x32c_timeElectrocuting = 0.f; if (IsFrozen()) UnFreeze(); else if (IsOnFire()) DouseFlames(); } void CBodyController::DouseElectrocuting() { x324_electrocutionDur = 0.f; x32c_timeElectrocuting = 0.f; CBodyStateCmd cmd(EBodyStateCmd::StopReaction); x4_cmdMgr.DeliverCmd(cmd); } void CBodyController::UpdateFrozenInfo(float dt, CStateManager& mgr) { if (x300_26_frozen) { float totalTime = x304_intoFreezeDur + x308_frozenDur + x30c_breakoutDur; if (x310_timeFrozen > totalTime && x2a4_bodyStateInfo.GetCurrentAdditiveStateId() != pas::EAnimationState::AdditiveReaction) { UnFreeze(); x0_actor.SendScriptMsgs(EScriptObjectState::UnFrozen, mgr, EScriptObjectMessage::None); mgr.GetActorModelParticles()->StartIce(x0_actor); return; } if (x310_timeFrozen <= totalTime) { float percUnfrozen = 1.f; if (x310_timeFrozen < totalTime - x30c_breakoutDur) percUnfrozen = 1.f - GetPercentageFrozen(); MultiplyPlaybackRate(percUnfrozen); x310_timeFrozen += dt; x0_actor.SetVolume(percUnfrozen); if (x310_timeFrozen > totalTime && HasIceBreakoutState()) { CBCAdditiveReactionCmd cmd(pas::EAdditiveReactionType::IceBreakout, 1.f, false); x4_cmdMgr.DeliverCmd(cmd); } } } } bool CBodyController::HasIceBreakoutState() const { CPASAnimParmData parms(pas::EAnimationState::AdditiveReaction, CPASAnimParm::FromEnum(static_cast(pas::EAdditiveReactionType::IceBreakout))); std::pair best = GetPASDatabase().FindBestAnimation(parms, -1); return best.first > 0.f; } void CBodyController::StopElectrocution() { x324_electrocutionDur = 0.f; x32c_timeElectrocuting = 0.f; x4_cmdMgr.DeliverCmd(CBodyStateCmd(EBodyStateCmd::StopReaction)); } void CBodyController::FrozenBreakout() { if (x300_26_frozen) { float timeToBreakout = x304_intoFreezeDur + x308_frozenDur; if (x310_timeFrozen < timeToBreakout) x310_timeFrozen = timeToBreakout; } } } // namespace metaforce