From 56bfc276d6e1afb80045186d1dd6de1971b26088 Mon Sep 17 00:00:00 2001 From: Jack Andersen Date: Sun, 9 Jul 2017 18:55:51 -1000 Subject: [PATCH] Implement body state classes --- DataSpec/DNAMP1/ScriptObjects/Waypoint.hpp | 2 +- Runtime/Character/CAdditiveBodyState.cpp | 0 Runtime/Character/CAdditiveBodyState.hpp | 38 + Runtime/Character/CAnimData.cpp | 258 +- Runtime/Character/CAnimData.hpp | 2 + Runtime/Character/CAnimPlaybackParms.hpp | 33 +- Runtime/Character/CAnimSource.cpp | 14 +- Runtime/Character/CAnimSourceReader.cpp | 2 +- Runtime/Character/CAnimTreeSequence.cpp | 2 +- Runtime/Character/CAnimTreeTimeScale.cpp | 15 +- Runtime/Character/CAnimTreeTransition.cpp | 2 +- Runtime/Character/CBodyController.cpp | 81 + Runtime/Character/CBodyController.hpp | 68 +- Runtime/Character/CBodyState.cpp | 2419 ++++++++++++++++++ Runtime/Character/CBodyState.hpp | 391 ++- Runtime/Character/CBodyStateCmdMgr.cpp | 59 + Runtime/Character/CBodyStateCmdMgr.hpp | 210 +- Runtime/Character/CBodyStateInfo.cpp | 416 +++ Runtime/Character/CBodyStateInfo.hpp | 41 + Runtime/Character/CCharAnimTime.cpp | 14 +- Runtime/Character/CCharAnimTime.hpp | 17 +- Runtime/Character/CFBStreamedCompression.cpp | 2 +- Runtime/Character/CMakeLists.txt | 4 +- Runtime/Character/CMetaTransTrans.cpp | 2 +- Runtime/Character/CPASAnimInfo.hpp | 3 +- Runtime/Character/CPASAnimParmData.hpp | 12 +- Runtime/Character/CPASAnimState.cpp | 17 +- Runtime/Character/CPASDatabase.cpp | 2 +- Runtime/Character/CTimeScaleFunctions.cpp | 17 +- Runtime/Character/CharacterCommon.hpp | 149 +- Runtime/MP1/World/CBeetle.cpp | 2 +- Runtime/MP1/World/CMetroid.cpp | 2 +- Runtime/MP1/World/CMetroidBeta.cpp | 2 +- Runtime/MP1/World/CNewIntroBoss.cpp | 2 +- Runtime/MP1/World/CSpacePirate.cpp | 2 +- Runtime/MP1/World/CThardusRockProjectile.cpp | 2 +- Runtime/MP1/World/CWarWasp.cpp | 2 +- Runtime/World/CActor.cpp | 7 + Runtime/World/CActor.hpp | 1 + Runtime/World/CPatterned.hpp | 16 + Runtime/World/CPhysicsActor.hpp | 2 + Runtime/World/CScriptWaypoint.hpp | 1 + specter | 2 +- 43 files changed, 4087 insertions(+), 248 deletions(-) create mode 100644 Runtime/Character/CAdditiveBodyState.cpp create mode 100644 Runtime/Character/CAdditiveBodyState.hpp create mode 100644 Runtime/Character/CBodyStateInfo.cpp create mode 100644 Runtime/Character/CBodyStateInfo.hpp diff --git a/DataSpec/DNAMP1/ScriptObjects/Waypoint.hpp b/DataSpec/DNAMP1/ScriptObjects/Waypoint.hpp index 210254530..812fb26af 100644 --- a/DataSpec/DNAMP1/ScriptObjects/Waypoint.hpp +++ b/DataSpec/DNAMP1/ScriptObjects/Waypoint.hpp @@ -23,7 +23,7 @@ struct Waypoint : IScriptObject Value unknown6; Value unknown7; Value unknown8; - Value unknown9; + Value jumpFlags; // 0x2: single, 0x4: double Value unknown10; }; } diff --git a/Runtime/Character/CAdditiveBodyState.cpp b/Runtime/Character/CAdditiveBodyState.cpp new file mode 100644 index 000000000..e69de29bb diff --git a/Runtime/Character/CAdditiveBodyState.hpp b/Runtime/Character/CAdditiveBodyState.hpp new file mode 100644 index 000000000..5788d1e23 --- /dev/null +++ b/Runtime/Character/CAdditiveBodyState.hpp @@ -0,0 +1,38 @@ +#ifndef __URDE_CADDITIVEBODYSTATE_HPP__ +#define __URDE_CADDITIVEBODYSTATE_HPP__ + +#include "RetroTypes.hpp" +#include "CharacterCommon.hpp" +#include "CBodyStateCmdMgr.hpp" + +namespace urde +{ + +class CAdditiveBodyState +{ + +}; + +class CABSAim : public CAdditiveBodyState +{ + +}; + +class CABSFlinch : public CAdditiveBodyState +{ + +}; + +class CABSIdle : public CAdditiveBodyState +{ + +}; + +class CABSReaction : public CAdditiveBodyState +{ + +}; + +} + +#endif // __URDE_CADDITIVEBODYSTATE_HPP__ diff --git a/Runtime/Character/CAnimData.cpp b/Runtime/Character/CAnimData.cpp index aa6c671cf..c2b8e6ca2 100644 --- a/Runtime/Character/CAnimData.cpp +++ b/Runtime/Character/CAnimData.cpp @@ -30,6 +30,7 @@ rstl::reserved_vectorCAnimData::g_BoolPOINodes; rstl::reserved_vector CAnimData::g_Int32POINodes; rstl::reserved_vector CAnimData::g_ParticlePOINodes; rstl::reserved_vector CAnimData::g_SoundPOINodes; +rstl::reserved_vector CAnimData::g_TransientInt32POINodes; void CAnimData::FreeCache() { @@ -73,6 +74,7 @@ CAnimData::CAnimData(ResId id, g_Int32POINodes.resize(16); g_ParticlePOINodes.resize(20); g_SoundPOINodes.resize(20); + g_TransientInt32POINodes.resize(16); x108_aabb = xd8_modelData->GetModel()->GetAABB(); x120_particleDB.CacheParticleDesc(xc_charInfo.GetParticleResData()); @@ -127,7 +129,7 @@ SAdvancementDeltas CAnimData::AdvanceAdditiveAnims(float dt) std::shared_ptr& anim = additive.second.GetAnim(); if (additive.second.IsActive()) { - while (time.GreaterThanZero() && std::fabs(time) >= 0.00001f) + while (time.GreaterThanZero() && std::fabs(time.GetSeconds()) >= 0.00001f) { x210_passedIntCount += anim->GetInt32POIList(time, g_Int32POINodes.data(), 16, x210_passedIntCount, 0); x20c_passedBoolCount += anim->GetBoolPOIList(time, g_BoolPOINodes.data(), 8, x20c_passedBoolCount, 0); @@ -143,7 +145,7 @@ SAdvancementDeltas CAnimData::AdvanceAdditiveAnims(float dt) else { CCharAnimTime remTime = anim->VGetTimeRemaining(); - while (remTime.GreaterThanZero() && std::fabs(remTime) >= 0.00001f) + while (remTime.GreaterThanZero() && std::fabs(remTime.GetSeconds()) >= 0.00001f) { x210_passedIntCount += anim->GetInt32POIList(time, g_Int32POINodes.data(), 16, x210_passedIntCount, 0); x20c_passedBoolCount += anim->GetBoolPOIList(time, g_BoolPOINodes.data(), 8, x20c_passedBoolCount, 0); @@ -276,9 +278,25 @@ SAdvancementDeltas CAnimData::GetAdvancementDeltas(const CCharAnimTime& a, return x1f8_animRoot->VGetAdvancementResults(a, b).x8_deltas; } -CCharAnimTime CAnimData::GetTimeOfUserEvent(EUserEventType, const CCharAnimTime& time) const +CCharAnimTime CAnimData::GetTimeOfUserEvent(EUserEventType type, const CCharAnimTime& time) const { - return {}; + u32 count = x1f8_animRoot->GetInt32POIList(time, g_TransientInt32POINodes.data(), 16, 0, 64); + for (int i=0 ; i& node) { + zeus::CQuaternion orient; + x1e8_alignRot = zeus::CQuaternion::skNoRotation; + + x220_27_ = false; + if (parms.GetDeltaOrient() && parms.GetObjectXform()) + { + ResetPOILists(); + x210_passedIntCount += node->GetInt32POIList(CCharAnimTime::Infinity(), g_Int32POINodes.data(), + 16, x210_passedIntCount, 64); + for (int i=0 ; iVGetAdvancementResults(poi.GetTime(), 0.f); + orient = zeus::CQuaternion::slerp(zeus::CQuaternion::skNoRotation, + *parms.GetDeltaOrient() * + zeus::CQuaternion(parms.GetObjectXform()->buildMatrix3f().inverted()) * + res.x8_deltas.xc_rotDelta.inverse(), + 1.f / (60.f * poi.GetTime().GetSeconds())); + x1e8_alignRot = orient; + x220_27_ = true; + } + } + } + + if (!x220_27_) + { + bool didAlign = false; + bool didStart = false; + zeus::CVector3f posStart, posAlign; + CCharAnimTime timeStart, timeAlign; + if (parms.GetTargetPos() && parms.GetObjectXform()) + { + ResetPOILists(); + x210_passedIntCount += node->GetInt32POIList(CCharAnimTime::Infinity(), g_Int32POINodes.data(), + 16, x210_passedIntCount, 64); + for (int i=0 ; iVGetAdvancementResults(poi.GetTime(), 0.f); + posStart = res.x8_deltas.x0_posDelta; + timeStart = poi.GetTime(); + + if (parms.GetIsUseLocator()) + posStart += GetLocatorTransform(poi.GetLocatorName(), &poi.GetTime()).origin; + + if (didAlign) + break; + } + else if (EUserEventType(poi.GetValue()) == EUserEventType::AlignTargetPos) + { + didAlign = true; + SAdvancementResults res = node->VGetAdvancementResults(poi.GetTime(), 0.f); + posAlign = res.x8_deltas.x0_posDelta; + timeAlign = poi.GetTime(); + + if (parms.GetIsUseLocator()) + posAlign += GetLocatorTransform(poi.GetLocatorName(), &poi.GetTime()).origin; + + if (didStart) + break; + } + } + } + + if (didAlign && didStart) + { + zeus::CVector3f scaleStart = *parms.GetObjectScale() * posStart; + zeus::CVector3f scaleAlign = *parms.GetObjectScale() * posAlign; + x1dc_alignPos = (parms.GetObjectXform()->inverse() * *parms.GetTargetPos() - scaleStart - + (scaleAlign - scaleStart)) / *parms.GetObjectScale() * + (1.f / (timeAlign.GetSeconds() - timeStart.GetSeconds())); + x220_28_ = true; + x220_26_ = false; + } + else + { + x1dc_alignPos = zeus::CVector3f::skZero; + x220_28_ = false; + x220_26_ = false; + } + } + } + else + { + bool didStart = false; + bool didAlign = false; + CCharAnimTime timeStart, timeAlign; + zeus::CVector3f startPos; + if (parms.GetTargetPos() && parms.GetObjectXform()) + { + ResetPOILists(); + x210_passedIntCount += node->GetInt32POIList(CCharAnimTime::Infinity(), g_Int32POINodes.data(), + 16, x210_passedIntCount, 64); + for (int i=0 ; iVGetAdvancementResults(frameInterval, time); + pos += quat.toTransform() * res.x8_deltas.x0_posDelta; + quat *= (res.x8_deltas.xc_rotDelta * orient); + if (!foundStartPos && time >= timeStart) + { + startPos = pos; + foundStartPos = true; + } + time += frameInterval; + } + zeus::CVector3f scaleStart = startPos * *parms.GetObjectScale(); + zeus::CVector3f scaleAlign = pos * *parms.GetObjectScale(); + x1dc_alignPos = (parms.GetObjectXform()->inverse() * *parms.GetTargetPos() - scaleStart - + (scaleAlign - scaleStart)) / *parms.GetObjectScale() * + (1.f / (timeAlign.GetSeconds() - timeStart.GetSeconds())); + x220_28_ = true; + x220_26_ = false; + } + else + { + x1dc_alignPos = zeus::CVector3f::skZero; + x220_28_ = false; + x220_26_ = false; + } + } + else + { + x1dc_alignPos = zeus::CVector3f::skZero; + x220_28_ = false; + x220_26_ = false; + } + } } zeus::CTransform CAnimData::GetLocatorTransform(CSegId id, const CCharAnimTime* time) const @@ -330,12 +528,12 @@ bool CAnimData::IsAnimTimeRemaining(float rem, const std::string& name) const { if (!x1f8_animRoot) return false; - return float(x1f8_animRoot->VGetTimeRemaining()) <= rem; + return x1f8_animRoot->VGetTimeRemaining().GetSeconds() <= rem; } float CAnimData::GetAnimTimeRemaining(const std::string& name) const { - float rem = x1f8_animRoot->VGetTimeRemaining(); + float rem = x1f8_animRoot->VGetTimeRemaining().GetSeconds(); if (x200_speedScale) return rem / x200_speedScale; return rem; @@ -373,7 +571,7 @@ float CAnimData::GetAnimationDuration(int animIn) const } } - durAccum += dur; + durAccum += dur.GetSeconds(); } if (anim->GetType() == EMetaAnimType::Random) @@ -491,52 +689,52 @@ void CAnimData::PrimitiveSetToTokenVector(const std::set& primSet, void CAnimData::GetAnimationPrimitives(const CAnimPlaybackParms& parms, std::set& primsOut) const { std::shared_ptr animA = - x100_animMgr->GetMetaAnimation(xc_charInfo.GetAnimationIndex(parms.x0_animA)); + x100_animMgr->GetMetaAnimation(xc_charInfo.GetAnimationIndex(parms.GetAnimationId())); animA->GetUniquePrimitives(primsOut); - if (parms.x4_animB != -1) + if (parms.GetSecondAnimationId() != -1) { std::shared_ptr animB = - x100_animMgr->GetMetaAnimation(xc_charInfo.GetAnimationIndex(parms.x4_animB)); + x100_animMgr->GetMetaAnimation(xc_charInfo.GetAnimationIndex(parms.GetSecondAnimationId())); animB->GetUniquePrimitives(primsOut); } } void CAnimData::SetAnimation(const CAnimPlaybackParms& parms, bool noTrans) { - if (parms.x0_animA == x40c_playbackParms.x0_animA || - (parms.x4_animB == x40c_playbackParms.x4_animB && - parms.x8_blendWeight == x40c_playbackParms.x8_blendWeight && - parms.x8_blendWeight != 1.f) || - parms.x4_animB == -1) + if (parms.GetAnimationId() == x40c_playbackParms.GetAnimationId() || + (parms.GetSecondAnimationId() == x40c_playbackParms.GetSecondAnimationId() && + parms.GetBlendFactor() == x40c_playbackParms.GetBlendFactor() && + parms.GetBlendFactor() != 1.f) || + parms.GetSecondAnimationId() == -1) { if (x220_29_animationJustStarted) return; } - x40c_playbackParms.x0_animA = parms.x0_animA; - x40c_playbackParms.x4_animB = parms.x4_animB; - x40c_playbackParms.x8_blendWeight = parms.x8_blendWeight; + x40c_playbackParms.SetAnimationId(parms.GetAnimationId()); + x40c_playbackParms.SetSecondAnimationId(parms.GetSecondAnimationId()); + x40c_playbackParms.SetBlendFactor(parms.GetBlendFactor()); x200_speedScale = 1.f; - x208_defaultAnim = parms.x0_animA; + x208_defaultAnim = parms.GetAnimationId(); - u32 animIdxA = xc_charInfo.GetAnimationIndex(parms.x0_animA); + u32 animIdxA = xc_charInfo.GetAnimationIndex(parms.GetAnimationId()); ResetPOILists(); std::shared_ptr blendNode; - if (parms.x4_animB != -1) + if (parms.GetSecondAnimationId() != -1) { - u32 animIdxB = xc_charInfo.GetAnimationIndex(parms.x4_animB); + u32 animIdxB = xc_charInfo.GetAnimationIndex(parms.GetSecondAnimationId()); std::shared_ptr treeA = x100_animMgr->GetAnimationTree(animIdxA, CMetaAnimTreeBuildOrders::NoSpecialOrders()); std::shared_ptr treeB = x100_animMgr->GetAnimationTree(animIdxB, CMetaAnimTreeBuildOrders::NoSpecialOrders()); - blendNode = std::make_shared(false, treeA, treeB, parms.x8_blendWeight, + blendNode = std::make_shared(false, treeA, treeB, parms.GetBlendFactor(), CAnimTreeBlend::CreatePrimitiveName(treeA, treeB, - parms.x8_blendWeight)); + parms.GetBlendFactor())); } else { @@ -548,7 +746,7 @@ void CAnimData::SetAnimation(const CAnimPlaybackParms& parms, bool noTrans) else x1f8_animRoot = blendNode; - x220_24_animating = parms.xc_animating; + x220_24_animating = parms.GetIsPlayAnimation(); CalcPlaybackAlignmentParms(parms, blendNode); ResetPOILists(); x220_29_animationJustStarted = true; @@ -608,7 +806,7 @@ SAdvancementDeltas CAnimData::DoAdvance(float dt, bool& suspendParticles, CRando x218_passedSoundCount += x1f8_animRoot->GetSoundPOIList(time, g_SoundPOINodes.data(), 16, x218_passedSoundCount, 0); AdvanceAnim(time, offsetPost, quatPost); remTime = x1f8_animRoot->VGetTimeRemaining(); - time = std::max(0.f, std::min(float(remTime), float(time))); + time = std::max(0.f, std::min(remTime.GetSeconds(), time.GetSeconds())); if (remTime.EpsilonZero()) { x220_24_animating = false; @@ -703,7 +901,7 @@ void CAnimData::AdvanceAnim(CCharAnimTime& time, zeus::CVector3f& offset, zeus:: offset += results.x8_deltas.x0_posDelta; if (x220_26_) - offset += x1dc_alignPos * time; + offset += x1dc_alignPos * time.GetSeconds(); zeus::CQuaternion rot = results.x8_deltas.xc_rotDelta * x1e8_alignRot; quat = quat * rot; @@ -771,8 +969,8 @@ float CAnimData::GetAverageVelocity(int animIn) const } } - velAccum += dur * avgVel; - durAccum += dur; + velAccum += dur.GetSeconds() * avgVel; + durAccum += dur.GetSeconds(); } if (durAccum > 0.f) diff --git a/Runtime/Character/CAnimData.hpp b/Runtime/Character/CAnimData.hpp index 112be40d7..0241d875d 100644 --- a/Runtime/Character/CAnimData.hpp +++ b/Runtime/Character/CAnimData.hpp @@ -139,6 +139,7 @@ class CAnimData static rstl::reserved_vector g_Int32POINodes; static rstl::reserved_vector g_ParticlePOINodes; static rstl::reserved_vector g_SoundPOINodes; + static rstl::reserved_vector g_TransientInt32POINodes; int m_drawInstCount; @@ -184,6 +185,7 @@ public: float GetAnimationDuration(int) const; bool GetIsLoop() const {return x220_25_loop;} void EnableLooping(bool val) {x220_25_loop = val; x220_24_animating = true;} + void EnableAnimation(bool val) {x220_24_animating = val;} bool IsAnimating() const {return x220_24_animating;} std::shared_ptr GetAnimSysContext() const; std::shared_ptr GetAnimationManager() const; diff --git a/Runtime/Character/CAnimPlaybackParms.hpp b/Runtime/Character/CAnimPlaybackParms.hpp index c06a1e6a3..fb8ff6c8b 100644 --- a/Runtime/Character/CAnimPlaybackParms.hpp +++ b/Runtime/Character/CAnimPlaybackParms.hpp @@ -2,27 +2,42 @@ #define __CANIMPLAYBACKPARMS_HPP__ #include "RetroTypes.hpp" +#include "zeus/CQuaternion.hpp" namespace urde { class CAnimPlaybackParms { - friend class CAnimData; s32 x0_animA = -1; s32 x4_animB = -1; float x8_blendWeight = 1.f; bool xc_animating = true; - s32 x10_ = 0; - s32 x14_ = 0; - bool x18_ = false; - s32 x1c_ = 0; - s32 x20_ = 0; - s32 x24_ = 0; + s32 x10_ = 0; + const zeus::CVector3f* x14_targetPos = nullptr; + bool x18_useLocator = false; + const zeus::CQuaternion* x1c_deltaOrient = nullptr; + const zeus::CTransform* x20_objectXf = nullptr; + const zeus::CVector3f* x24_objectScale = nullptr; public: CAnimPlaybackParms() = default; CAnimPlaybackParms(s32 animA, s32 animB, float blendWeight, bool animating) - : x0_animA(animA), x4_animB(animB), x8_blendWeight(blendWeight), xc_animating(animating) - {} + : x0_animA(animA), x4_animB(animB), x8_blendWeight(blendWeight), xc_animating(animating) {} + CAnimPlaybackParms(s32 anim, const zeus::CQuaternion* deltaOrient, const zeus::CVector3f* targetPos, + const zeus::CTransform* xf, const zeus::CVector3f* scale, bool useLocator) + : x0_animA(anim), x14_targetPos(targetPos), x18_useLocator(useLocator), x1c_deltaOrient(deltaOrient), + x20_objectXf(xf), x24_objectScale(scale) {} + const zeus::CTransform* GetObjectXform() const { return x20_objectXf; } + const zeus::CQuaternion* GetDeltaOrient() const { return x1c_deltaOrient; } + const zeus::CVector3f* GetTargetPos() const { return x14_targetPos; } + bool GetIsUseLocator() const { return x18_useLocator; } + const zeus::CVector3f* GetObjectScale() const { return x24_objectScale; } + s32 GetAnimationId() const { return x0_animA; } + s32 GetSecondAnimationId() const { return x4_animB; } + float GetBlendFactor() const { return x8_blendWeight; } + void SetAnimationId(s32 id) { x0_animA = id; } + void SetSecondAnimationId(s32 id) { x4_animB = id; } + void SetBlendFactor(float f) { x8_blendWeight = f; } + bool GetIsPlayAnimation() const { return xc_animating; } }; } diff --git a/Runtime/Character/CAnimSource.cpp b/Runtime/Character/CAnimSource.cpp index 99cd8d32c..26d6a8017 100644 --- a/Runtime/Character/CAnimSource.cpp +++ b/Runtime/Character/CAnimSource.cpp @@ -120,7 +120,7 @@ void CAnimSource::CalcAverageVelocity() if (frameVel > 0.00001f) accum += frameVel; } - x60_averageVelocity = accum / x0_duration; + x60_averageVelocity = accum / x0_duration.GetSeconds(); } CAnimSource::CAnimSource(CInputStream& in, IObjectStore& store) @@ -146,10 +146,10 @@ void CAnimSource::GetSegStatementSet(const CSegIdList& list, const CCharAnimTime& time) const { u32 frameIdx = unsigned(time / x8_interval); - float remTime = time - frameIdx * x8_interval; + float remTime = time.GetSeconds() - frameIdx * x8_interval.GetSeconds(); if (std::fabs(remTime) < 0.00001f) remTime = 0.f; - float t = ClampZeroToOne(remTime / x8_interval); + float t = ClampZeroToOne(remTime / x8_interval.GetSeconds()); const u32 floatsPerFrame = x40_data.x10_transPerFrame * 3 + x40_data.xc_rotPerFrame * 4; const u32 rotFloatsPerFrame = x40_data.xc_rotPerFrame * 4; @@ -209,10 +209,10 @@ zeus::CQuaternion CAnimSource::GetRotation(const CSegId& seg, const CCharAnimTim if (rotIdx != 0xff) { u32 frameIdx = unsigned(time / x8_interval); - float remTime = time - frameIdx * x8_interval; + float remTime = time.GetSeconds() - frameIdx * x8_interval.GetSeconds(); if (std::fabs(remTime) < 0.00001f) remTime = 0.f; - float t = ClampZeroToOne(remTime / x8_interval); + float t = ClampZeroToOne(remTime / x8_interval.GetSeconds()); const u32 floatsPerFrame = x40_data.x10_transPerFrame * 3 + x40_data.xc_rotPerFrame * 4; const float* frameDataA = @@ -240,10 +240,10 @@ zeus::CVector3f CAnimSource::GetOffset(const CSegId& seg, const CCharAnimTime& t return {}; u32 frameIdx = unsigned(time / x8_interval); - float remTime = time - frameIdx * x8_interval; + float remTime = time.GetSeconds() - frameIdx * x8_interval.GetSeconds(); if (std::fabs(remTime) < 0.00001f) remTime = 0.f; - float t = ClampZeroToOne(remTime / x8_interval); + float t = ClampZeroToOne(remTime / x8_interval.GetSeconds()); const u32 floatsPerFrame = x40_data.x10_transPerFrame * 3 + x40_data.xc_rotPerFrame * 4; const u32 rotFloatsPerFrame = x40_data.xc_rotPerFrame * 4; diff --git a/Runtime/Character/CAnimSourceReader.cpp b/Runtime/Character/CAnimSourceReader.cpp index fe131e33a..848419660 100644 --- a/Runtime/Character/CAnimSourceReader.cpp +++ b/Runtime/Character/CAnimSourceReader.cpp @@ -298,7 +298,7 @@ SAdvancementResults CAnimSourceReader::VGetAdvancementResults(const CCharAnimTim void CAnimSourceReader::VSetPhase(float phase) { - xc_curTime = phase * x54_source->GetDuration(); + xc_curTime = phase * x54_source->GetDuration().GetSeconds(); if (x54_source->GetPOIData()) { UpdatePOIStates(); diff --git a/Runtime/Character/CAnimTreeSequence.cpp b/Runtime/Character/CAnimTreeSequence.cpp index 746f4cae1..1e46bca5c 100644 --- a/Runtime/Character/CAnimTreeSequence.cpp +++ b/Runtime/Character/CAnimTreeSequence.cpp @@ -53,7 +53,7 @@ CCharAnimTime CAnimTreeSequence::VGetTimeRemaining() const { if (x38_curIdx == x28_.size() - 1) return x14_child->VGetTimeRemaining(); - return x3c_fundamentals.GetSteadyStateAnimInfo().GetDuration() - x94_curTime; + return x3c_fundamentals.GetSteadyStateAnimInfo().GetDuration() - x94_curTime.GetSeconds(); } CSteadyStateAnimInfo CAnimTreeSequence::VGetSteadyStateAnimInfo() const diff --git a/Runtime/Character/CAnimTreeTimeScale.cpp b/Runtime/Character/CAnimTreeTimeScale.cpp index a54c27a05..f61884af1 100644 --- a/Runtime/Character/CAnimTreeTimeScale.cpp +++ b/Runtime/Character/CAnimTreeTimeScale.cpp @@ -3,13 +3,15 @@ namespace urde { -CAnimTreeTimeScale::CAnimTreeTimeScale(const std::weak_ptr& node, float scale, const std::string& name) +CAnimTreeTimeScale::CAnimTreeTimeScale(const std::weak_ptr& node, float scale, + const std::string& name) : CAnimTreeSingleChild(node, name) , x18_timeScale(new CConstantAnimationTimeScale(scale)) { } -std::string CAnimTreeTimeScale::CreatePrimitiveName(const std::weak_ptr&, float, const CCharAnimTime&, float) +std::string CAnimTreeTimeScale::CreatePrimitiveName(const std::weak_ptr&, float, + const CCharAnimTime&, float) { return {}; } @@ -22,13 +24,16 @@ CCharAnimTime CAnimTreeTimeScale::GetRealLifeTime(const CCharAnimTime& time) con if (x28_ > CCharAnimTime()) { if (tmp < CCharAnimTime(x28_ * x20_)) - return x18_timeScale->VTimeScaleIntegral(x20_, x20_ + tmp); + return x18_timeScale->VTimeScaleIntegral(x20_.GetSeconds(), + (x20_ + tmp).GetSeconds()); else { - CCharAnimTime integral = x18_timeScale->VTimeScaleIntegral(x20_, x28_); + CCharAnimTime integral = + x18_timeScale->VTimeScaleIntegral(x20_.GetSeconds(), x28_.GetSeconds()); if (integral > tmp) - return x18_timeScale->VFindUpperLimit(x20_, tmp) * x20_; + return x18_timeScale->VFindUpperLimit(x20_.GetSeconds(), tmp.GetSeconds()) * + x20_.GetSeconds(); else return integral + (integral * tmp); } diff --git a/Runtime/Character/CAnimTreeTransition.cpp b/Runtime/Character/CAnimTreeTransition.cpp index aba6a6549..3a03436e4 100644 --- a/Runtime/Character/CAnimTreeTransition.cpp +++ b/Runtime/Character/CAnimTreeTransition.cpp @@ -71,7 +71,7 @@ void CAnimTreeTransition::SetBlendingWeight(float w) float CAnimTreeTransition::VGetBlendingWeight() const { if (x24_.GreaterThanZero()) - return (1.f / x24_) * x2c_; + return (1.f / x24_.GetSeconds()) * x2c_.GetSeconds(); return 0.f; } } diff --git a/Runtime/Character/CBodyController.cpp b/Runtime/Character/CBodyController.cpp index e69de29bb..b88167862 100644 --- a/Runtime/Character/CBodyController.cpp +++ b/Runtime/Character/CBodyController.cpp @@ -0,0 +1,81 @@ +#include "CBodyController.hpp" +#include "World/CActor.hpp" +#include "TCastTo.hpp" +#include "World/CPhysicsActor.hpp" + +namespace urde +{ + +CBodyController::CBodyController(CActor& actor, float f1, EBodyType bodyType) +: x0_actor(actor), x2a4_bodyStateInfo(actor, bodyType), + x2f4_bodyType(bodyType), x2fc_rotRate(f1) +{ + x300_28_playDeathAnims = true; + x2a4_bodyStateInfo.x18_bodyController = this; +} + +void CBodyController::EnableAnimation(bool e) +{ + x0_actor.ModelData()->AnimationData()->EnableAnimation(e); +} + +float CBodyController::GetAnimTimeRemaining() const +{ + return x0_actor.GetModelData()->GetAnimationData()->GetAnimTimeRemaining("Whole Body"); +} + +const CPASDatabase& CBodyController::GetPASDatabase() const +{ + return x0_actor.GetModelData()->GetAnimationData()->GetCharacterInfo().GetPASDatabase(); +} + +void CBodyController::FaceDirection(const zeus::CVector3f& v0, float dt) +{ + if (x300_26_) + 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_rotRate)); + 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_) + 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 (std::fabs(dot - 1.f) > 0.00001f) + { + if (dot < -0.9999f) + { + zeus::CQuaternion rot = + zeus::CQuaternion::fromAxisAngle(act->GetTransform().basis[2], + zeus::degToRad(dt * x2fc_rotRate)); + rot.setImaginary(act->GetTransform().transposeRotate(rot.getImaginary())); + act->RotateInOneFrameOR(rot, dt); + } + else + { + /* TODO: Finish */ + } + } + } + } +} + +} diff --git a/Runtime/Character/CBodyController.hpp b/Runtime/Character/CBodyController.hpp index d3b37b334..b3fa9a5d8 100644 --- a/Runtime/Character/CBodyController.hpp +++ b/Runtime/Character/CBodyController.hpp @@ -4,6 +4,8 @@ #include "RetroTypes.hpp" #include "zeus/CQuaternion.hpp" #include "CharacterCommon.hpp" +#include "CBodyStateCmdMgr.hpp" +#include "CBodyStateInfo.hpp" namespace urde { @@ -14,26 +16,60 @@ class CFinalInput; class CPASAnimParmData; class CRandom16; class CStateManager; +class CPASDatabase; + class CBodyController { + CActor& x0_actor; + CBodyStateCmdMgr x4_cmdMgr; + CBodyStateInfo x2a4_bodyStateInfo; + zeus::CQuaternion x2dc_rot; + pas::ELocomotionType x2ec_locomotionType = pas::ELocomotionType::Relaxed; + pas::EFallState x2f0_fallState = pas::EFallState::Zero; + EBodyType x2f4_bodyType; + s32 x2f8_curAnim = -1; + float x2fc_rotRate; + union + { + struct + { + bool x300_24_animationOver : 1; + bool x300_25_ : 1; + bool x300_26_ : 1; + bool x300_27_ : 1; + bool x300_28_playDeathAnims : 1; + }; + u32 _dummy = 0; + }; + float x304_ = 0.f; + float x308_ = 0.f; + float x30c_ = 0.f; + float x310_ = 0.f; + zeus::CVector3f x314_; + float x320_ = 0.f; + float x324_ = 0.f; + float x328_ = 0.f; + float x32c_ = 0.f; + float x330_restrictedFlyerMoveSpeed = 0.f; public: - CBodyController(CActor&, float, EBodyType); + CBodyController(CActor& owner, float f1, EBodyType bodyType); void GetCurrentStateId() const; - void GetComandMgr(); - void SetDoDeathAnims(bool); + CBodyStateCmdMgr& GetCommandMgr() { return x4_cmdMgr; } + const CBodyStateCmdMgr& GetCommandMgr() const { return x4_cmdMgr; } + void SetDoDeathAnims(bool d) { x300_28_playDeathAnims = d; } bool IsElectrocuting() const; bool IsOnFire() const; bool IsFrozen() const; - void GetBodyStateInfo() const; + const CBodyStateInfo& GetBodyStateInfo() const { return x2a4_bodyStateInfo; } bool GetIsActive() const; void BodyStateInfo(); float GetTurnSpeed() const; - pas::ELocomotionType GetLocomotionType() const; - void GetOwner(); - bool IsAnimationOver() const; - void EnableAnimation(bool); - bool ShouldPlayDeathAnims() const; - void GetCurrentAnimId() const; + pas::ELocomotionType GetLocomotionType() const { return x2ec_locomotionType; } + CActor& GetOwner() const { return x0_actor; } + bool IsAnimationOver() const { return x300_24_animationOver; } + void EnableAnimation(bool e); + bool ShouldPlayDeathAnims() const { return x300_28_playDeathAnims; } + s32 GetCurrentAnimId() const { return x2f8_curAnim; } void Activate(CStateManager&); void GetCurrentAdditiveState(); void SetState(pas::EAnimationState); @@ -47,15 +83,15 @@ public: void SetAdditiveState(pas::EAnimationState); void SetTurnSpeed(float); void SetCurrentAnimation(const CAnimPlaybackParms&, bool, bool); - void GetAnimTimeRemaining() const; + float GetAnimTimeRemaining() const; void SetPlaybackRate(float); void MultiplyPlaybackRate(float); - void SetDeltaRotation(const zeus::CQuaternion&); + void SetDeltaRotation(const zeus::CQuaternion& q) { x2dc_rot *= q; } void FaceDirection(const zeus::CVector3f&, float); - void FaceDirection3D(const zeus::CVector3f&, float); + void FaceDirection3D(const zeus::CVector3f&, const zeus::CVector3f&, float); void HasBodyInfo(CActor&); void ProcessInput(const CFinalInput&); - void GetPASDatabase() const; + const CPASDatabase& GetPASDatabase() const; void PlayBestAnimation(const CPASAnimParmData&, CRandom16&); void LoopBestAnimation(const CPASAnimParmData&, CRandom16&); void Freeze(float, float, float); @@ -67,9 +103,9 @@ public: void DouseElectrocuting(); void UpdateFrozenInfo(float, CStateManager&); void GetCurrentAdditiveStateId() const; - EBodyType GetBodyType() const; + EBodyType GetBodyType() const { return x2f4_bodyType; } bool HasBeenFrozen() const; - void GetOwner() const; + float GetRestrictedFlyerMoveSpeed() const { return x330_restrictedFlyerMoveSpeed; } }; } diff --git a/Runtime/Character/CBodyState.cpp b/Runtime/Character/CBodyState.cpp index e69de29bb..7f122dc31 100644 --- a/Runtime/Character/CBodyState.cpp +++ b/Runtime/Character/CBodyState.cpp @@ -0,0 +1,2419 @@ +#include "CBodyState.hpp" +#include "CBodyController.hpp" +#include "World/CActor.hpp" +#include "CStateManager.hpp" +#include "TCastTo.hpp" +#include "World/CPhysicsActor.hpp" +#include "CPASAnimParmData.hpp" +#include "World/CPatterned.hpp" + +namespace urde +{ + +void CBSAttack::Start(CBodyController& bc, CStateManager& mgr) +{ + const CBCMeleeAttackCmd* cmd = + static_cast(bc.GetCommandMgr().GetCmd(EBodyStateCmd::MeleeAttack)); + const CPASDatabase& pasDatabase = bc.GetPASDatabase(); + CPASAnimParmData parms(7, CPASAnimParm::FromEnum(s32(cmd->GetAttackSeverity())), + CPASAnimParm::FromEnum(s32(bc.GetLocomotionType()))); + std::pair best = pasDatabase.FindBestAnimation(parms, *mgr.GetActiveRandom(), -1); + CAnimPlaybackParms playParms(best.second, -1, 1.f, true); + bc.SetCurrentAnimation(playParms, false, false); + if (cmd->HasAttackTargetPos()) + { + x20_targetPos = cmd->GetAttackTargetPos(); + + CCharAnimTime evTime = bc.GetOwner().GetModelData()->GetAnimationData()->GetTimeOfUserEvent( + EUserEventType::AlignTargetPosStart, CCharAnimTime::Infinity()); + x2c_alignTargetPosStartTime = (evTime != CCharAnimTime::Infinity()) ? evTime.GetSeconds() : 0.f; + + evTime = bc.GetOwner().GetModelData()->GetAnimationData()->GetTimeOfUserEvent( + EUserEventType::AlignTargetPos, CCharAnimTime::Infinity()); + x30_alignTargetPosTime = (evTime != CCharAnimTime::Infinity()) ? + evTime.GetSeconds() : bc.GetAnimTimeRemaining(); + } + else + { + x20_targetPos = zeus::CVector3f::skZero; + x2c_alignTargetPosStartTime = -1.f; + x30_alignTargetPosTime = -1.f; + } + + x4_nextState = pas::EAnimationState::Locomotion; + x34_curTime = 0.f; +} + +pas::EAnimationState CBSAttack::GetBodyStateTransition(float dt, CBodyController& bc) +{ + if (bc.GetCommandMgr().GetCmd(EBodyStateCmd::Hurled)) + return pas::EAnimationState::Hurled; + if (bc.GetCommandMgr().GetCmd(EBodyStateCmd::KnockDown)) + return pas::EAnimationState::Fall; + if (bc.GetCommandMgr().GetCmd(EBodyStateCmd::LoopHitReaction)) + return pas::EAnimationState::LoopReaction; + if (bc.GetCommandMgr().GetCmd(EBodyStateCmd::KnockBack)) + return pas::EAnimationState::KnockBack; + if (bc.GetCommandMgr().GetCmd(EBodyStateCmd::Locomotion)) + return pas::EAnimationState::Locomotion; + if (const CBCSlideCmd* cmd = static_cast( + bc.GetCommandMgr().GetCmd(EBodyStateCmd::Slide))) + { + x8_slide = *cmd; + x4_nextState = pas::EAnimationState::Slide; + } + if (bc.GetCommandMgr().GetCmd(EBodyStateCmd::Generate)) + return pas::EAnimationState::Generate; + if (bc.IsAnimationOver()) + { + if (bc.GetCommandMgr().GetCmd(EBodyStateCmd::MeleeAttack)) + return pas::EAnimationState::MeleeAttack; + if (bc.GetCommandMgr().GetCmd(EBodyStateCmd::ProjectileAttack)) + return pas::EAnimationState::ProjectileAttack; + if (bc.GetCommandMgr().GetCmd(EBodyStateCmd::LoopAttack)) + return pas::EAnimationState::LoopAttack; + return x4_nextState; + } + if (bc.GetCommandMgr().GetCmd(EBodyStateCmd::NextState)) + return x4_nextState; + return pas::EAnimationState::Invalid; +} + +void CBSAttack::UpdatePhysicsActor(CBodyController& bc, float dt) +{ + if (x20_targetPos.isZero()) + return; + if (x34_curTime >= x2c_alignTargetPosStartTime && x34_curTime <= x30_alignTargetPosTime) + { + if (TCastToPtr act = bc.GetOwner()) + { + zeus::CVector3f delta = x20_targetPos - act->GetTranslation(); + float td = x30_alignTargetPosTime - x2c_alignTargetPosStartTime; + if (dt > 0.f) + delta *= dt / td; + zeus::CVector3f localDelta = act->GetTransform().transposeRotate(delta); + act->ApplyImpulseWR(act->GetMoveToORImpulseWR(localDelta, dt), zeus::CAxisAngle::sIdentity); + } + } +} + +pas::EAnimationState CBSAttack::UpdateBody(float dt, CBodyController& bc, CStateManager& mgr) +{ + x34_curTime += dt; + pas::EAnimationState st = GetBodyStateTransition(dt, bc); + if (st == pas::EAnimationState::Invalid) + { + if (!bc.GetCommandMgr().GetTargetVector().isZero()) + bc.FaceDirection(bc.GetCommandMgr().GetTargetVector(), dt); + UpdatePhysicsActor(bc, dt); + } + else if (st == pas::EAnimationState::Slide) + { + bc.GetCommandMgr().DeliverCmd(x8_slide); + } + return st; +} + +void CBSProjectileAttack::Start(CBodyController& bc, CStateManager& mgr) +{ + const CBCProjectileAttackCmd* cmd = + static_cast(bc.GetCommandMgr().GetCmd(EBodyStateCmd::ProjectileAttack)); + zeus::CVector3f localDelta = + bc.GetOwner().GetTransform().transposeRotate(cmd->GetTargetPosition() - + bc.GetOwner().GetTranslation()); + zeus::CRelAngle angle = std::atan2(localDelta.y, localDelta.x); + float attackAngle = angle.asDegrees(); + CPASAnimParmData parms(18, CPASAnimParm::FromEnum(s32(cmd->GetAttackSeverity())), + CPASAnimParm::FromReal32(angle.asDegrees()), + CPASAnimParm::FromEnum(s32(bc.GetLocomotionType()))); + std::pair best1 = bc.GetPASDatabase().FindBestAnimation(parms, *mgr.GetActiveRandom(), -1); + if (cmd->BlendTwoClosest()) + { + std::pair best2 = bc.GetPASDatabase().FindBestAnimation(parms, *mgr.GetActiveRandom(), + best1.second); + const CPASAnimState* projAttackState = bc.GetPASDatabase().GetAnimState(18); + float angle1 = projAttackState->GetAnimParmData(best1.second, 1).GetReal32Value(); + float angle2 = projAttackState->GetAnimParmData(best2.second, 1).GetReal32Value(); + if (angle1 - angle2 > 180.f) + angle2 += 360.f; + else if (angle2 - angle1 > 180.f) + angle1 += 360.f; + CAnimPlaybackParms playParms(best1.second, best2.second, + (angle1 - attackAngle) / (angle1 - angle2), true); + bc.SetCurrentAnimation(playParms, false, false); + } + else + { + CAnimPlaybackParms playParms(best1.second, -1, 1.f, true); + bc.SetCurrentAnimation(playParms, false, false); + } +} + +pas::EAnimationState CBSProjectileAttack::GetBodyStateTransition(float dt, CBodyController& bc) +{ + if (bc.GetCommandMgr().GetCmd(EBodyStateCmd::Hurled)) + return pas::EAnimationState::Hurled; + if (bc.GetCommandMgr().GetCmd(EBodyStateCmd::KnockDown)) + return pas::EAnimationState::Fall; + if (bc.GetCommandMgr().GetCmd(EBodyStateCmd::LoopHitReaction)) + return pas::EAnimationState::LoopReaction; + if (bc.GetCommandMgr().GetCmd(EBodyStateCmd::KnockBack)) + return pas::EAnimationState::KnockBack; + if (bc.GetCommandMgr().GetCmd(EBodyStateCmd::Locomotion)) + return pas::EAnimationState::Locomotion; + if (bc.GetCommandMgr().GetCmd(EBodyStateCmd::Generate)) + return pas::EAnimationState::Generate; + if (bc.IsAnimationOver() || bc.GetCommandMgr().GetCmd(EBodyStateCmd::NextState)) + return pas::EAnimationState::Locomotion; + return pas::EAnimationState::Invalid; +} + +pas::EAnimationState CBSProjectileAttack::UpdateBody(float dt, CBodyController& bc, CStateManager& mgr) +{ + pas::EAnimationState st = GetBodyStateTransition(dt, bc); + if (st == pas::EAnimationState::Invalid) + if (!bc.GetCommandMgr().GetTargetVector().isZero()) + bc.FaceDirection(bc.GetCommandMgr().GetTargetVector(), dt); + return st; +} + +void CBSDie::Start(CBodyController& bc, CStateManager& mgr) +{ + bool shouldReset = true; + if (bc.ShouldPlayDeathAnims()) + { + CPASAnimParmData parms(4, CPASAnimParm::FromEnum(s32(bc.GetFallState()))); + std::pair best = bc.GetPASDatabase().FindBestAnimation(parms, *mgr.GetActiveRandom(), -1); + if (best.first > 0.f) + { + CAnimPlaybackParms playParms(best.second, -1, 1.f, true); + bc.SetCurrentAnimation(playParms, false, false); + x4_remTime = bc.GetAnimTimeRemaining(); + shouldReset = false; + } + } + + if (shouldReset) + { + bc.EnableAnimation(false); + x4_remTime = bc.ShouldPlayDeathAnims() ? 3.f : 4.f; + } + + x8_isDead = false; +} + +pas::EAnimationState CBSDie::UpdateBody(float dt, CBodyController& bc, CStateManager& mgr) +{ + x4_remTime -= dt; + if (x4_remTime <= 0.f) + { + bc.EnableAnimation(false); + x8_isDead = true; + } + return pas::EAnimationState::Invalid; +} + +void CBSFall::Start(CBodyController& bc, CStateManager& mgr) +{ + const CBCKnockDownCmd* cmd = + static_cast(bc.GetCommandMgr().GetCmd(EBodyStateCmd::KnockDown)); + zeus::CVector3f localDir = bc.GetOwner().GetTransform().transposeRotate(cmd->GetHitDirection()); + zeus::CRelAngle angle = std::atan2(localDir.y, localDir.z); + CPASAnimParmData parms(0, CPASAnimParm::FromReal32(angle.asDegrees()), + CPASAnimParm::FromEnum(s32(cmd->GetHitSeverity()))); + std::pair best = bc.GetPASDatabase().FindBestAnimation(parms, *mgr.GetActiveRandom(), -1); + CAnimPlaybackParms playParms(best.second, -1, 1.f, true); + bc.SetCurrentAnimation(playParms, false, false); + const CPASAnimState* knockdownState = bc.GetPASDatabase().GetAnimState(0); + if (!knockdownState->GetAnimParmData(best.second, 2).GetBoolValue()) + { + float animAngle = zeus::degToRad(knockdownState->GetAnimParmData(best.second, 0).GetReal32Value()); + zeus::CRelAngle delta1 = angle - animAngle; + zeus::CRelAngle delta2 = animAngle - angle; + float minAngle = std::min(float(delta1), float(delta2)); + x8_remTime = 0.15f * bc.GetAnimTimeRemaining(); + float flippedAngle = (delta1 > M_PIF) ? -minAngle : minAngle; + x4_rotateSpeed = (x8_remTime > FLT_EPSILON) ? flippedAngle / x8_remTime : flippedAngle; + } + else + { + x8_remTime = 0.f; + x4_rotateSpeed = 0.f; + } + xc_fallState = pas::EFallState(knockdownState->GetAnimParmData(best.second, 3).GetEnumValue()); +} + +pas::EAnimationState CBSFall::GetBodyStateTransition(float dt, CBodyController& bc) +{ + if (bc.IsAnimationOver()) + return pas::EAnimationState::LieOnGround; + return pas::EAnimationState::Invalid; +} + +pas::EAnimationState CBSFall::UpdateBody(float dt, CBodyController& bc, CStateManager& mgr) +{ + pas::EAnimationState st = GetBodyStateTransition(dt, bc); + if (st == pas::EAnimationState::Invalid && x8_remTime > 0.f) + { + zeus::CQuaternion quat; + quat.rotateZ(x4_rotateSpeed * dt); + bc.SetDeltaRotation(quat); + x8_remTime -= dt; + } + return st; +} + +void CBSFall::Shutdown(CBodyController& bc) +{ + bc.SetFallState(xc_fallState); +} + +void CBSGetup::Start(CBodyController& bc, CStateManager& mgr) +{ + const CBCGetupCmd* cmd = + static_cast(bc.GetCommandMgr().GetCmd(EBodyStateCmd::Getup)); + CPASAnimParmData parms(1, CPASAnimParm::FromEnum(s32(bc.GetFallState())), + CPASAnimParm::FromEnum(s32(cmd->GetGetupType()))); + std::pair best = bc.GetPASDatabase().FindBestAnimation(parms, *mgr.GetActiveRandom(), -1); + if (best.first > FLT_EPSILON) + { + if (best.second != bc.GetCurrentAnimId()) + { + CAnimPlaybackParms playParms(best.second, -1, 1.f, true); + bc.SetCurrentAnimation(playParms, false, false); + } + x4_fallState = + pas::EFallState(bc.GetPASDatabase().GetAnimState(1)->GetAnimParmData(best.second, 2).GetEnumValue()); + } + else + { + x4_fallState = pas::EFallState::Zero; + } +} + +pas::EAnimationState CBSGetup::GetBodyStateTransition(float dt, CBodyController& bc) +{ + if (bc.GetCommandMgr().GetCmd(EBodyStateCmd::Hurled)) + return pas::EAnimationState::Hurled; + if (bc.GetCommandMgr().GetCmd(EBodyStateCmd::KnockDown)) + return pas::EAnimationState::Fall; + if (bc.IsAnimationOver()) + { + if (x4_fallState == pas::EFallState::Zero) + return pas::EAnimationState::Locomotion; + return pas::EAnimationState::Getup; + } + return pas::EAnimationState::Invalid; +} + +pas::EAnimationState CBSGetup::UpdateBody(float dt, CBodyController& bc, CStateManager& mgr) +{ + return GetBodyStateTransition(dt, bc); +} + +void CBSGetup::Shutdown(CBodyController& bc) +{ + bc.SetFallState(x4_fallState); +} + +void CBSKnockBack::Start(CBodyController& bc, CStateManager& mgr) +{ + const CBCKnockBackCmd* cmd = + static_cast(bc.GetCommandMgr().GetCmd(EBodyStateCmd::KnockBack)); + zeus::CVector3f localDir = bc.GetOwner().GetTransform().transposeRotate(cmd->GetHitDirection()); + zeus::CRelAngle angle = std::atan2(localDir.y, localDir.x); + CPASAnimParmData parms(6, CPASAnimParm::FromReal32(angle.asDegrees()), + CPASAnimParm::FromEnum(s32(cmd->GetHitSeverity()))); + std::pair best = bc.GetPASDatabase().FindBestAnimation(parms, *mgr.GetActiveRandom(), -1); + CAnimPlaybackParms playParms(best.second, -1, 1.f, true); + bc.SetCurrentAnimation(playParms, false, false); + const CPASAnimState* knockbackState = bc.GetPASDatabase().GetAnimState(6); + if (!knockbackState->GetAnimParmData(best.second, 2).GetBoolValue()) + { + float animAngle = zeus::degToRad(knockbackState->GetAnimParmData(best.second, 0).GetReal32Value()); + zeus::CRelAngle delta1 = angle - animAngle; + zeus::CRelAngle delta2 = animAngle - angle; + float minAngle = std::min(float(delta1), float(delta2)); + float flippedAngle = (delta1 > M_PIF) ? -minAngle : minAngle; + xc_remTime = 0.15f * bc.GetAnimTimeRemaining(); + x8_rotateSpeed = (xc_remTime > FLT_EPSILON) ? flippedAngle / xc_remTime : flippedAngle; + } + else + { + xc_remTime = 0.f; + x8_rotateSpeed = 0.f; + } + x4_curTime = 0.f; +} + +pas::EAnimationState CBSKnockBack::GetBodyStateTransition(float dt, CBodyController& bc) +{ + if (bc.GetCommandMgr().GetCmd(EBodyStateCmd::Hurled)) + return pas::EAnimationState::Hurled; + if (bc.GetCommandMgr().GetCmd(EBodyStateCmd::KnockDown)) + return pas::EAnimationState::Fall; + if (bc.GetCommandMgr().GetCmd(EBodyStateCmd::LoopHitReaction)) + return pas::EAnimationState::LoopReaction; + if (bc.GetCommandMgr().GetCmd(EBodyStateCmd::KnockBack) && x4_curTime > 0.2f) + return pas::EAnimationState::KnockBack; + if (bc.IsAnimationOver()) + return pas::EAnimationState::Locomotion; + return pas::EAnimationState::Invalid; +} + +pas::EAnimationState CBSKnockBack::UpdateBody(float dt, CBodyController& bc, CStateManager& mgr) +{ + pas::EAnimationState st = GetBodyStateTransition(dt, bc); + if (st == pas::EAnimationState::Invalid) + { + x4_curTime += dt; + if (xc_remTime > 0.f) + { + zeus::CQuaternion quat; + quat.rotateZ(x8_rotateSpeed * dt); + bc.SetDeltaRotation(quat); + xc_remTime -= dt; + } + } + return st; +} + +CBSLieOnGround::CBSLieOnGround(CActor& actor) +{ + x4_24_hasGroundHit = + actor.GetModelData()->GetAnimationData()->GetCharacterInfo().GetPASDatabase().HasState(11); +} + +void CBSLieOnGround::Start(CBodyController& bc, CStateManager& mgr) +{ + CPASAnimParmData parms(2, CPASAnimParm::FromEnum(s32(bc.GetFallState()))); + std::pair best = bc.GetPASDatabase().FindBestAnimation(parms, *mgr.GetActiveRandom(), -1); + if (best.first > 0.f) + { + CAnimPlaybackParms playParms(best.second, -1, 1.f, true); + bc.SetCurrentAnimation(playParms, true, false); + } + else + { + bc.EnableAnimation(false); + } +} + +pas::EAnimationState CBSLieOnGround::GetBodyStateTransition(float dt, CBodyController& bc) +{ + if (bc.GetCommandMgr().GetCmd(EBodyStateCmd::Die)) + return pas::EAnimationState::Death; + if (x4_24_hasGroundHit && bc.GetCommandMgr().GetCmd(EBodyStateCmd::KnockBack)) + return pas::EAnimationState::GroundHit; + if (!bc.GetCommandMgr().GetCmd(EBodyStateCmd::Locomotion) && + bc.GetCommandMgr().GetCmd(EBodyStateCmd::Getup)) + return pas::EAnimationState::Getup; + return pas::EAnimationState::Invalid; +} + +pas::EAnimationState CBSLieOnGround::UpdateBody(float dt, CBodyController& bc, CStateManager& mgr) +{ + return GetBodyStateTransition(dt, bc); +} + +void CBSLieOnGround::Shutdown(CBodyController& bc) +{ + bc.EnableAnimation(true); +} + +void CBSStep::Start(CBodyController& bc, CStateManager& mgr) +{ + const CBCStepCmd* cmd = + static_cast(bc.GetCommandMgr().GetCmd(EBodyStateCmd::Step)); + CPASAnimParmData parms(3, CPASAnimParm::FromEnum(s32(cmd->GetStepDirection())), + CPASAnimParm::FromEnum(s32(cmd->GetStepType()))); + bc.PlayBestAnimation(parms, *mgr.GetActiveRandom()); +} + +pas::EAnimationState CBSStep::GetBodyStateTransition(float dt, CBodyController& bc) +{ + if (bc.GetCommandMgr().GetCmd(EBodyStateCmd::Hurled)) + return pas::EAnimationState::Hurled; + if (bc.GetCommandMgr().GetCmd(EBodyStateCmd::KnockDown)) + return pas::EAnimationState::Fall; + if (bc.GetCommandMgr().GetCmd(EBodyStateCmd::LoopHitReaction)) + return pas::EAnimationState::LoopReaction; + if (bc.GetCommandMgr().GetCmd(EBodyStateCmd::KnockBack)) + return pas::EAnimationState::KnockBack; + if (bc.GetCommandMgr().GetCmd(EBodyStateCmd::Locomotion)) + return pas::EAnimationState::Locomotion; + if (bc.GetCommandMgr().GetCmd(EBodyStateCmd::Generate)) + return pas::EAnimationState::Generate; + if (bc.GetCommandMgr().GetCmd(EBodyStateCmd::MeleeAttack)) + return pas::EAnimationState::MeleeAttack; + if (bc.GetCommandMgr().GetCmd(EBodyStateCmd::ProjectileAttack)) + return pas::EAnimationState::ProjectileAttack; + if (bc.GetCommandMgr().GetCmd(EBodyStateCmd::LoopAttack)) + return pas::EAnimationState::LoopAttack; + if (bc.GetCommandMgr().GetCmd(EBodyStateCmd::Jump)) + return pas::EAnimationState::Jump; + if (bc.GetCommandMgr().GetCmd(EBodyStateCmd::LoopReaction)) + return pas::EAnimationState::LoopReaction; + if (bc.GetCommandMgr().GetCmd(EBodyStateCmd::Scripted)) + return pas::EAnimationState::Scripted; + if (bc.IsAnimationOver() || bc.GetCommandMgr().GetCmd(EBodyStateCmd::NextState)) + return pas::EAnimationState::Locomotion; + return pas::EAnimationState::Invalid; +} + +pas::EAnimationState CBSStep::UpdateBody(float dt, CBodyController& bc, CStateManager& mgr) +{ + pas::EAnimationState st = GetBodyStateTransition(dt, bc); + if (st == pas::EAnimationState::Invalid && !bc.GetCommandMgr().GetTargetVector().isZero()) + bc.FaceDirection(bc.GetCommandMgr().GetTargetVector(), dt); + return st; +} + +void CBSTurn::Start(CBodyController& bc, CStateManager& mgr) +{ + const zeus::CVector3f& lookDir = bc.GetOwner().GetTransform().basis[1]; + zeus::CVector2f lookDir2d(lookDir.x, lookDir.y); + x8_dest = zeus::CVector2f(bc.GetCommandMgr().GetFaceVector().x, + bc.GetCommandMgr().GetFaceVector().y); + float deltaAngle = zeus::radToDeg(zeus::CVector2f::getAngleDiff(lookDir2d, x8_dest)); + x10_turnDir = pas::ETurnDirection(zeus::CVector2f(lookDir2d.y, -lookDir2d.x).dot(x8_dest) > 0.f); + CPASAnimParmData parms(8, CPASAnimParm::FromEnum(s32(x10_turnDir)), + CPASAnimParm::FromReal32(deltaAngle), + CPASAnimParm::FromEnum(s32(bc.GetLocomotionType()))); + std::pair best = bc.GetPASDatabase().FindBestAnimation(parms, *mgr.GetActiveRandom(), -1); + CAnimPlaybackParms playParms(best.second, -1, 1.f, true); + bc.SetCurrentAnimation(playParms, false, false); + float animAngle = bc.GetPASDatabase().GetAnimState(8)->GetAnimParmData(best.second, 1).GetReal32Value(); + x4_rotateSpeed = zeus::degToRad((x10_turnDir == pas::ETurnDirection::Left) ? + animAngle - deltaAngle : deltaAngle - animAngle); + float timeRem = bc.GetAnimTimeRemaining(); + if (timeRem > 0.f) + x4_rotateSpeed /= timeRem; +} + +bool CBSTurn::FacingDest(CBodyController& bc) const +{ + const zeus::CVector3f& lookDir = bc.GetOwner().GetTransform().basis[1]; + zeus::CVector2f lookDir2d(lookDir.x, lookDir.y); + zeus::CVector2f leftDir(lookDir2d.y, -lookDir2d.x); + if (x10_turnDir == pas::ETurnDirection::Left) + { + if (leftDir.dot(x8_dest) < 0.f) + return true; + } + else + { + if (leftDir.dot(x8_dest) > 0.f) + return true; + } + return false; +} + +pas::EAnimationState CBSTurn::GetBodyStateTransition(float dt, CBodyController& bc) +{ + if (bc.GetCommandMgr().GetCmd(EBodyStateCmd::Hurled)) + return pas::EAnimationState::Hurled; + if (bc.GetCommandMgr().GetCmd(EBodyStateCmd::KnockDown)) + return pas::EAnimationState::Fall; + if (bc.GetCommandMgr().GetCmd(EBodyStateCmd::LoopHitReaction)) + return pas::EAnimationState::LoopReaction; + if (bc.GetCommandMgr().GetCmd(EBodyStateCmd::KnockBack)) + return pas::EAnimationState::KnockBack; + if (bc.GetCommandMgr().GetCmd(EBodyStateCmd::Locomotion)) + return pas::EAnimationState::Locomotion; + if (bc.GetCommandMgr().GetCmd(EBodyStateCmd::Generate)) + return pas::EAnimationState::Generate; + if (bc.GetCommandMgr().GetCmd(EBodyStateCmd::MeleeAttack)) + return pas::EAnimationState::MeleeAttack; + if (bc.GetCommandMgr().GetCmd(EBodyStateCmd::ProjectileAttack)) + return pas::EAnimationState::ProjectileAttack; + if (bc.GetCommandMgr().GetCmd(EBodyStateCmd::LoopAttack)) + return pas::EAnimationState::LoopAttack; + if (bc.GetCommandMgr().GetCmd(EBodyStateCmd::LoopReaction)) + return pas::EAnimationState::LoopReaction; + if (bc.GetCommandMgr().GetCmd(EBodyStateCmd::Jump)) + return pas::EAnimationState::Jump; + if (bc.GetCommandMgr().GetCmd(EBodyStateCmd::Step)) + return pas::EAnimationState::Step; + if (bc.GetCommandMgr().GetCmd(EBodyStateCmd::Scripted)) + return pas::EAnimationState::Scripted; + if (bc.IsAnimationOver() || FacingDest(bc) || !bc.GetCommandMgr().GetMoveVector().isZero()) + return pas::EAnimationState::Locomotion; + return pas::EAnimationState::Invalid; +} + +pas::EAnimationState CBSTurn::UpdateBody(float dt, CBodyController& bc, CStateManager& mgr) +{ + pas::EAnimationState st = GetBodyStateTransition(dt, bc); + if (st == pas::EAnimationState::Invalid) + { + zeus::CQuaternion quat; + quat.rotateZ(x4_rotateSpeed * dt); + bc.SetDeltaRotation(quat); + } + return st; +} + +void CBSFlyerTurn::Start(CBodyController& bc, CStateManager& mgr) +{ + if (bc.GetPASDatabase().GetAnimState(8)->GetNumAnims()) + { + CBSTurn::Start(bc, mgr); + } + else + { + x8_dest = zeus::CVector2f(bc.GetCommandMgr().GetFaceVector().x, + bc.GetCommandMgr().GetFaceVector().y); + const zeus::CVector3f& lookDir = bc.GetOwner().GetTransform().basis[1]; + zeus::CVector2f lookDir2d(lookDir.x, lookDir.y); + x10_turnDir = pas::ETurnDirection(zeus::CVector2f(lookDir2d.y, -lookDir2d.x).dot(x8_dest) > 0.f); + CPASAnimParmData parms(5, CPASAnimParm::FromEnum(0), + CPASAnimParm::FromEnum(s32(bc.GetLocomotionType()))); + std::pair best = bc.GetPASDatabase().FindBestAnimation(parms, *mgr.GetActiveRandom(), -1); + if (best.second != bc.GetCurrentAnimId()) + { + CAnimPlaybackParms playParms(best.second, -1, 1.f, true); + bc.SetCurrentAnimation(playParms, true, false); + } + } +} + +pas::EAnimationState CBSFlyerTurn::UpdateBody(float dt, CBodyController& bc, CStateManager& mgr) +{ + if (bc.GetPASDatabase().GetAnimState(8)->GetNumAnims()) + { + return CBSTurn::UpdateBody(dt, bc, mgr); + } + else + { + pas::EAnimationState st = GetBodyStateTransition(dt, bc); + if (st == pas::EAnimationState::Invalid) + { + if (!bc.GetCommandMgr().GetFaceVector().isZero()) + { + x8_dest = zeus::CVector2f(bc.GetCommandMgr().GetFaceVector().x, + bc.GetCommandMgr().GetFaceVector().y); + const zeus::CVector3f& lookDir = bc.GetOwner().GetTransform().basis[1]; + zeus::CVector2f lookDir2d(lookDir.x, lookDir.y); + x10_turnDir = pas::ETurnDirection(zeus::CVector2f(lookDir2d.y, -lookDir2d.x).dot(x8_dest) > 0.f); + } + bc.FaceDirection(zeus::CVector3f(x8_dest.x, x8_dest.y, 0.f), dt); + } + return st; + } +} + +void CBSLoopAttack::Start(CBodyController& bc, CStateManager& mgr) +{ + const CBCLoopAttackCmd* cmd = + static_cast(bc.GetCommandMgr().GetCmd(EBodyStateCmd::LoopAttack)); + x8_loopAttackType = cmd->GetAttackType(); + xc_24_waitForAnimOver = cmd->WaitForAnimOver(); + xc_25_advance = false; + if (bc.GetLocomotionType() == pas::ELocomotionType::Crouch) + { + CPASAnimParmData parms(9, CPASAnimParm::FromEnum(s32(x4_state)), + CPASAnimParm::FromEnum(s32(x8_loopAttackType))); + bc.LoopBestAnimation(parms, *mgr.GetActiveRandom()); + } + else + { + x4_state = pas::ELoopState::Begin; + CPASAnimParmData parms(9, CPASAnimParm::FromEnum(s32(x4_state)), + CPASAnimParm::FromEnum(s32(x8_loopAttackType))); + std::pair best = bc.GetPASDatabase().FindBestAnimation(parms, *mgr.GetActiveRandom(), -1); + if (best.first > FLT_EPSILON) + { + CAnimPlaybackParms playParms(best.second, -1, 1.f, true); + bc.SetCurrentAnimation(playParms, false, false); + } + else + { + x4_state = pas::ELoopState::Loop; + CPASAnimParmData parms(9, CPASAnimParm::FromEnum(s32(x4_state)), + CPASAnimParm::FromEnum(s32(x8_loopAttackType))); + bc.LoopBestAnimation(parms, *mgr.GetActiveRandom()); + } + } +} + +pas::EAnimationState CBSLoopAttack::GetBodyStateTransition(float dt, CBodyController& bc) +{ + if (bc.GetCommandMgr().GetCmd(EBodyStateCmd::Hurled)) + return pas::EAnimationState::Hurled; + if (bc.GetCommandMgr().GetCmd(EBodyStateCmd::KnockDown)) + return pas::EAnimationState::Fall; + if (bc.GetCommandMgr().GetCmd(EBodyStateCmd::LoopHitReaction)) + return pas::EAnimationState::LoopReaction; + if (bc.GetCommandMgr().GetCmd(EBodyStateCmd::KnockBack)) + return pas::EAnimationState::KnockBack; + if (bc.GetCommandMgr().GetCmd(EBodyStateCmd::Locomotion)) + return pas::EAnimationState::Locomotion; + if (x4_state == pas::ELoopState::End) + { + if (bc.GetCommandMgr().GetCmd(EBodyStateCmd::MeleeAttack)) + return pas::EAnimationState::MeleeAttack; + if (bc.GetCommandMgr().GetCmd(EBodyStateCmd::ProjectileAttack)) + return pas::EAnimationState::ProjectileAttack; + if (bc.GetCommandMgr().GetCmd(EBodyStateCmd::LoopAttack)) + return pas::EAnimationState::LoopAttack; + if (bc.GetCommandMgr().GetCmd(EBodyStateCmd::Step)) + return pas::EAnimationState::Step; + if (!bc.GetCommandMgr().GetMoveVector().isZero()) + return pas::EAnimationState::Locomotion; + if (!bc.GetCommandMgr().GetFaceVector().isZero()) + return pas::EAnimationState::Turn; + } + return pas::EAnimationState::Invalid; +} + +pas::EAnimationState CBSLoopAttack::UpdateBody(float dt, CBodyController& bc, CStateManager& mgr) +{ + pas::EAnimationState st = GetBodyStateTransition(dt, bc); + if (st == pas::EAnimationState::Invalid) + { + xc_25_advance |= bc.GetCommandMgr().GetCmd(EBodyStateCmd::ExitState) != nullptr; + switch (x4_state) + { + case pas::ELoopState::Begin: + if (xc_25_advance && (!xc_24_waitForAnimOver || bc.IsAnimationOver())) + { + x4_state = pas::ELoopState::Invalid; + return pas::EAnimationState::Locomotion; + } + if (bc.IsAnimationOver()) + { + CPASAnimParmData parms(9, CPASAnimParm::FromEnum(1), + CPASAnimParm::FromEnum(s32(x8_loopAttackType))); + bc.LoopBestAnimation(parms, *mgr.GetActiveRandom()); + x4_state = pas::ELoopState::Loop; + } + else if (!bc.GetCommandMgr().GetTargetVector().isZero()) + { + bc.FaceDirection(bc.GetCommandMgr().GetTargetVector(), dt); + } + break; + case pas::ELoopState::Loop: + if (xc_25_advance && (!xc_24_waitForAnimOver || bc.IsAnimationOver())) + { + if (bc.GetLocomotionType() != pas::ELocomotionType::Crouch) + { + CPASAnimParmData parms(9, CPASAnimParm::FromEnum(2), + CPASAnimParm::FromEnum(s32(x8_loopAttackType))); + bc.PlayBestAnimation(parms, *mgr.GetActiveRandom()); + x4_state = pas::ELoopState::End; + } + else + { + x4_state = pas::ELoopState::Invalid; + return pas::EAnimationState::Locomotion; + } + } + break; + case pas::ELoopState::End: + if (bc.IsAnimationOver()) + { + x4_state = pas::ELoopState::Invalid; + return pas::EAnimationState::Locomotion; + } + break; + default: break; + } + } + return st; +} + +void CBSLoopReaction::Start(CBodyController& bc, CStateManager& mgr) +{ + if (const CBCLoopReactionCmd* cmd = + static_cast(bc.GetCommandMgr().GetCmd(EBodyStateCmd::LoopReaction))) + { + x8_reactionType = cmd->GetReactionType(); + xc_24_loopHit = false; + } + else + { + const CBCLoopHitReactionCmd* hcmd = + static_cast(bc.GetCommandMgr().GetCmd(EBodyStateCmd::LoopHitReaction)); + x8_reactionType = hcmd->GetReactionType(); + xc_24_loopHit = true; + } + + x4_state = pas::ELoopState::Begin; + CPASAnimParmData parms(10, CPASAnimParm::FromEnum(s32(x8_reactionType)), + CPASAnimParm::FromEnum(s32(x4_state))); + std::pair best = bc.GetPASDatabase().FindBestAnimation(parms, *mgr.GetActiveRandom(), -1); + if (best.first > FLT_EPSILON) + { + CAnimPlaybackParms playParms(best.second, -1, 1.f, true); + bc.SetCurrentAnimation(playParms, false, false); + } + else + { + x4_state = pas::ELoopState::Loop; + CPASAnimParmData parms(10, CPASAnimParm::FromEnum(s32(x8_reactionType)), + CPASAnimParm::FromEnum(s32(x4_state))); + bc.LoopBestAnimation(parms, *mgr.GetActiveRandom()); + } +} + +pas::EAnimationState CBSLoopReaction::GetBodyStateTransition(float dt, CBodyController& bc) +{ + if (bc.GetCommandMgr().GetCmd(EBodyStateCmd::Hurled)) + return pas::EAnimationState::Hurled; + if (bc.GetCommandMgr().GetCmd(EBodyStateCmd::KnockDown)) + return pas::EAnimationState::Fall; + if (bc.GetCommandMgr().GetCmd(EBodyStateCmd::KnockBack)) + return pas::EAnimationState::KnockBack; + if (!xc_24_loopHit && bc.GetCommandMgr().GetCmd(EBodyStateCmd::Locomotion)) + return pas::EAnimationState::Locomotion; + if (x4_state == pas::ELoopState::End) + { + if (bc.GetCommandMgr().GetCmd(EBodyStateCmd::MeleeAttack)) + return pas::EAnimationState::MeleeAttack; + if (bc.GetCommandMgr().GetCmd(EBodyStateCmd::ProjectileAttack)) + return pas::EAnimationState::ProjectileAttack; + if (bc.GetCommandMgr().GetCmd(EBodyStateCmd::LoopAttack)) + return pas::EAnimationState::LoopAttack; + if (bc.GetCommandMgr().GetCmd(EBodyStateCmd::Step)) + return pas::EAnimationState::Step; + if (!bc.GetCommandMgr().GetMoveVector().isZero()) + return pas::EAnimationState::Locomotion; + if (!bc.GetCommandMgr().GetFaceVector().isZero()) + return pas::EAnimationState::Turn; + } + return pas::EAnimationState::Invalid; +} + +bool CBSLoopReaction::PlayExitAnimation(CBodyController& bc, CStateManager& mgr) const +{ + CPASAnimParmData parms(10, CPASAnimParm::FromEnum(int(x8_reactionType)), + CPASAnimParm::FromEnum(2)); + std::pair best = bc.GetPASDatabase().FindBestAnimation(parms, *mgr.GetActiveRandom(), -1); + if (best.first > 0.f) + { + CAnimPlaybackParms playParms(best.second, -1, 1.f, true); + bc.SetCurrentAnimation(playParms, false, false); + return true; + } + return false; +} + +pas::EAnimationState CBSLoopReaction::UpdateBody(float dt, CBodyController& bc, CStateManager& mgr) +{ + pas::EAnimationState st = GetBodyStateTransition(dt, bc); + if (st == pas::EAnimationState::Invalid) + { + switch (x4_state) + { + case pas::ELoopState::Begin: + if (bc.GetCommandMgr().GetCmd(EBodyStateCmd::ExitState)) + { + if (PlayExitAnimation(bc, mgr)) + { + x4_state = pas::ELoopState::End; + } + else + { + x4_state = pas::ELoopState::Invalid; + return pas::EAnimationState::Locomotion; + } + } + else + { + if (bc.IsAnimationOver()) + { + CPASAnimParmData parms(10, CPASAnimParm::FromEnum(s32(x8_reactionType)), + CPASAnimParm::FromEnum(1)); + bc.LoopBestAnimation(parms, *mgr.GetActiveRandom()); + x4_state = pas::ELoopState::Loop; + } + else if (!bc.GetCommandMgr().GetTargetVector().isZero()) + { + bc.FaceDirection(bc.GetCommandMgr().GetTargetVector(), dt); + } + } + break; + case pas::ELoopState::Loop: + if (bc.GetCommandMgr().GetCmd(EBodyStateCmd::ExitState)) + { + if (PlayExitAnimation(bc, mgr)) + { + x4_state = pas::ELoopState::End; + } + else + { + x4_state = pas::ELoopState::Invalid; + return pas::EAnimationState::Locomotion; + } + } + else if (!bc.GetCommandMgr().GetTargetVector().isZero()) + { + bc.FaceDirection(bc.GetCommandMgr().GetTargetVector(), dt); + } + break; + case pas::ELoopState::End: + if (bc.IsAnimationOver()) + { + x4_state = pas::ELoopState::Invalid; + return pas::EAnimationState::Locomotion; + } + break; + default: break; + } + } + return st; +} + +void CBSGroundHit::Start(CBodyController& bc, CStateManager& mgr) +{ + const CBCKnockBackCmd* cmd = + static_cast(bc.GetCommandMgr().GetCmd(EBodyStateCmd::KnockBack)); + zeus::CVector3f localDir = bc.GetOwner().GetTransform().transposeRotate(cmd->GetHitDirection()); + zeus::CRelAngle angle = std::atan2(localDir.y, localDir.x); + CPASAnimParmData parms(11, CPASAnimParm::FromEnum(s32(bc.GetFallState())), + CPASAnimParm::FromReal32(angle.asDegrees())); + std::pair best = bc.GetPASDatabase().FindBestAnimation(parms, *mgr.GetActiveRandom(), -1); + CAnimPlaybackParms playParms(best.second, -1, 1.f, true); + bc.SetCurrentAnimation(playParms, false, false); + const CPASAnimState* groundHitState = bc.GetPASDatabase().GetAnimState(11); + if (!groundHitState->GetAnimParmData(best.second, 2).GetBoolValue()) + { + float animAngle = zeus::degToRad(groundHitState->GetAnimParmData(best.second, 1).GetReal32Value()); + zeus::CRelAngle delta1 = angle - animAngle; + zeus::CRelAngle delta2 = animAngle - angle; + float minAngle = std::min(float(delta1), float(delta2)); + 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; + } + xc_fallState = pas::EFallState(groundHitState->GetAnimParmData(best.second, 3).GetEnumValue()); +} + +pas::EAnimationState CBSGroundHit::GetBodyStateTransition(float dt, CBodyController& bc) +{ + if (bc.IsAnimationOver()) + { + if (bc.GetCommandMgr().GetCmd(EBodyStateCmd::Die)) + return pas::EAnimationState::Death; + return pas::EAnimationState::LieOnGround; + } + return pas::EAnimationState::Invalid; +} + +pas::EAnimationState CBSGroundHit::UpdateBody(float dt, CBodyController& bc, CStateManager& mgr) +{ + pas::EAnimationState st = GetBodyStateTransition(dt, bc); + if (st == pas::EAnimationState::Invalid && x8_remTime > 0.f) + { + zeus::CQuaternion quat; + quat.rotateZ(x4_rotateSpeed * dt); + bc.SetDeltaRotation(quat); + x8_remTime -= dt; + } + return st; +} + +void CBSGroundHit::Shutdown(CBodyController& bc) +{ + bc.SetFallState(xc_fallState); +} + +void CBSGenerate::Start(CBodyController& bc, CStateManager& mgr) +{ + const CBCGenerateCmd* cmd = + static_cast(bc.GetCommandMgr().GetCmd(EBodyStateCmd::Generate)); + s32 anim; + if (!cmd->UseSpecialAnimId()) + { + CPASAnimParmData parms(12, CPASAnimParm::FromEnum(s32(cmd->GetGenerateType()))); + std::pair best = bc.GetPASDatabase().FindBestAnimation(parms, *mgr.GetActiveRandom(), -1); + anim = best.second; + } + else + { + anim = cmd->GetSpecialAnimId(); + } + + if (cmd->HasExitTargetPos()) + { + CAnimPlaybackParms playParms(anim, nullptr, &cmd->GetExitTargetPos(), &bc.GetOwner().GetTransform(), + &bc.GetOwner().GetModelData()->GetScale(), false); + bc.SetCurrentAnimation(playParms, false, false); + } + else + { + CAnimPlaybackParms playParms(anim, -1, 1.f, true); + bc.SetCurrentAnimation(playParms, false, false); + } +} + +pas::EAnimationState CBSGenerate::GetBodyStateTransition(float dt, CBodyController& bc) +{ + if (bc.GetCommandMgr().GetCmd(EBodyStateCmd::Hurled)) + return pas::EAnimationState::Hurled; + if (bc.GetCommandMgr().GetCmd(EBodyStateCmd::KnockDown)) + return pas::EAnimationState::Fall; + if (bc.GetCommandMgr().GetCmd(EBodyStateCmd::Generate)) + return pas::EAnimationState::Generate; + if (bc.IsAnimationOver() || bc.GetCommandMgr().GetCmd(EBodyStateCmd::NextState)) + return pas::EAnimationState::Locomotion; + return pas::EAnimationState::Invalid; +} + +pas::EAnimationState CBSGenerate::UpdateBody(float dt, CBodyController& bc, CStateManager& mgr) +{ + pas::EAnimationState st = GetBodyStateTransition(dt, bc); + if (st == pas::EAnimationState::Invalid) + if (!bc.GetCommandMgr().GetTargetVector().isZero()) + bc.FaceDirection(bc.GetCommandMgr().GetTargetVector(), dt); + return st; +} + +void CBSJump::Start(CBodyController& bc, CStateManager& mgr) +{ + const CBCJumpCmd* cmd = + static_cast(bc.GetCommandMgr().GetCmd(EBodyStateCmd::Jump)); + x8_jumpType = cmd->GetJumpType(); + xc_waypoint1 = cmd->GetJumpTarget(); + x24_waypoint2 = cmd->GetSecondJumpTarget(); + x30_25_wallJump = cmd->IsWallJump(); + x30_28_startInJumpLoop = cmd->StartInJumpLoop(); + x30_24_bodyForceSet = false; + x30_27_wallBounceComplete = false; + if (x30_25_wallJump) + x30_26_wallBounceRight = + (xc_waypoint1 - bc.GetOwner().GetTranslation()).cross(zeus::CVector3f::skUp). + dot(x24_waypoint2 - xc_waypoint1) < 0.f; + if (!cmd->StartInJumpLoop()) + { + x4_state = pas::EJumpState::IntoJump; + CPASAnimParmData parms(13, CPASAnimParm::FromEnum(s32(x4_state)), + CPASAnimParm::FromEnum(s32(x8_jumpType))); + bc.PlayBestAnimation(parms, *mgr.GetActiveRandom()); + } + else + { + PlayJumpLoop(mgr, bc); + } +} + +pas::EAnimationState CBSJump::GetBodyStateTransition(float dt, CBodyController& bc) +{ + if (bc.GetCommandMgr().GetCmd(EBodyStateCmd::Hurled)) + return pas::EAnimationState::Hurled; + if (bc.GetCommandMgr().GetCmd(EBodyStateCmd::KnockDown)) + return pas::EAnimationState::Fall; + if (bc.GetCommandMgr().GetCmd(EBodyStateCmd::Jump) && + bc.GetBodyType() == EBodyType::WallWalker) + return pas::EAnimationState::Jump; + return pas::EAnimationState::Invalid; +} + +bool CBSJump::CheckForWallJump(CBodyController& bc, CStateManager& mgr) +{ + if (x30_25_wallJump && !x30_27_wallBounceComplete) + { + if (TCastToPtr act = bc.GetOwner()) + { + float distToWall = (xc_waypoint1 - act->GetTranslation()).magnitude(); + zeus::CAABox aabb = act->GetBoundingBox(); + float xExtent = (aabb.max.x - aabb.min.x) * 0.5f; + if (distToWall < 1.414f * xExtent || (act->GetX328_26() && distToWall < 3.f * xExtent)) + { + x4_state = x30_26_wallBounceRight ? pas::EJumpState::WallBounceRight : pas::EJumpState::WallBounceLeft; + CPASAnimParmData parms(13, CPASAnimParm::FromEnum(s32(x4_state)), + CPASAnimParm::FromEnum(s32(x8_jumpType))); + bc.PlayBestAnimation(parms, *mgr.GetActiveRandom()); + mgr.SendScriptMsg(act.GetPtr(), kInvalidUniqueId, EScriptObjectMessage::OnFloor); + return true; + } + } + } + return false; +} + +void CBSJump::CheckForLand(CBodyController& bc, CStateManager& mgr) +{ + if (TCastToPtr act = bc.GetOwner()) + { + if (act->GetX328_26() || act->IsOnGround()) + { + x4_state = pas::EJumpState::OutOfJump; + CPASAnimParmData parms(13, CPASAnimParm::FromEnum(s32(x4_state)), + CPASAnimParm::FromEnum(s32(x8_jumpType))); + bc.PlayBestAnimation(parms, *mgr.GetActiveRandom()); + mgr.SendScriptMsg(act.GetPtr(), kInvalidUniqueId, EScriptObjectMessage::OnFloor); + } + } +} + +void CBSJump::PlayJumpLoop(CStateManager& mgr, CBodyController& bc) +{ + CPASAnimParmData parms(13, CPASAnimParm::FromEnum(1), + CPASAnimParm::FromEnum(s32(x8_jumpType))); + std::pair best = bc.GetPASDatabase().FindBestAnimation(parms, *mgr.GetActiveRandom(), -1); + if (best.first > 99.f) + { + x4_state = pas::EJumpState::AmbushJump; + CAnimPlaybackParms playParms(best.second, -1, 1.f, true); + bc.SetCurrentAnimation(playParms, false, false); + } + else + { + x4_state = pas::EJumpState::Loop; + CPASAnimParmData parms(13, CPASAnimParm::FromEnum(s32(x4_state)), + CPASAnimParm::FromEnum(s32(x8_jumpType))); + bc.LoopBestAnimation(parms, *mgr.GetActiveRandom()); + } + + if (TCastToPtr act = bc.GetOwner()) + { + mgr.SendScriptMsg(act.GetPtr(), kInvalidUniqueId, EScriptObjectMessage::Falling); + mgr.SendScriptMsg(act.GetPtr(), kInvalidUniqueId, EScriptObjectMessage::InternalMessage18); + x30_24_bodyForceSet = false; + x18_velocity = act->GetVelocity(); + } +} + +pas::EAnimationState CBSJump::UpdateBody(float dt, CBodyController& bc, CStateManager& mgr) +{ + pas::EAnimationState st = GetBodyStateTransition(dt, bc); + if (st == pas::EAnimationState::Invalid) + { + switch (x4_state) + { + case pas::EJumpState::IntoJump: + if (bc.IsAnimationOver()) + PlayJumpLoop(mgr, bc); + break; + case pas::EJumpState::AmbushJump: + if (!x30_24_bodyForceSet) + { + if (TCastToPtr act = bc.GetOwner()) + act->SetConstantForce(act->GetMass() * x18_velocity); + x30_24_bodyForceSet = true; + } + if (!bc.GetCommandMgr().GetTargetVector().isZero()) + bc.FaceDirection(bc.GetCommandMgr().GetTargetVector(), dt); + if (bc.IsAnimationOver()) + { + x4_state = pas::EJumpState::Loop; + CPASAnimParmData parms(13, CPASAnimParm::FromEnum(s32(x4_state)), + CPASAnimParm::FromEnum(s32(x8_jumpType))); + bc.LoopBestAnimation(parms, *mgr.GetActiveRandom()); + } + else + { + if (!CheckForWallJump(bc, mgr)) + CheckForLand(bc, mgr); + } + break; + case pas::EJumpState::Loop: + if (!x30_24_bodyForceSet) + { + if (TCastToPtr act = bc.GetOwner()) + act->SetConstantForce(act->GetMass() * x18_velocity); + x30_24_bodyForceSet = true; + } + if (!bc.GetCommandMgr().GetTargetVector().isZero()) + bc.FaceDirection(bc.GetCommandMgr().GetTargetVector(), dt); + if (!CheckForWallJump(bc, mgr)) + CheckForLand(bc, mgr); + break; + case pas::EJumpState::WallBounceLeft: + case pas::EJumpState::WallBounceRight: + if (TCastToPtr act = bc.GetOwner()) + { + act->Stop(); + act->SetMomentumWR(zeus::CVector3f::skZero); + } + if (bc.IsAnimationOver()) + { + mgr.SendScriptMsg(&bc.GetOwner(), kInvalidUniqueId, EScriptObjectMessage::Falling); + x4_state = pas::EJumpState::Loop; + CPASAnimParmData parms(13, CPASAnimParm::FromEnum(s32(x4_state)), + CPASAnimParm::FromEnum(s32(x8_jumpType))); + bc.LoopBestAnimation(parms, *mgr.GetActiveRandom()); + x30_27_wallBounceComplete = true; + if (TCastToPtr act = bc.GetOwner()) + { + zeus::CVector3f toFinal = x24_waypoint2 - act->GetTranslation(); + float tmp = std::sqrt(act->GetGravityConstant() / (2.f * toFinal.z)); + act->SetVelocityWR(zeus::CVector3f(tmp * toFinal.x, tmp * toFinal.y, 0.f)); + } + } + break; + case pas::EJumpState::OutOfJump: + if (bc.IsAnimationOver()) + { + x4_state = pas::EJumpState::Invalid; + return pas::EAnimationState::Locomotion; + } + break; + default: break; + } + } + return st; +} + +bool CBSJump::ApplyAnimationDeltas() const +{ + if (x4_state == pas::EJumpState::AmbushJump || x4_state == pas::EJumpState::Loop) + return false; + return true; +} + +bool CBSJump::IsInAir(const CBodyController& bc) const +{ + return x4_state == pas::EJumpState::AmbushJump || x4_state == pas::EJumpState::Loop; +} + +bool CBSJump::CanShoot() const +{ + return x4_state == pas::EJumpState::AmbushJump || x4_state == pas::EJumpState::Loop; +} + +void CBSHurled::Start(CBodyController& bc, CStateManager& mgr) +{ + const CBCHurledCmd* cmd = + static_cast(bc.GetCommandMgr().GetCmd(EBodyStateCmd::Hurled)); + x4_state = pas::EHurledState(cmd->GetSkipLaunchState()); + zeus::CVector3f localDir = bc.GetOwner().GetTransform().transposeRotate(cmd->GetHitDirection()); + zeus::CRelAngle angle = std::atan2(localDir.y, localDir.x); + x8_knockAngle = angle.asDegrees(); + CPASAnimParmData parms(14, CPASAnimParm::FromInt32(-1), + CPASAnimParm::FromReal32(x8_knockAngle), + CPASAnimParm::FromEnum(s32(x4_state))); + std::pair best = bc.GetPASDatabase().FindBestAnimation(parms, *mgr.GetActiveRandom(), -1); + CAnimPlaybackParms playParms(best.second, -1, 1.f, true); + bc.SetCurrentAnimation(playParms, false, false); + const CPASAnimState* hurledState = bc.GetPASDatabase().GetAnimState(14); + xc_animSeries = hurledState->GetAnimParmData(best.second, 0).GetInt32Value(); + mgr.SendScriptMsg(&bc.GetOwner(), kInvalidUniqueId, EScriptObjectMessage::Falling); + mgr.SendScriptMsg(&bc.GetOwner(), kInvalidUniqueId, EScriptObjectMessage::InternalMessage18); + if (!zeus::close_enough(cmd->GetLaunchVelocity(), zeus::CVector3f::skZero, 0.0001f)) + if (TCastToPtr act = bc.GetOwner()) + act->SetConstantForce(act->GetMass() * cmd->GetLaunchVelocity()); + float animAngle = zeus::degToRad(hurledState->GetAnimParmData(best.second, 1).GetReal32Value()); + zeus::CRelAngle delta1 = angle - animAngle; + zeus::CRelAngle delta2 = animAngle - angle; + float minAngle = std::min(float(delta1), float(delta2)); + x14_remTime = 0.15f * bc.GetAnimTimeRemaining(); + float flippedAngle = (delta1 > M_PIF) ? -minAngle : minAngle; + x10_rotateSpeed = (x14_remTime > FLT_EPSILON) ? flippedAngle / x14_remTime : flippedAngle; + x18_curTime = 0.f; + x2c_24_needsRecover = false; +} + +pas::EAnimationState CBSHurled::GetBodyStateTransition(float dt, CBodyController& bc) +{ + if (bc.GetCommandMgr().GetCmd(EBodyStateCmd::NextState)) + return pas::EAnimationState::LieOnGround; + if (x18_curTime > 0.25f) + { + if (const CBCHurledCmd* cmd = + static_cast(bc.GetCommandMgr().GetCmd(EBodyStateCmd::Hurled))) + { + const_cast(cmd)->SetSkipLaunchState(true); + return pas::EAnimationState::Hurled; + } + } + return pas::EAnimationState::Invalid; +} + +void CBSHurled::Recover(CStateManager& mgr, CBodyController& bc, pas::EHurledState state) +{ + CPASAnimParmData parms(14, CPASAnimParm::FromInt32(xc_animSeries), + CPASAnimParm::FromReal32(x8_knockAngle), + CPASAnimParm::FromEnum(s32(state))); + std::pair best = bc.GetPASDatabase().FindBestAnimation(parms, *mgr.GetActiveRandom(), -1); + if (best.first > FLT_EPSILON) + { + CAnimPlaybackParms playParms(best.second, -1, 1.f, true); + bc.SetCurrentAnimation(playParms, false, false); + x4_state = state; + if (TCastToPtr act = bc.GetOwner()) + act->SetMomentumWR(zeus::CVector3f::skZero); + } + x2c_24_needsRecover = false; +} + +void CBSHurled::PlayStrikeWallAnimation(CBodyController& bc, CStateManager& mgr) +{ + CPASAnimParmData parms(14, CPASAnimParm::FromInt32(xc_animSeries), + CPASAnimParm::FromReal32(x8_knockAngle), + CPASAnimParm::FromEnum(3)); + std::pair best = bc.GetPASDatabase().FindBestAnimation(parms, *mgr.GetActiveRandom(), -1); + if (best.first > FLT_EPSILON) + { + CAnimPlaybackParms playParms(best.second, -1, 1.f, true); + bc.SetCurrentAnimation(playParms, false, false); + x4_state = pas::EHurledState::StrikeWall; + } +} + +void CBSHurled::PlayLandAnimation(CBodyController& bc, CStateManager& mgr) +{ + CPASAnimParmData parms(14, CPASAnimParm::FromInt32(xc_animSeries), + CPASAnimParm::FromReal32(x8_knockAngle), + CPASAnimParm::FromEnum(s32(x4_state))); + std::pair best = bc.GetPASDatabase().FindBestAnimation(parms, *mgr.GetActiveRandom(), -1); + CAnimPlaybackParms playParms(best.second, -1, 1.f, true); + bc.SetCurrentAnimation(playParms, false, false); + const CPASAnimState* hurledState = bc.GetPASDatabase().GetAnimState(14); + bc.SetFallState(pas::EFallState(hurledState->GetAnimParmData(best.second, 3).GetEnumValue())); + if (TCastToPtr act = bc.GetOwner()) + mgr.SendScriptMsg(act.GetPtr(), kInvalidUniqueId, EScriptObjectMessage::OnFloor); +} + +bool CBSHurled::ShouldStartStrikeWall(CBodyController& bc) const +{ + if (TCastToPtr ai = bc.GetOwner()) + { + if (ai->GetX328_26()) + if (!ai->IsOnGround()) + return true; + } + return false; +} + +bool CBSHurled::ShouldStartLand(float dt, CBodyController& bc) const +{ + bool ret = true; + if (TCastToPtr ai = bc.GetOwner()) + { + ret = false; + if (ai->IsOnGround()) + return true; + if (zeus::close_enough(ai->GetTranslation(), x1c_lastTranslation, 0.0001f) && + ai->GetVelocity().z < 0.f) + { + x28_landedDur += dt; + if (x28_landedDur >= 0.25f) + ret = true; + } + else + { + x28_landedDur = 0.f; + } + x1c_lastTranslation = ai->GetTranslation(); + } + return ret; +} + +pas::EAnimationState CBSHurled::UpdateBody(float dt, CBodyController& bc, CStateManager& mgr) +{ + pas::EAnimationState st = GetBodyStateTransition(dt, bc); + if (st == pas::EAnimationState::Invalid) + { + x18_curTime += dt; + if (x14_remTime > 0.f) + { + zeus::CQuaternion quat; + quat.rotateZ(x10_rotateSpeed * dt); + bc.SetDeltaRotation(quat); + x14_remTime -= dt; + } + if (bc.GetCommandMgr().GetCmd(EBodyStateCmd::ExitState)) + x2c_24_needsRecover = true; + switch (x4_state) + { + case pas::EHurledState::KnockIntoAir: + { + if (bc.IsAnimationOver()) + { + CPASAnimParmData parms(14, CPASAnimParm::FromInt32(xc_animSeries), + CPASAnimParm::FromReal32(x8_knockAngle), + CPASAnimParm::FromEnum(1)); + bc.LoopBestAnimation(parms, *mgr.GetActiveRandom()); + x4_state = pas::EHurledState::KnockLoop; + x28_landedDur = 0.f; + } + break; + } + case pas::EHurledState::KnockLoop: + if (ShouldStartLand(dt, bc)) + { + x4_state = pas::EHurledState::KnockDown; + PlayLandAnimation(bc, mgr); + } + else if (ShouldStartStrikeWall(bc)) + { + PlayStrikeWallAnimation(bc, mgr); + if (TCastToPtr ai = bc.GetOwner()) + ai->SetVelocityWR(zeus::CVector3f::skDown * (2.f * dt * ai->GetGravityConstant())); + } + else if (x2c_24_needsRecover) + { + Recover(mgr, bc, pas::EHurledState::Six); + } + break; + case pas::EHurledState::StrikeWall: + if (bc.IsAnimationOver()) + { + x4_state = pas::EHurledState::StrikeWallFallLoop; + CPASAnimParmData parms(14, CPASAnimParm::FromInt32(xc_animSeries), + CPASAnimParm::FromReal32(x8_knockAngle), + CPASAnimParm::FromEnum(s32(x4_state))); + bc.LoopBestAnimation(parms, *mgr.GetActiveRandom()); + x28_landedDur = 0.f; + } + break; + case pas::EHurledState::StrikeWallFallLoop: + if (ShouldStartLand(dt, bc)) + { + x4_state = pas::EHurledState::OutOfStrikeWall; + PlayLandAnimation(bc, mgr); + } + else if (x2c_24_needsRecover) + { + Recover(mgr, bc, pas::EHurledState::Seven); + } + break; + case pas::EHurledState::Six: + case pas::EHurledState::Seven: + if (TCastToPtr act = bc.GetOwner()) + act->SetVelocityWR(act->GetVelocity() * std::pow(0.9, 60.0 * dt)); + if (bc.IsAnimationOver()) + { + x4_state = pas::EHurledState::Invalid; + return pas::EAnimationState::Locomotion; + } + break; + case pas::EHurledState::KnockDown: + case pas::EHurledState::OutOfStrikeWall: + if (bc.IsAnimationOver()) + { + x4_state = pas::EHurledState::Invalid; + if (bc.GetFallState() == pas::EFallState::Zero) + return pas::EAnimationState::Locomotion; + return pas::EAnimationState::LieOnGround; + } + break; + default: break; + } + } + return st; +} + +void CBSSlide::Start(CBodyController& bc, CStateManager& mgr) +{ + const CBCSlideCmd* cmd = + static_cast(bc.GetCommandMgr().GetCmd(EBodyStateCmd::Slide)); + zeus::CVector3f localDir = bc.GetOwner().GetTransform().transposeRotate(cmd->GetSlideDirection()); + float angle = std::atan2(localDir.y, localDir.x); + CPASAnimParmData parms(15, CPASAnimParm::FromEnum(s32(cmd->GetSlideType())), + CPASAnimParm::FromReal32(zeus::radToDeg(angle))); + std::pair best = bc.GetPASDatabase().FindBestAnimation(parms, *mgr.GetActiveRandom(), -1); + CAnimPlaybackParms playParms(best.second, -1, 1.f, true); + bc.SetCurrentAnimation(playParms, false, false); + float timeRem = bc.GetAnimTimeRemaining(); + if (timeRem > FLT_EPSILON) + { + const CPASAnimState* slideState = bc.GetPASDatabase().GetAnimState(15); + float animAngle = zeus::degToRad(slideState->GetAnimParmData(best.second, 1).GetReal32Value()); + float delta1 = zeus::CRelAngle(angle - animAngle); + float flippedAngle = (delta1 > M_PIF) ? delta1 - 2.f * M_PIF : delta1; + x4_rotateSpeed = flippedAngle / timeRem; + } + else + { + x4_rotateSpeed = 0.f; + } +} + +pas::EAnimationState CBSSlide::GetBodyStateTransition(float dt, CBodyController& bc) +{ + if (bc.GetCommandMgr().GetCmd(EBodyStateCmd::Hurled)) + return pas::EAnimationState::Hurled; + if (bc.GetCommandMgr().GetCmd(EBodyStateCmd::KnockDown)) + return pas::EAnimationState::Fall; + if (bc.GetCommandMgr().GetCmd(EBodyStateCmd::LoopHitReaction)) + return pas::EAnimationState::LoopReaction; + if (bc.GetCommandMgr().GetCmd(EBodyStateCmd::KnockBack)) + return pas::EAnimationState::KnockBack; + if (bc.IsAnimationOver()) + return pas::EAnimationState::Locomotion; + return pas::EAnimationState::Invalid; +} + +pas::EAnimationState CBSSlide::UpdateBody(float dt, CBodyController& bc, CStateManager& mgr) +{ + pas::EAnimationState st = GetBodyStateTransition(dt, bc); + if (st == pas::EAnimationState::Invalid && x4_rotateSpeed != 0.f) + { + zeus::CQuaternion quat; + quat.rotateZ(x4_rotateSpeed * dt); + bc.SetDeltaRotation(quat); + } + return st; +} + +void CBSTaunt::Start(CBodyController& bc, CStateManager& mgr) +{ + const CBCTauntCmd* cmd = + static_cast(bc.GetCommandMgr().GetCmd(EBodyStateCmd::Taunt)); + CPASAnimParmData parms(16, CPASAnimParm::FromEnum(s32(cmd->GetTauntType()))); + bc.PlayBestAnimation(parms, *mgr.GetActiveRandom()); +} + +pas::EAnimationState CBSTaunt::GetBodyStateTransition(float dt, CBodyController& bc) +{ + if (bc.GetCommandMgr().GetCmd(EBodyStateCmd::Hurled)) + return pas::EAnimationState::Hurled; + if (bc.GetCommandMgr().GetCmd(EBodyStateCmd::KnockDown)) + return pas::EAnimationState::Fall; + if (bc.GetCommandMgr().GetCmd(EBodyStateCmd::LoopHitReaction)) + return pas::EAnimationState::LoopReaction; + if (bc.GetCommandMgr().GetCmd(EBodyStateCmd::KnockBack)) + return pas::EAnimationState::KnockBack; + if (bc.GetCommandMgr().GetCmd(EBodyStateCmd::Locomotion)) + return pas::EAnimationState::Locomotion; + if (bc.IsAnimationOver()) + return pas::EAnimationState::Locomotion; + return pas::EAnimationState::Invalid; +} + +pas::EAnimationState CBSTaunt::UpdateBody(float dt, CBodyController& bc, CStateManager& mgr) +{ + pas::EAnimationState st = GetBodyStateTransition(dt, bc); + if (st == pas::EAnimationState::Invalid && !bc.GetCommandMgr().GetTargetVector().isZero()) + bc.FaceDirection(bc.GetCommandMgr().GetTargetVector(), dt); + return st; +} + +void CBSScripted::Start(CBodyController& bc, CStateManager& mgr) +{ + const CBCScriptedCmd* cmd = + static_cast(bc.GetCommandMgr().GetCmd(EBodyStateCmd::Scripted)); + x4_24_loopAnim = cmd->IsLooped(); + x4_25_timedLoop = cmd->GetUseLoopDuration(); + x8_remTime = cmd->GetLoopDuration(); + CAnimPlaybackParms playParms(cmd->GetAnimId(), -1, 1.f, true); + bc.SetCurrentAnimation(playParms, cmd->IsLooped(), false); +} + +pas::EAnimationState CBSScripted::GetBodyStateTransition(float dt, CBodyController& bc) +{ + if (bc.GetCommandMgr().GetCmd(EBodyStateCmd::Hurled)) + return pas::EAnimationState::Hurled; + if (bc.GetCommandMgr().GetCmd(EBodyStateCmd::KnockDown)) + return pas::EAnimationState::Fall; + if (bc.GetCommandMgr().GetCmd(EBodyStateCmd::LoopHitReaction)) + return pas::EAnimationState::LoopReaction; + if (bc.GetCommandMgr().GetCmd(EBodyStateCmd::KnockBack)) + return pas::EAnimationState::KnockBack; + if (bc.GetCommandMgr().GetCmd(EBodyStateCmd::Scripted)) + return pas::EAnimationState::Scripted; + if (x4_24_loopAnim && bc.GetCommandMgr().GetCmd(EBodyStateCmd::ExitState)) + return pas::EAnimationState::Locomotion; + if (bc.IsAnimationOver()) + return pas::EAnimationState::Locomotion; + return pas::EAnimationState::Invalid; +} + +pas::EAnimationState CBSScripted::UpdateBody(float dt, CBodyController& bc, CStateManager& mgr) +{ + pas::EAnimationState st = GetBodyStateTransition(dt, bc); + if (st == pas::EAnimationState::Invalid) + { + if (!bc.GetCommandMgr().GetTargetVector().isZero()) + bc.FaceDirection(bc.GetCommandMgr().GetTargetVector(), dt); + if (x4_24_loopAnim && x4_25_timedLoop) + { + x8_remTime -= dt; + if (x8_remTime <= 0.f) + return pas::EAnimationState::Locomotion; + } + } + return st; +} + +void CBSCover::Start(CBodyController& bc, CStateManager& mgr) +{ + const CBCCoverCmd* cmd = + static_cast(bc.GetCommandMgr().GetCmd(EBodyStateCmd::Cover)); + x8_coverDirection = cmd->GetDirection(); + x4_state = pas::ECoverState::IntoCover; + CPASAnimParmData parms(19, CPASAnimParm::FromEnum(s32(x4_state)), + CPASAnimParm::FromEnum(s32(x8_coverDirection))); + std::pair best = bc.GetPASDatabase().FindBestAnimation(parms, *mgr.GetActiveRandom(), -1); + zeus::CQuaternion orientDelta = + zeus::CQuaternion::lookAt(zeus::CVector3f::skForward, cmd->GetAlignDirection(), 2.f * M_PIF); + CAnimPlaybackParms playParms(best.second, &orientDelta, &cmd->GetTarget(), &bc.GetOwner().GetTransform(), + &bc.GetOwner().GetModelData()->GetScale(), false); + bc.SetCurrentAnimation(playParms, false, false); + xc_needsExit = false; + if (bc.GetCommandMgr().GetCmd(EBodyStateCmd::ExitState)) + xc_needsExit = true; +} + +pas::EAnimationState CBSCover::GetBodyStateTransition(float dt, CBodyController& bc) +{ + if (bc.GetCommandMgr().GetCmd(EBodyStateCmd::Hurled)) + return pas::EAnimationState::Hurled; + if (bc.GetCommandMgr().GetCmd(EBodyStateCmd::KnockDown)) + return pas::EAnimationState::Fall; + if (bc.GetCommandMgr().GetCmd(EBodyStateCmd::LoopHitReaction)) + return pas::EAnimationState::LoopReaction; + if (bc.GetCommandMgr().GetCmd(EBodyStateCmd::KnockBack)) + return pas::EAnimationState::KnockBack; + if (bc.GetCommandMgr().GetCmd(EBodyStateCmd::Locomotion)) + return pas::EAnimationState::Locomotion; + return pas::EAnimationState::Invalid; +} + +pas::EAnimationState CBSCover::UpdateBody(float dt, CBodyController& bc, CStateManager& mgr) +{ + pas::EAnimationState st = GetBodyStateTransition(dt, bc); + if (st == pas::EAnimationState::Invalid) + { + switch (x4_state) + { + case pas::ECoverState::Lean: + case pas::ECoverState::IntoCover: + if (bc.IsAnimationOver()) + { + x4_state = pas::ECoverState::Cover; + CPASAnimParmData parms(19, CPASAnimParm::FromEnum(s32(x4_state)), + CPASAnimParm::FromEnum(s32(x8_coverDirection))); + bc.LoopBestAnimation(parms, *mgr.GetActiveRandom()); + } + if (bc.GetCommandMgr().GetCmd(EBodyStateCmd::ExitState)) + xc_needsExit = true; + break; + case pas::ECoverState::Cover: + if (!bc.GetCommandMgr().GetTargetVector().isZero()) + bc.FaceDirection(bc.GetCommandMgr().GetTargetVector(), dt); + if (bc.GetCommandMgr().GetCmd(EBodyStateCmd::ExitState) || xc_needsExit) + { + xc_needsExit = false; + x4_state = pas::ECoverState::OutOfCover; + CPASAnimParmData parms(19, CPASAnimParm::FromEnum(s32(x4_state)), + CPASAnimParm::FromEnum(s32(x8_coverDirection))); + bc.PlayBestAnimation(parms, *mgr.GetActiveRandom()); + } + else if (bc.GetCommandMgr().GetCmd(EBodyStateCmd::LeanFromCover)) + { + x4_state = pas::ECoverState::Lean; + CPASAnimParmData parms(19, CPASAnimParm::FromEnum(s32(x4_state)), + CPASAnimParm::FromEnum(s32(x8_coverDirection))); + bc.PlayBestAnimation(parms, *mgr.GetActiveRandom()); + } + break; + case pas::ECoverState::OutOfCover: + if (bc.IsAnimationOver()) + { + x4_state = pas::ECoverState::Invalid; + return pas::EAnimationState::Locomotion; + } + break; + default: break; + } + } + return st; +} + +void CBSWallHang::Start(CBodyController& bc, CStateManager& mgr) +{ + const CBCWallHangCmd* cmd = + static_cast(bc.GetCommandMgr().GetCmd(EBodyStateCmd::WallHang)); + x4_state = pas::EWallHangState::IntoJump; + x8_wpId = cmd->GetTarget(); + x18_25_needsExit = false; + CPASAnimParmData parms(20, CPASAnimParm::FromEnum(s32(x4_state))); + bc.PlayBestAnimation(parms, *mgr.GetActiveRandom()); +} + +pas::EAnimationState CBSWallHang::GetBodyStateTransition(float dt, CBodyController& bc) +{ + if (bc.GetCommandMgr().GetCmd(EBodyStateCmd::Hurled)) + return pas::EAnimationState::Hurled; + if (bc.GetCommandMgr().GetCmd(EBodyStateCmd::KnockDown)) + return pas::EAnimationState::Fall; + return pas::EAnimationState::Invalid; +} + +void CBSWallHang::FixInPlace(CBodyController& bc) +{ + if (TCastToPtr ai = bc.GetOwner()) + { + ai->SetConstantForce(zeus::CVector3f::skZero); + ai->SetVelocityWR(zeus::CVector3f::skZero); + } +} + +bool CBSWallHang::CheckForLand(CBodyController& bc, CStateManager& mgr) +{ + if (TCastToPtr ai = bc.GetOwner()) + { + if (ai->GetX328_26() || ai->IsOnGround()) + { + x4_state = pas::EWallHangState::DetachOutOfJump; + CPASAnimParmData parms(20, CPASAnimParm::FromEnum(s32(x4_state))); + bc.PlayBestAnimation(parms, *mgr.GetActiveRandom()); + mgr.SendScriptMsg(ai.GetPtr(), kInvalidUniqueId, EScriptObjectMessage::OnFloor); + return true; + } + } + return false; +} + +bool CBSWallHang::CheckForWall(CBodyController& bc, CStateManager& mgr) +{ + if (TCastToPtr ai = bc.GetOwner()) + { + float magSq = 10.f; + TCastToPtr wp = mgr.ObjectById(x8_wpId); + if (wp) + magSq = (wp->GetTranslation() - ai->GetTranslation()).magSquared(); + + if (magSq < 1.f || ai->GetX328_26()) + { + x4_state = pas::EWallHangState::IntoWallHang; + CPASAnimParmData parms(20, CPASAnimParm::FromEnum(s32(x4_state))); + std::pair best = bc.GetPASDatabase().FindBestAnimation(parms, *mgr.GetActiveRandom(), -1); + const zeus::CVector3f& target = wp ? wp->GetTranslation() : ai->GetTranslation(); + CAnimPlaybackParms playParms(best.second, nullptr, &target, &bc.GetOwner().GetTransform(), + &bc.GetOwner().GetModelData()->GetScale(), false); + bc.SetCurrentAnimation(playParms, false, false); + ai->SetVelocityWR(zeus::CVector3f::skZero); + ai->SetMomentumWR(zeus::CVector3f::skZero); + mgr.SendScriptMsg(ai.GetPtr(), kInvalidUniqueId, EScriptObjectMessage::OnFloor); + return true; + } + } + return false; +} + +void CBSWallHang::SetLaunchVelocity(CBodyController& bc) +{ + if (!x18_24_launched) + { + if (TCastToPtr act = bc.GetOwner()) + { + act->SetVelocityWR(xc_launchVel); + act->SetConstantForce(xc_launchVel * act->GetMass()); + } + x18_24_launched = true; + } +} + +pas::EAnimationState CBSWallHang::UpdateBody(float dt, CBodyController& bc, CStateManager& mgr) +{ + pas::EAnimationState st = GetBodyStateTransition(dt, bc); + if (st == pas::EAnimationState::Invalid) + { + switch (x4_state) + { + case pas::EWallHangState::IntoJump: + { + CPASAnimParmData parms(20, CPASAnimParm::FromEnum(1)); + std::pair best = bc.GetPASDatabase().FindBestAnimation(parms, *mgr.GetActiveRandom(), -1); + if (best.first > 0.f) + { + x4_state = pas::EWallHangState::JumpArc; + CAnimPlaybackParms playParms(best.second, -1, 1.f, true); + bc.SetCurrentAnimation(playParms, false, false); + } + else + { + x4_state = pas::EWallHangState::JumpAirLoop; + CPASAnimParmData parms(20, CPASAnimParm::FromEnum(s32(x4_state))); + bc.LoopBestAnimation(parms, *mgr.GetActiveRandom()); + } + if (TCastToPtr act = bc.GetOwner()) + { + mgr.SendScriptMsg(act.GetPtr(), kInvalidUniqueId, EScriptObjectMessage::InternalMessage18); + if (TCastToPtr wp = mgr.ObjectById(x8_wpId)) + { + zeus::CVector3f toWp = wp->GetTranslation() - act->GetTranslation(); + if (toWp.z > 0.f) + { + float tmp = -act->GetMomentum().z / act->GetMass(); + float tmp2 = std::sqrt(tmp * 2.f * toWp.z); + tmp = 1.f / (tmp2 / tmp); + xc_launchVel = zeus::CVector3f(tmp * toWp.x, tmp * toWp.y, tmp2); + x18_24_launched = false; + } + } + } + break; + } + case pas::EWallHangState::JumpArc: + { + SetLaunchVelocity(bc); + if (bc.IsAnimationOver()) + { + x4_state = pas::EWallHangState::JumpAirLoop; + CPASAnimParmData parms(20, CPASAnimParm::FromEnum(s32(x4_state))); + bc.LoopBestAnimation(parms, *mgr.GetActiveRandom()); + } + else + { + CheckForWall(bc, mgr); + } + break; + } + case pas::EWallHangState::JumpAirLoop: + { + SetLaunchVelocity(bc); + if (!CheckForWall(bc, mgr)) + CheckForLand(bc, mgr); + break; + } + case pas::EWallHangState::IntoWallHang: + { + if (bc.IsAnimationOver()) + { + x4_state = pas::EWallHangState::WallHang; + CPASAnimParmData parms(20, CPASAnimParm::FromEnum(s32(x4_state))); + bc.LoopBestAnimation(parms, *mgr.GetActiveRandom()); + } + else if (bc.GetCommandMgr().GetCmd(EBodyStateCmd::ExitState)) + { + x18_25_needsExit = true; + } + break; + } + case pas::EWallHangState::WallHang: + { + if (!bc.GetCommandMgr().GetTargetVector().isZero()) + { + if (TCastToPtr wp = mgr.ObjectById(x8_wpId)) + { + if (TCastToPtr act = bc.GetOwner()) + { + zeus::CVector3f lookDir = bc.GetCommandMgr().GetTargetVector().normalized(); + float actorDotWp = act->GetTransform().basis[1].dot(wp->GetTransform().basis[1]); + float lookDotWp = lookDir.dot(wp->GetTransform().basis[1]); + if (actorDotWp < -0.5f || lookDotWp > 0.5f) + bc.FaceDirection(lookDir, dt); + } + } + } + if (bc.GetCommandMgr().GetCmd(EBodyStateCmd::ExitState) || x18_25_needsExit) + { + x4_state = pas::EWallHangState::OutOfWallHang; + CPASAnimParmData parms(20, CPASAnimParm::FromEnum(s32(x4_state))); + bc.PlayBestAnimation(parms, *mgr.GetActiveRandom()); + } + FixInPlace(bc); + break; + } + case pas::EWallHangState::Five: + { + if (bc.IsAnimationOver()) + { + x4_state = pas::EWallHangState::WallHang; + CPASAnimParmData parms(20, CPASAnimParm::FromEnum(s32(x4_state))); + bc.LoopBestAnimation(parms, *mgr.GetActiveRandom()); + } + FixInPlace(bc); + break; + } + case pas::EWallHangState::OutOfWallHang: + { + if (bc.IsAnimationOver()) + { + CPASAnimParmData parms(20, CPASAnimParm::FromEnum(7)); + std::pair best = bc.GetPASDatabase().FindBestAnimation(parms, *mgr.GetActiveRandom(), -1); + if (best.first > 0.f) + { + x4_state = pas::EWallHangState::OutOfWallHangTurn; + CAnimPlaybackParms playParms(best.second, -1, 1.f, true); + bc.SetCurrentAnimation(playParms, false, false); + } + else + { + x4_state = pas::EWallHangState::DetachJumpLoop; + CPASAnimParmData parms(20, CPASAnimParm::FromEnum(s32(x4_state))); + bc.LoopBestAnimation(parms, *mgr.GetActiveRandom()); + } + if (TCastToPtr act = bc.GetOwner()) + { + mgr.SendScriptMsg(act.GetPtr(), kInvalidUniqueId, EScriptObjectMessage::InternalMessage18); + x18_24_launched = false; + if (TCastToPtr wp = mgr.ObjectById(x8_wpId)) + { + xc_launchVel = 15.f * wp->GetTransform().basis[1]; + xc_launchVel.z = 5.f; + } + else + { + xc_launchVel = -15.f * act->GetTransform().basis[1]; + } + act->SetAngularMomentum(zeus::CAxisAngle::sIdentity); + } + } + break; + } + case pas::EWallHangState::OutOfWallHangTurn: + { + SetLaunchVelocity(bc); + if (bc.IsAnimationOver()) + { + x4_state = pas::EWallHangState::DetachJumpLoop; + CPASAnimParmData parms(20, CPASAnimParm::FromEnum(s32(x4_state))); + bc.LoopBestAnimation(parms, *mgr.GetActiveRandom()); + } + else + { + CheckForLand(bc, mgr); + } + break; + } + case pas::EWallHangState::DetachJumpLoop: + { + SetLaunchVelocity(bc); + CheckForLand(bc, mgr); + break; + } + case pas::EWallHangState::DetachOutOfJump: + { + if (bc.IsAnimationOver()) + { + x4_state = pas::EWallHangState::Invalid; + return pas::EAnimationState::Locomotion; + } + break; + } + default: break; + } + } + return st; +} + +bool CBSWallHang::IsInAir(const CBodyController& bc) const +{ + switch (x4_state) + { + case pas::EWallHangState::JumpArc: + case pas::EWallHangState::JumpAirLoop: + case pas::EWallHangState::OutOfWallHangTurn: + case pas::EWallHangState::DetachJumpLoop: + return true; + default: + return false; + } +} + +bool CBSWallHang::ApplyAnimationDeltas() const +{ + switch (x4_state) + { + case pas::EWallHangState::IntoJump: + case pas::EWallHangState::IntoWallHang: + case pas::EWallHangState::WallHang: + case pas::EWallHangState::Five: + case pas::EWallHangState::OutOfWallHang: + case pas::EWallHangState::DetachOutOfJump: + return true; + default: + return false; + } +} + +bool CBSWallHang::ApplyHeadTracking() const +{ + switch (x4_state) + { + case pas::EWallHangState::WallHang: + case pas::EWallHangState::Five: + return true; + default: + return false; + } +} + +bool CBSWallHang::ApplyGravity() const +{ + switch (x4_state) + { + case pas::EWallHangState::WallHang: + case pas::EWallHangState::IntoWallHang: + case pas::EWallHangState::OutOfWallHang: + return false; + default: + return true; + } +} + +float CBSLocomotion::GetStartVelocityMagnitude(CBodyController& bc) +{ + if (TCastToPtr act = bc.GetOwner()) + { + float maxSpeed = bc.GetBodyStateInfo().GetMaxSpeed(); + float ret = 0.f; + if (maxSpeed > 0.f) + ret = act->GetVelocity().magnitude() / maxSpeed; + return std::min(1.f, ret); + } + return 0.f; +} + +void CBSLocomotion::ReStartBodyState(CBodyController& bc, bool b) +{ + UpdateLocomotionAnimation(0.f, b ? GetStartVelocityMagnitude(bc) : 0.f, bc, true); +} + +float CBSLocomotion::ComputeWeightPercentage(const std::pair& a, + const std::pair& b, float f) const +{ + float range = b.second - a.second; + if (range > FLT_EPSILON) + return std::max(0.f, std::min(1.f, (f - a.second) / range)); + return 0.f; +} + +void CBSLocomotion::Start(CBodyController& bc, CStateManager& mgr) +{ + x4_locomotionType = bc.GetLocomotionType(); + if (bc.GetCommandMgr().GetCmd(EBodyStateCmd::Thirteen)) + ReStartBodyState(bc, true); + else + ReStartBodyState(bc, false); +} + +pas::EAnimationState CBSLocomotion::UpdateBody(float dt, CBodyController& bc, CStateManager& mgr) +{ + pas::EAnimationState st = GetBodyStateTransition(dt, bc); + if (st == pas::EAnimationState::Invalid) + UpdateLocomotionAnimation(dt, ApplyLocomotionPhysics(dt, bc), bc, false); + return st; +} + +void CBSLocomotion::Shutdown(CBodyController& bc) +{ + bc.MultiplyPlaybackRate(1.f); +} + +float CBSLocomotion::ApplyLocomotionPhysics(float dt, CBodyController& bc) +{ + if (TCastToPtr act = bc.GetOwner()) + { + zeus::CVector3f vec = + (zeus::close_enough(bc.GetCommandMgr().GetFaceVector(), zeus::CVector3f::skZero, 0.0001f)) ? + bc.GetCommandMgr().GetMoveVector() : bc.GetCommandMgr().GetFaceVector(); + if (vec.canBeNormalized()) + { + if (IsPitchable()) + { + zeus::CVector3f tmp = vec; + tmp.z = 0.f; + zeus::CVector3f lookVec = act->GetTransform().basis[1]; + lookVec.z = 0.f; + lookVec.normalize(); + bc.FaceDirection3D(tmp, lookVec, dt); + zeus::CVector3f lookVec2 = act->GetTransform().basis[1]; + lookVec2.z = vec.z; + lookVec2.normalize(); + if (!zeus::close_enough(lookVec, lookVec2, 0.0001f)) + { + zeus::CRelAngle pitchAngle = std::min(bc.GetBodyStateInfo().GetMaximumPitch(), + zeus::CVector3f::getAngleDiff(vec, tmp)); + lookVec2 = zeus::CVector3f::slerp(lookVec, lookVec2, pitchAngle); + } + bc.FaceDirection3D(lookVec2, act->GetTransform().basis[1], dt); + zeus::CVector3f lookVec3 = act->GetTransform().basis[1]; + lookVec3.z = 0.f; + bc.FaceDirection3D(lookVec3, act->GetTransform().basis[1], dt); + } + else + { + bc.FaceDirection(vec.normalized(), dt); + } + } + return std::min(1.f, bc.GetCommandMgr().GetMoveVector().magnitude()); + } + return 0.f; +} + +pas::EAnimationState CBSLocomotion::GetBodyStateTransition(float, CBodyController& bc) +{ + if (bc.GetCommandMgr().GetCmd(EBodyStateCmd::Hurled)) + return pas::EAnimationState::Hurled; + if (bc.GetCommandMgr().GetCmd(EBodyStateCmd::KnockDown)) + return pas::EAnimationState::Fall; + if (bc.GetCommandMgr().GetCmd(EBodyStateCmd::LoopHitReaction)) + return pas::EAnimationState::LoopReaction; + if (bc.GetCommandMgr().GetCmd(EBodyStateCmd::KnockBack)) + return pas::EAnimationState::KnockBack; + if (bc.GetCommandMgr().GetCmd(EBodyStateCmd::Locomotion)) + { + bc.GetCommandMgr().ClearLocomotionCmds(); + return pas::EAnimationState::Invalid; + } + if (bc.GetCommandMgr().GetCmd(EBodyStateCmd::Slide)) + return pas::EAnimationState::Slide; + if (bc.GetCommandMgr().GetCmd(EBodyStateCmd::Generate)) + return pas::EAnimationState::Generate; + if (bc.GetCommandMgr().GetCmd(EBodyStateCmd::MeleeAttack)) + return pas::EAnimationState::MeleeAttack; + if (bc.GetCommandMgr().GetCmd(EBodyStateCmd::ProjectileAttack)) + return pas::EAnimationState::ProjectileAttack; + if (bc.GetCommandMgr().GetCmd(EBodyStateCmd::LoopAttack)) + return pas::EAnimationState::LoopAttack; + if (bc.GetCommandMgr().GetCmd(EBodyStateCmd::LoopReaction)) + return pas::EAnimationState::LoopReaction; + if (bc.GetCommandMgr().GetCmd(EBodyStateCmd::Jump)) + return pas::EAnimationState::Jump; + if (bc.GetCommandMgr().GetCmd(EBodyStateCmd::Taunt)) + return pas::EAnimationState::Taunt; + if (bc.GetCommandMgr().GetCmd(EBodyStateCmd::Step)) + return pas::EAnimationState::Step; + if (bc.GetCommandMgr().GetCmd(EBodyStateCmd::Cover)) + return pas::EAnimationState::Cover; + if (bc.GetCommandMgr().GetCmd(EBodyStateCmd::WallHang)) + return pas::EAnimationState::WallHang; + if (bc.GetCommandMgr().GetCmd(EBodyStateCmd::Scripted)) + return pas::EAnimationState::Scripted; + if (bc.GetCommandMgr().GetMoveVector().isZero()) + { + if (!bc.GetCommandMgr().GetFaceVector().isZero()) + if (!IsMoving()) + return pas::EAnimationState::Turn; + } + if (x4_locomotionType != bc.GetLocomotionType()) + return pas::EAnimationState::Locomotion; + return pas::EAnimationState::Invalid; +} + +CBSBiPedLocomotion::CBSBiPedLocomotion(CActor& actor) +{ + const CPASDatabase& pasDatabase = + actor.GetModelData()->GetAnimationData()->GetCharacterInfo().GetPASDatabase(); + for (int i=0 ; i<14 ; ++i) + { + x8_anims.emplace_back(); + rstl::reserved_vector, 8>& innerVec = x8_anims.back(); + for (int j=0 ; j<8 ; ++j) + { + CPASAnimParmData parms(5, CPASAnimParm::FromEnum(j), CPASAnimParm::FromEnum(i)); + std::pair best = pasDatabase.FindBestAnimation(parms, -1); + float avgVel = 0.f; + if (best.second != -1) + { + avgVel = actor.GetAverageAnimVelocity(best.second); + if (j == 0) + avgVel = 0.f; + } + innerVec.push_back({best.second, avgVel}); + } + } +} + +void CBSBiPedLocomotion::Start(CBodyController& bc, CStateManager& mgr) +{ + x3c8_primeTime = 0.f; + CBSLocomotion::Start(bc, mgr); +} + +pas::EAnimationState CBSBiPedLocomotion::UpdateBody(float dt, CBodyController& bc, CStateManager& mgr) +{ + if (x3c8_primeTime < 0.2f) + x3c8_primeTime += dt; + return CBSLocomotion::UpdateBody(dt, bc, mgr); +} + +float CBSBiPedLocomotion::GetLocomotionSpeed(pas::ELocomotionType type, pas::ELocomotionAnim anim) const +{ + return GetLocoAnimation(type, anim).second; +} + +float CBSBiPedLocomotion::UpdateRun(float vel, CBodyController& bc, pas::ELocomotionAnim anim) +{ + const std::pair& walk = GetLocoAnimation(x4_locomotionType, pas::ELocomotionAnim::Walk); + const std::pair& run = GetLocoAnimation(x4_locomotionType, pas::ELocomotionAnim::Run); + float perc = ComputeWeightPercentage(walk, run, vel); + + if (perc < 0.4f) + { + float rate = walk.second > 0.f ? vel / walk.second : 1.f; + if (anim != pas::ELocomotionAnim::Walk && bc.GetCurrentAnimId() != walk.first) + { + CAnimPlaybackParms playParms(walk.first, -1, 1.f, true); + bc.SetCurrentAnimation(playParms, true, false); + x3c8_primeTime = 0.f; + } + bc.MultiplyPlaybackRate(rate); + x3c4_anim = pas::ELocomotionAnim::Walk; + return rate; + } + else + { + float rate = std::min(1.f, vel / run.second); + if (anim != pas::ELocomotionAnim::Run && bc.GetCurrentAnimId() != run.first) + { + CAnimPlaybackParms playParms(run.first, -1, 1.f, true); + bc.SetCurrentAnimation(playParms, true, false); + x3c8_primeTime = 0.f; + } + bc.MultiplyPlaybackRate(rate); + x3c4_anim = pas::ELocomotionAnim::Run; + return rate; + } +} + +float CBSBiPedLocomotion::UpdateWalk(float vel, CBodyController& bc, pas::ELocomotionAnim anim) +{ + if (anim != pas::ELocomotionAnim::Walk) + { + const std::pair& walk = GetLocoAnimation(x4_locomotionType, pas::ELocomotionAnim::Walk); + if (bc.GetCurrentAnimId() != walk.first) + { + CAnimPlaybackParms playParms(walk.first, -1, 1.f, true); + bc.SetCurrentAnimation(playParms, true, false); + x3c8_primeTime = 0.f; + } + x3c4_anim = pas::ELocomotionAnim::Walk; + } + + const std::pair& idle = GetLocoAnimation(x4_locomotionType, pas::ELocomotionAnim::Idle); + const std::pair& walk = GetLocoAnimation(x4_locomotionType, pas::ELocomotionAnim::Walk); + float perc = std::max(0.5f, ComputeWeightPercentage(idle, walk, vel)); + bc.MultiplyPlaybackRate(perc); + return perc; +} + +static const pas::ELocomotionAnim Strafes[] = +{ + pas::ELocomotionAnim::StrafeRight, + pas::ELocomotionAnim::StrafeLeft, + pas::ELocomotionAnim::Walk, + pas::ELocomotionAnim::BackUp, + pas::ELocomotionAnim::StrafeUp, + pas::ELocomotionAnim::StrafeDown +}; + +float CBSBiPedLocomotion::UpdateStrafe(float vel, CBodyController& bc, pas::ELocomotionAnim anim) +{ + if (TCastToPtr act = bc.GetOwner()) + { + zeus::CVector3f localVec = act->GetTransform().transposeRotate(bc.GetCommandMgr().GetMoveVector()); + zeus::CVector3f localVecSq = localVec * localVec; + int maxComp = 0; + for (int i=0 ; i<3 ; ++i) + if (localVecSq[i] >= localVecSq[maxComp]) + maxComp = i; + int strafeKey = maxComp * 2 + localVec[maxComp] > 0.f ? 0 : 1; + pas::ELocomotionAnim strafeType = Strafes[strafeKey]; + float rate = vel * GetLocomotionSpeed(x4_locomotionType, strafeType); + if (anim != strafeType) + { + const std::pair& strafe = + GetLocoAnimation(x4_locomotionType, strafeType); + if (bc.GetCurrentAnimId() != strafe.first) + { + CAnimPlaybackParms playParms(strafe.first, -1, 1.f, true); + bc.SetCurrentAnimation(playParms, true, false); + x3c8_primeTime = 0.f; + } + x3c4_anim = strafeType; + } + const std::pair& idle = GetLocoAnimation(x4_locomotionType, pas::ELocomotionAnim::Idle); + const std::pair& strafe = GetLocoAnimation(x4_locomotionType, strafeType); + float perc = std::max(0.5f, ComputeWeightPercentage(idle, strafe, rate)); + bc.MultiplyPlaybackRate(perc); + } + return 1.f; +} + +float CBSBiPedLocomotion::UpdateLocomotionAnimation(float dt, float velMag, + CBodyController& bc, bool init) +{ + float ret = 1.f; + if (init || x3c8_primeTime >= 0.2f) + { + pas::ELocomotionAnim anim = init ? pas::ELocomotionAnim::Invalid : x3c4_anim; + float maxSpeed = velMag * GetLocomotionSpeed(x4_locomotionType, pas::ELocomotionAnim::Run); + if (IsStrafing(bc) && velMag >= 0.01f) + return UpdateStrafe(velMag, bc, anim); + + if (maxSpeed < 0.01f) + { + if (anim != pas::ELocomotionAnim::Idle || init) + { + if (!bc.GetBodyStateInfo().GetLocoAnimChangeAtEndOfAnimOnly() || + bc.GetAnimTimeRemaining() <= dt || init) + { + const std::pair& best = + GetLocoAnimation(x4_locomotionType, pas::ELocomotionAnim::Idle); + if (bc.GetCurrentAnimId() != best.first) + { + CAnimPlaybackParms playParms(best.first, -1, 1.f, true); + bc.SetCurrentAnimation(playParms, true, false); + x3c8_primeTime = 0.f; + } + x3c4_anim = pas::ELocomotionAnim::Idle; + } + } + } + else + { + const std::pair& best = + GetLocoAnimation(x4_locomotionType, pas::ELocomotionAnim::Walk); + if (maxSpeed < best.second) + return UpdateWalk(maxSpeed, bc, anim); + return UpdateRun(maxSpeed, bc, anim); + } + } + return ret; +} + +bool CBSBiPedLocomotion::IsStrafing(const CBodyController& bc) const +{ + if (!zeus::close_enough(bc.GetCommandMgr().GetMoveVector(), zeus::CVector3f::skZero, 0.0001f)) + if (!zeus::close_enough(bc.GetCommandMgr().GetFaceVector(), zeus::CVector3f::skZero, 0.0001f)) + return true; + return false; +} + +CBSFlyerLocomotion::CBSFlyerLocomotion(CActor& actor, bool pitchable) +: CBSBiPedLocomotion(actor), x3cc_pitchable(pitchable) +{} + +float CBSFlyerLocomotion::ApplyLocomotionPhysics(float dt, CBodyController& bc) +{ + float ret = CBSLocomotion::ApplyLocomotionPhysics(dt, bc); + if (TCastToPtr act = bc.GetOwner()) + { + if (std::fabs(bc.GetCommandMgr().GetMoveVector().z) > 0.01f && + (!x3cc_pitchable || bc.GetBodyStateInfo().GetMaximumPitch() < 0.17453292f)) + { + zeus::CVector3f dir; + dir.z = dt * bc.GetBodyStateInfo().GetMaxSpeed() * bc.GetCommandMgr().GetMoveVector().z; + act->ApplyImpulseWR(act->GetMoveToORImpulseWR(dir, dt), zeus::CAxisAngle::sIdentity); + } + } + return ret; +} + +CBSWallWalkerLocomotion::CBSWallWalkerLocomotion(CActor& actor) +: CBSBiPedLocomotion(actor) +{} + +float CBSWallWalkerLocomotion::ApplyLocomotionPhysics(float dt, CBodyController& bc) +{ + if (TCastToPtr act = bc.GetOwner()) + { + float maxSpeed = bc.GetBodyStateInfo().GetMaxSpeed(); + zeus::CVector3f x40 = bc.GetCommandMgr().GetMoveVector() * maxSpeed; + if ((zeus::CVector3f::getAngleDiff(bc.GetCommandMgr().GetFaceVector(), x40) < (M_PIF / 2.f) ? + x40 : bc.GetCommandMgr().GetFaceVector()).canBeNormalized()) + bc.FaceDirection3D(x40.normalized(), act->GetTransform().basis[1], dt); + zeus::CVector3f impulse = + act->GetMoveToORImpulseWR(act->GetTransform().transposeRotate(x40 * dt), dt); + impulse = act->GetMass() > FLT_EPSILON ? impulse / act->GetMass() : + zeus::CVector3f(0.f, act->GetVelocity().magnitude(), 0.f); + if (maxSpeed > FLT_EPSILON) + return std::min(1.f, impulse.magnitude() / maxSpeed); + } + return 0.f; +} + +CBSNewFlyerLocomotion::CBSNewFlyerLocomotion(CActor& actor) +: CBSBiPedLocomotion(actor) +{} + +float CBSNewFlyerLocomotion::ApplyLocomotionPhysics(float dt, CBodyController& bc) +{ + if (TCastToPtr(bc.GetOwner())) + bc.FaceDirection(bc.GetCommandMgr().GetFaceVector(), dt); + return 0.f; +} + +static const pas::ELocomotionAnim RunStrafes[] = +{ + pas::ELocomotionAnim::StrafeRight, + pas::ELocomotionAnim::StrafeLeft, + pas::ELocomotionAnim::Run, + pas::ELocomotionAnim::BackUp, + pas::ELocomotionAnim::StrafeUp, + pas::ELocomotionAnim::StrafeDown +}; + +float CBSNewFlyerLocomotion::UpdateLocomotionAnimation(float dt, float velMag, + CBodyController& bc, bool init) +{ + if (TCastToPtr act = bc.GetOwner()) + { + pas::ELocomotionAnim strafeType = pas::ELocomotionAnim::Idle; + if (bc.GetCommandMgr().GetMoveVector().canBeNormalized()) + { + zeus::CVector3f localVec = act->GetTransform().transposeRotate(bc.GetCommandMgr().GetMoveVector()); + zeus::CVector3f localVecSq = localVec * localVec; + int maxComp = 0; + for (int i=0 ; i<3 ; ++i) + if (localVecSq[i] >= localVecSq[maxComp]) + maxComp = i; + int strafeKey = maxComp * 2 + localVec[maxComp] > 0.f ? 0 : 1; + strafeType = RunStrafes[strafeKey]; + } + + if (init || strafeType != x3c4_anim) + { + const std::pair& strafe = GetLocoAnimation(x4_locomotionType, strafeType); + if (init || bc.GetCurrentAnimId() != strafe.first) + { + CAnimPlaybackParms playParms(strafe.first, -1, 1.f, true); + bc.SetCurrentAnimation(playParms, true, false); + } + x3c4_anim = strafeType; + } + } + return 1.f; +} + +CBSRestrictedLocomotion::CBSRestrictedLocomotion(CActor& actor) +{ + const CPASDatabase& pasDatabase = + actor.GetModelData()->GetAnimationData()->GetCharacterInfo().GetPASDatabase(); + for (int i=0 ; i<14 ; ++i) + { + CPASAnimParmData parms(5, CPASAnimParm::FromEnum(0), CPASAnimParm::FromEnum(i)); + std::pair best = pasDatabase.FindBestAnimation(parms, -1); + x8_anims.push_back(best.second); + } +} + +float CBSRestrictedLocomotion::UpdateLocomotionAnimation(float dt, float velMag, + CBodyController& bc, bool init) +{ + pas::ELocomotionAnim anim = init ? pas::ELocomotionAnim::Invalid : x44_anim; + if (anim != pas::ELocomotionAnim::Idle) + { + s32 newAnim = x8_anims[int(x4_locomotionType)]; + if (newAnim != bc.GetCurrentAnimId()) + { + CAnimPlaybackParms playParms(newAnim, -1, 1.f, true); + bc.SetCurrentAnimation(playParms, true, false); + } + x44_anim = pas::ELocomotionAnim::Idle; + } + return 1.f; +} + +CBSRestrictedFlyerLocomotion::CBSRestrictedFlyerLocomotion(CActor& actor) +: CBSRestrictedLocomotion(actor) +{} + +float CBSRestrictedFlyerLocomotion::ApplyLocomotionPhysics(float dt, CBodyController& bc) +{ + if (TCastToPtr act = bc.GetOwner()) + { + bc.FaceDirection(bc.GetCommandMgr().GetFaceVector(), dt); + act->ApplyImpulseWR(bc.GetCommandMgr().GetMoveVector() * + bc.GetRestrictedFlyerMoveSpeed() * act->GetMass(), + zeus::CAxisAngle::sIdentity); + } + return 0.f; +} + +} diff --git a/Runtime/Character/CBodyState.hpp b/Runtime/Character/CBodyState.hpp index 90727bf7b..1df3a49e8 100644 --- a/Runtime/Character/CBodyState.hpp +++ b/Runtime/Character/CBodyState.hpp @@ -1,128 +1,431 @@ #ifndef CBODYSTATE_HPP #define CBODYSTATE_HPP + #include "RetroTypes.hpp" #include "CharacterCommon.hpp" +#include "CBodyStateCmdMgr.hpp" namespace urde { class CBodyController; class CStateManager; +class CActor; class CBodyState { public: virtual bool IsInAir(const CBodyController&) const { return false; } virtual bool IsDead() const { return false; } + virtual bool IsDying() const { return false; } virtual bool IsMoving() const { return false; } virtual bool ApplyGravity() const { return true; } virtual bool ApplyHeadTracking() const { return true; } virtual bool ApplyAnimationDeltas() const { return true; } virtual bool CanShoot() const { return false; } virtual void Start(CBodyController&, CStateManager&) = 0; - virtual void UpdateBody(float, CBodyController&, CStateManager&) = 0; + virtual pas::EAnimationState UpdateBody(float, CBodyController&, CStateManager&) = 0; virtual void Shutdown(CBodyController&)=0; }; class CBSAttack : public CBodyState { + pas::EAnimationState x4_nextState = pas::EAnimationState::Invalid; + CBCSlideCmd x8_slide; + zeus::CVector3f x20_targetPos; + float x2c_alignTargetPosStartTime = -1.f; + float x30_alignTargetPosTime = -1.f; + float x34_curTime = 0.f; + pas::EAnimationState GetBodyStateTransition(float dt, CBodyController& bc); + void UpdatePhysicsActor(CBodyController& bc, float dt); public: - virtual bool CanShoot() const { return false; } - virtual void Start(CBodyController &, CStateManager &) {} - virtual void UpdateBody(float, CBodyController &, CStateManager&); - virtual void Shutdown(CBodyController&) {} + bool CanShoot() const { return false; } + void Start(CBodyController& bc, CStateManager & mgr); + pas::EAnimationState UpdateBody(float dt, CBodyController& bc, CStateManager& mgr); + void Shutdown(CBodyController&) {} +}; + +class CBSProjectileAttack : public CBodyState +{ + pas::EAnimationState GetBodyStateTransition(float dt, CBodyController& bc); +public: + bool CanShoot() const { return true; } + void Start(CBodyController& bc, CStateManager& mgr); + pas::EAnimationState UpdateBody(float dt, CBodyController& bc, CStateManager& mgr); + void Shutdown(CBodyController&) {} }; class CBSDie : public CBodyState { + float x4_remTime = 0.f; bool x8_isDead = false; public: bool IsDead() const { return x8_isDead; } - void Start(CBodyController &, CStateManager &) {} - void UpdateBody(float, CBodyController &, CStateManager&) {} - void Shutdown(CBodyController &) {} + bool IsDying() const { return true; } + void Start(CBodyController& bc, CStateManager& mgr); + pas::EAnimationState UpdateBody(float dt, CBodyController& bc, CStateManager& mgr); + void Shutdown(CBodyController&) {} }; class CBSFall : public CBodyState { + float x4_rotateSpeed = 0.f; + float x8_remTime = 0.f; + pas::EFallState xc_fallState = pas::EFallState::Invalid; + pas::EAnimationState GetBodyStateTransition(float dt, CBodyController& bc); public: - void Start(CBodyController &, CStateManager &) {} - void UpdateBody(float, CBodyController &, CStateManager&) {} - void Shutdown(CBodyController &) {} + void Start(CBodyController& bc, CStateManager& mgr); + pas::EAnimationState UpdateBody(float dt, CBodyController& bc, CStateManager& mgr); + void Shutdown(CBodyController& bc); }; class CBSGetup : public CBodyState { + pas::EFallState x4_fallState = pas::EFallState::Invalid; + pas::EAnimationState GetBodyStateTransition(float dt, CBodyController& bc); public: - void Start(CBodyController &, CStateManager &) {} - void UpdateBody(float, CBodyController &, CStateManager&) {} - void Shutdown(CBodyController &) {} + void Start(CBodyController& bc, CStateManager& mgr); + pas::EAnimationState UpdateBody(float dt, CBodyController& bc, CStateManager& mgr); + void Shutdown(CBodyController& bc); }; class CBSKnockBack : public CBodyState { + float x4_curTime = 0.f; + float x8_rotateSpeed = 0.f; + float xc_remTime = 0.f; + pas::EAnimationState GetBodyStateTransition(float dt, CBodyController& bc); public: bool IsMoving() const { return true; } - void Start(CBodyController &, CStateManager &) {} - void UpdateBody(float, CBodyController &, CStateManager&) {} - void Shutdown(CBodyController &) {} + void Start(CBodyController& bc, CStateManager& mgr); + pas::EAnimationState UpdateBody(float dt, CBodyController& bc, CStateManager& mgr); + void Shutdown(CBodyController&) {} }; class CBSLieOnGround : public CBodyState { + bool x4_24_hasGroundHit : 1; + pas::EAnimationState GetBodyStateTransition(float dt, CBodyController& bc); public: - void Start(CBodyController &, CStateManager &) {} - void UpdateBody(float, CBodyController &, CStateManager&) {} - void Shutdown(CBodyController &) {} + CBSLieOnGround(CActor& actor); + void Start(CBodyController& bc, CStateManager& mgr); + pas::EAnimationState UpdateBody(float dt, CBodyController& bc, CStateManager& mgr); + void Shutdown(CBodyController& bc); }; class CBSStep : public CBodyState { + pas::EAnimationState GetBodyStateTransition(float dt, CBodyController& bc); public: bool IsMoving() const { return true; } bool CanShoot() const { return true; } - void Start(CBodyController &, CStateManager &) {} - void UpdateBody(float, CBodyController &, CStateManager&) {} - void Shutdown(CBodyController &) {} + void Start(CBodyController& bc, CStateManager& mgr); + pas::EAnimationState UpdateBody(float dt, CBodyController& bc, CStateManager& mgr); + void Shutdown(CBodyController&) {} }; class CBSTurn : public CBodyState { +protected: + float x4_rotateSpeed = 0.f; + zeus::CVector2f x8_dest; + pas::ETurnDirection x10_turnDir = pas::ETurnDirection::Invalid; + bool FacingDest(CBodyController& bc) const; public: - virtual bool CanShoot() const { return true; } - virtual void Start(CBodyController &, CStateManager &) {} - virtual void UpdateBody(float, CBodyController &, CStateManager&) {} - virtual void Shutdown(CBodyController &) {} - virtual void GetBodyStateTransition(float, CBodyController&); + bool CanShoot() const { return true; } + void Start(CBodyController& bc, CStateManager& mgr); + pas::EAnimationState UpdateBody(float dt, CBodyController& bc, CStateManager& mgr); + void Shutdown(CBodyController&) {} + virtual pas::EAnimationState GetBodyStateTransition(float dt, CBodyController& bc); }; class CBSFlyerTurn : public CBSTurn { public: - virtual void Start(CBodyController &, CStateManager &) {} - virtual void UpdateBody(float, CBodyController &, CStateManager&) {} + void Start(CBodyController& bc, CStateManager& mgr); + pas::EAnimationState UpdateBody(float dt, CBodyController& bc, CStateManager& mgr); }; class CBSLoopAttack : public CBodyState { + pas::ELoopState x4_state = pas::ELoopState::Invalid; + pas::ELoopAttackType x8_loopAttackType = pas::ELoopAttackType::Invalid; + bool xc_24_waitForAnimOver : 1; + bool xc_25_advance : 1; + pas::EAnimationState GetBodyStateTransition(float dt, CBodyController& bc); public: - virtual bool CanShoot() const { return true; } - virtual void Start(CBodyController &, CStateManager &) {} - virtual void UpdateBody(float, CBodyController &, CStateManager&); - virtual void Shutdown(CBodyController&) {} + CBSLoopAttack() { xc_24_waitForAnimOver = false; xc_25_advance = false; } + bool CanShoot() const { return true; } + void Start(CBodyController& bc, CStateManager& mgr); + pas::EAnimationState UpdateBody(float dt, CBodyController& bc, CStateManager& mgr); + void Shutdown(CBodyController&) {} +}; + +class CBSLoopReaction : public CBodyState +{ + pas::ELoopState x4_state = pas::ELoopState::Invalid; + pas::EReactionType x8_reactionType = pas::EReactionType::Invalid; + bool xc_24_loopHit : 1; + pas::EAnimationState GetBodyStateTransition(float dt, CBodyController& bc); + bool PlayExitAnimation(CBodyController& bc, CStateManager& mgr) const; +public: + CBSLoopReaction() { xc_24_loopHit = false; } + void Start(CBodyController& bc, CStateManager& mgr); + pas::EAnimationState UpdateBody(float dt, CBodyController& bc, CStateManager& mgr); + void Shutdown(CBodyController&) {} +}; + +class CBSGroundHit : public CBodyState +{ + float x4_rotateSpeed = 0.f; + float x8_remTime = 0.f; + pas::EFallState xc_fallState = pas::EFallState::Invalid; + pas::EAnimationState GetBodyStateTransition(float dt, CBodyController& bc); +public: + void Start(CBodyController& bc, CStateManager& mgr); + pas::EAnimationState UpdateBody(float dt, CBodyController& bc, CStateManager& mgr); + void Shutdown(CBodyController& bc); +}; + +class CBSGenerate : public CBodyState +{ + pas::EAnimationState GetBodyStateTransition(float dt, CBodyController& bc); +public: + void Start(CBodyController& bc, CStateManager& mgr); + pas::EAnimationState UpdateBody(float dt, CBodyController& bc, CStateManager& mgr); + void Shutdown(CBodyController&) {} +}; + +class CBSJump : public CBodyState +{ + pas::EJumpState x4_state = pas::EJumpState::Invalid; + pas::EJumpType x8_jumpType; + zeus::CVector3f xc_waypoint1; + zeus::CVector3f x18_velocity; + zeus::CVector3f x24_waypoint2; + union + { + struct + { + bool x30_24_bodyForceSet : 1; + bool x30_25_wallJump : 1; + bool x30_26_wallBounceRight : 1; + bool x30_27_wallBounceComplete : 1; + bool x30_28_startInJumpLoop : 1; + }; + u32 _dummy = 0; + }; + pas::EAnimationState GetBodyStateTransition(float dt, CBodyController& bc); + bool CheckForWallJump(CBodyController& bc, CStateManager& mgr); + void CheckForLand(CBodyController& bc, CStateManager& mgr); + void PlayJumpLoop(CStateManager& mgr, CBodyController& bc); +public: + bool IsMoving() const { return true; } + bool ApplyHeadTracking() const { return false; } + bool CanShoot() const; + void Start(CBodyController& bc, CStateManager& mgr); + pas::EAnimationState UpdateBody(float dt, CBodyController& bc, CStateManager& mgr); + bool ApplyAnimationDeltas() const; + bool IsInAir(const CBodyController& bc) const; + void Shutdown(CBodyController&) {} +}; + +class CBSHurled : public CBodyState +{ + pas::EHurledState x4_state = pas::EHurledState::Invalid; + float x8_knockAngle = 0.f; + int xc_animSeries = -1; + float x10_rotateSpeed = 0.f; + float x14_remTime = 0.f; + float x18_curTime = 0.f; + mutable zeus::CVector3f x1c_lastTranslation; + mutable float x28_landedDur = 0.f; + bool x2c_24_needsRecover : 1; + pas::EAnimationState GetBodyStateTransition(float dt, CBodyController& bc); + void Recover(CStateManager& mgr, CBodyController& bc, pas::EHurledState state); + void PlayStrikeWallAnimation(CBodyController& bc, CStateManager& mgr); + void PlayLandAnimation(CBodyController& bc, CStateManager& mgr); + bool ShouldStartStrikeWall(CBodyController& bc) const; + bool ShouldStartLand(float dt, CBodyController& bc) const; +public: + CBSHurled() { x2c_24_needsRecover = false; } + bool IsMoving() const { return true; } + bool IsInAir(const CBodyController&) const { return true; } + bool ApplyHeadTracking() const { return false; } + void Start(CBodyController& bc, CStateManager& mgr); + pas::EAnimationState UpdateBody(float dt, CBodyController& bc, CStateManager& mgr); + void Shutdown(CBodyController&) {} +}; + +class CBSSlide : public CBodyState +{ + float x4_rotateSpeed = 0.f; + pas::EAnimationState GetBodyStateTransition(float dt, CBodyController& bc); +public: + bool ApplyHeadTracking() const { return false; } + bool IsMoving() const { return true; } + void Start(CBodyController& bc, CStateManager& mgr); + pas::EAnimationState UpdateBody(float dt, CBodyController& bc, CStateManager& mgr); + void Shutdown(CBodyController&) {} +}; + +class CBSTaunt : public CBodyState +{ + pas::EAnimationState GetBodyStateTransition(float dt, CBodyController& bc); +public: + void Start(CBodyController& bc, CStateManager& mgr); + pas::EAnimationState UpdateBody(float dt, CBodyController& bc, CStateManager& mgr); + void Shutdown(CBodyController&) {} +}; + +class CBSScripted : public CBodyState +{ + union + { + struct + { + bool x4_24_loopAnim : 1; + bool x4_25_timedLoop : 1; + }; + u32 _dummy = 0; + }; + float x8_remTime = 0.f; + pas::EAnimationState GetBodyStateTransition(float dt, CBodyController& bc); +public: + bool ApplyHeadTracking() const { return false; } + void Start(CBodyController& bc, CStateManager& mgr); + pas::EAnimationState UpdateBody(float dt, CBodyController& bc, CStateManager& mgr); + void Shutdown(CBodyController&) {} +}; + +class CBSCover : public CBodyState +{ + pas::ECoverState x4_state = pas::ECoverState::Invalid; + pas::ECoverDirection x8_coverDirection = pas::ECoverDirection::Invalid; + bool xc_needsExit = false; + pas::EAnimationState GetBodyStateTransition(float dt, CBodyController& bc); +public: + bool ApplyHeadTracking() const { return false; } + bool IsMoving() const { return true; } + bool CanShoot() const { return x4_state == pas::ECoverState::Lean; } + void Start(CBodyController& bc, CStateManager& mgr); + pas::EAnimationState UpdateBody(float dt, CBodyController& bc, CStateManager& mgr); + void Shutdown(CBodyController&) {} +}; + +class CBSWallHang : public CBodyState +{ + pas::EWallHangState x4_state = pas::EWallHangState::Invalid; + TUniqueId x8_wpId = kInvalidUniqueId; + zeus::CVector3f xc_launchVel; + union + { + struct + { + bool x18_24_launched : 1; + bool x18_25_needsExit : 1; + }; + u32 _dummy = 0; + }; + pas::EAnimationState GetBodyStateTransition(float dt, CBodyController& bc); + void FixInPlace(CBodyController& bc); + bool CheckForLand(CBodyController& bc, CStateManager& mgr); + bool CheckForWall(CBodyController& bc, CStateManager& mgr); + void SetLaunchVelocity(CBodyController& bc); +public: + bool IsMoving() const { return true; } + bool CanShoot() const { return x4_state == pas::EWallHangState::WallHang; } + bool IsInAir(const CBodyController& bc) const; + bool ApplyGravity() const; + bool ApplyHeadTracking() const; + bool ApplyAnimationDeltas() const; + void Start(CBodyController& bc, CStateManager& mgr); + pas::EAnimationState UpdateBody(float dt, CBodyController& bc, CStateManager& mgr); + void Shutdown(CBodyController&) {} }; class CBSLocomotion : public CBodyState { +protected: + pas::ELocomotionType x4_locomotionType = pas::ELocomotionType::Invalid; + float GetStartVelocityMagnitude(CBodyController& bc); + void ReStartBodyState(CBodyController& bc, bool); + float ComputeWeightPercentage(const std::pair& a, + const std::pair& b, float f) const; public: - virtual bool IsMoving() const = 0; - virtual bool CanShoot() const { return true; } - virtual void Start(CBodyController &, CStateManager &) {} - virtual void UpdateBody(float, CBodyController &, CStateManager&) {} - virtual void Shutdown() const {} - virtual bool IsPitchable() const { return true; } - virtual float GetLocomotionSpeed(pas::ELocomotionType, pas::ELocomotionAnim) = 0; - virtual void ApplyLocomotionPysics(float, CBodyController&) {} - virtual void UpdateLocomotionAnimation(float, const CBodyController& )=0; - virtual void GetBodyStateTransition(float, CBodyState&) {} + bool IsMoving() const = 0; + bool CanShoot() const { return true; } + void Start(CBodyController& bc, CStateManager& mgr); + pas::EAnimationState UpdateBody(float dt, CBodyController& bc, CStateManager& mgr); + void Shutdown(CBodyController& bc); + virtual bool IsPitchable() const { return false; } + virtual float GetLocomotionSpeed(pas::ELocomotionType type, pas::ELocomotionAnim anim) const = 0; + virtual float ApplyLocomotionPhysics(float dt, CBodyController& bc); + virtual float UpdateLocomotionAnimation(float dt, float velMag, CBodyController& bc, bool init) = 0; + virtual pas::EAnimationState GetBodyStateTransition(float dt, CBodyController& bc); +}; + +class CBSBiPedLocomotion : public CBSLocomotion +{ +protected: + rstl::reserved_vector, 8>, 14> x8_anims; + pas::ELocomotionAnim x3c4_anim = pas::ELocomotionAnim::Invalid; + float x3c8_primeTime; + float UpdateRun(float vel, CBodyController& bc, pas::ELocomotionAnim anim); + float UpdateWalk(float vel, CBodyController& bc, pas::ELocomotionAnim anim); + float UpdateStrafe(float vel, CBodyController& bc, pas::ELocomotionAnim anim); + const std::pair& + GetLocoAnimation(pas::ELocomotionType type, pas::ELocomotionAnim anim) const + { return x8_anims[int(type)][int(anim)]; } +public: + CBSBiPedLocomotion(CActor& actor); + bool IsMoving() const { return x3c4_anim != pas::ELocomotionAnim::Idle; } + void Start(CBodyController& bc, CStateManager& mgr); + pas::EAnimationState UpdateBody(float dt, CBodyController& bc, CStateManager& mgr); + float GetLocomotionSpeed(pas::ELocomotionType type, pas::ELocomotionAnim anim) const; + float UpdateLocomotionAnimation(float dt, float velMag, CBodyController& bc, bool init); + virtual bool IsStrafing(const CBodyController& bc) const; +}; + +class CBSFlyerLocomotion : public CBSBiPedLocomotion +{ + bool x3cc_pitchable; +public: + CBSFlyerLocomotion(CActor& actor, bool pitchable); + bool IsPitchable() const { return x3cc_pitchable; } + float ApplyLocomotionPhysics(float dt, CBodyController& bc); + virtual bool IsBackPedal(CBodyController& bc) const { return false; } +}; + +class CBSWallWalkerLocomotion : public CBSBiPedLocomotion +{ +public: + CBSWallWalkerLocomotion(CActor& actor); + float ApplyLocomotionPhysics(float dt, CBodyController& bc); +}; + +class CBSNewFlyerLocomotion : public CBSBiPedLocomotion +{ +public: + CBSNewFlyerLocomotion(CActor& actor); + float ApplyLocomotionPhysics(float dt, CBodyController& bc); + float UpdateLocomotionAnimation(float dt, float velMag, CBodyController& bc, bool init); +}; + +class CBSRestrictedLocomotion : public CBSLocomotion +{ + rstl::reserved_vector x8_anims; + pas::ELocomotionAnim x44_anim = pas::ELocomotionAnim::Invalid; +public: + CBSRestrictedLocomotion(CActor& actor); + bool IsMoving() const { return false; } + float GetLocomotionSpeed(pas::ELocomotionType type, pas::ELocomotionAnim anim) const { return 0.f; } + float UpdateLocomotionAnimation(float dt, float velMag, CBodyController& bc, bool init); +}; + +class CBSRestrictedFlyerLocomotion : public CBSRestrictedLocomotion +{ +public: + CBSRestrictedFlyerLocomotion(CActor& actor); + float ApplyLocomotionPhysics(float dt, CBodyController& bc); }; } #endif // CBODYSTATE_HPP diff --git a/Runtime/Character/CBodyStateCmdMgr.cpp b/Runtime/Character/CBodyStateCmdMgr.cpp index 91509aa46..f7c163648 100644 --- a/Runtime/Character/CBodyStateCmdMgr.cpp +++ b/Runtime/Character/CBodyStateCmdMgr.cpp @@ -35,4 +35,63 @@ CBodyStateCmdMgr::CBodyStateCmdMgr() x40_commandTable.push_back(&x298_); } +void CBodyStateCmdMgr::DeliverCmd(const CBCLocomotionCmd& cmd) +{ + if (cmd.GetWeight() <= FLT_EPSILON) + return; + x3c_steeringSpeed += cmd.GetWeight(); + x0_move += cmd.GetMoveVector() * cmd.GetWeight(); + xc_face += cmd.GetFaceVector() * cmd.GetWeight(); +} + +void CBodyStateCmdMgr::BlendSteeringCmds() +{ + if (x3c_steeringSpeed > FLT_EPSILON) + { + float stepMul = 1.f / x3c_steeringSpeed; + xc_face *= stepMul; + + switch (x30_steeringMode) + { + case ESteeringBlendMode::Normal: + x0_move *= stepMul; + break; + case ESteeringBlendMode::FullSpeed: + if (!zeus::close_enough(x0_move, zeus::CVector3f::skZero, 0.0001f)) + { + x0_move.normalize(); + x0_move *= x38_steeringSpeedMax; + } + break; + case ESteeringBlendMode::Clamped: + x0_move *= stepMul; + if (!zeus::close_enough(x0_move, zeus::CVector3f::skZero, 0.0001f)) + { + if (x0_move.magnitude() < x34_steeringSpeedMin) + x0_move = x0_move.normalized() * x34_steeringSpeedMin; + else if (x0_move.magnitude() > x38_steeringSpeedMax) + x0_move = x0_move.normalized() * x38_steeringSpeedMax; + } + break; + default: break; + } + } +} + +void CBodyStateCmdMgr::Reset() +{ + x0_move = zeus::CVector3f::skZero; + xc_face = zeus::CVector3f::skZero; + x18_target = zeus::CVector3f::skZero; + x3c_steeringSpeed = 0.f; + xb4_deliveredCmdMask = 0; +} + +void CBodyStateCmdMgr::ClearLocomotionCmds() +{ + x0_move = zeus::CVector3f::skZero; + xc_face = zeus::CVector3f::skZero; + x3c_steeringSpeed = 0.f; +} + } diff --git a/Runtime/Character/CBodyStateCmdMgr.hpp b/Runtime/Character/CBodyStateCmdMgr.hpp index bf03f909d..7a26ffecc 100644 --- a/Runtime/Character/CBodyStateCmdMgr.hpp +++ b/Runtime/Character/CBodyStateCmdMgr.hpp @@ -20,23 +20,29 @@ public: class CBCMeleeAttackCmd : public CBodyStateCmd { pas::ESeverity x8_severity = pas::ESeverity::Invalid; - zeus::CVector3f xc_; - bool x18_ = false; + zeus::CVector3f xc_targetPos; + bool x18_hasTargetPos = false; public: CBCMeleeAttackCmd() : CBodyStateCmd(EBodyStateCmd::MeleeAttack) {} CBCMeleeAttackCmd(pas::ESeverity severity) : CBodyStateCmd(EBodyStateCmd::MeleeAttack), x8_severity(severity) {} + pas::ESeverity GetAttackSeverity() const { return x8_severity; } + bool HasAttackTargetPos() const { return x18_hasTargetPos; } + const zeus::CVector3f& GetAttackTargetPos() const { return xc_targetPos; } }; class CBCProjectileAttackCmd : public CBodyStateCmd { pas::ESeverity x8_severity = pas::ESeverity::Invalid; - zeus::CVector3f xc_; - bool x18_ = false; + zeus::CVector3f xc_target; + bool x18_blendAnims = false; public: CBCProjectileAttackCmd() : CBodyStateCmd(EBodyStateCmd::ProjectileAttack) {} CBCProjectileAttackCmd(pas::ESeverity severity, const zeus::CVector3f& vec, bool b) - : CBodyStateCmd(EBodyStateCmd::ProjectileAttack), x8_severity(severity), xc_(vec), x18_(b) {} + : CBodyStateCmd(EBodyStateCmd::ProjectileAttack), x8_severity(severity), xc_target(vec), x18_blendAnims(b) {} + pas::ESeverity GetAttackSeverity() const { return x8_severity; } + const zeus::CVector3f& GetTargetPosition() const { return xc_target; } + bool BlendTwoClosest() const { return x18_blendAnims; } }; class CBCStepCmd : public CBodyStateCmd @@ -47,52 +53,80 @@ public: CBCStepCmd() : CBodyStateCmd(EBodyStateCmd::Step) {} CBCStepCmd(pas::EStepDirection dir, pas::EStepType type) : CBodyStateCmd(EBodyStateCmd::Step), x8_dir(dir), xc_type(type) {} + pas::EStepDirection GetStepDirection() const { return x8_dir; } + pas::EStepType GetStepType() const { return xc_type; } }; class CBCJumpCmd : public CBodyStateCmd { - pas::EJumpType x8_type = pas::EJumpType::Zero; - zeus::CVector3f xc_; - zeus::CVector3f x18_; - bool x24_24_ : 1; - bool x24_25_ : 1; + pas::EJumpType x8_type = pas::EJumpType::Normal; + zeus::CVector3f xc_waypoint1; + zeus::CVector3f x18_waypoint2; + bool x24_24_wallJump : 1; + bool x24_25_startInJumpLoop : 1; public: CBCJumpCmd() - : CBodyStateCmd(EBodyStateCmd::Jump) { x24_24_ = false; x24_25_ = false; } - CBCJumpCmd(const zeus::CVector3f& vec, pas::EJumpType type) - : CBodyStateCmd(EBodyStateCmd::Jump), x8_type(type), xc_(vec) { x24_24_ = false; x24_25_ = false; } + : CBodyStateCmd(EBodyStateCmd::Jump) { x24_24_wallJump = false; x24_25_startInJumpLoop = false; } + CBCJumpCmd(const zeus::CVector3f& wp1, pas::EJumpType type, bool startInLoop = false) + : CBodyStateCmd(EBodyStateCmd::Jump), x8_type(type), xc_waypoint1(wp1) + { x24_24_wallJump = false; x24_25_startInJumpLoop = startInLoop; } + CBCJumpCmd(const zeus::CVector3f& wp1, const zeus::CVector3f& wp2, pas::EJumpType type) + : CBodyStateCmd(EBodyStateCmd::Jump), x8_type(type), xc_waypoint1(wp1), x18_waypoint2(wp2) + { x24_24_wallJump = true; x24_25_startInJumpLoop = false; } + pas::EJumpType GetJumpType() const { return x8_type; } + const zeus::CVector3f& GetJumpTarget() const { return xc_waypoint1; } + const zeus::CVector3f& GetSecondJumpTarget() const { return x18_waypoint2; } + bool IsWallJump() const { return x24_24_wallJump; } + bool StartInJumpLoop() const { return x24_25_startInJumpLoop; } }; class CBCGenerateCmd : public CBodyStateCmd { pas::EGenerateType x8_type = pas::EGenerateType::Invalid; + zeus::CVector3f xc_targetPos; + s32 x18_animId = -1; + bool x1c_24_targetTransform : 1; + bool x1c_25_overrideAnim : 1; public: - CBCGenerateCmd() : CBodyStateCmd(EBodyStateCmd::Generate) {} + CBCGenerateCmd() : CBodyStateCmd(EBodyStateCmd::Generate) { x1c_24_targetTransform = false; x1c_25_overrideAnim = false; } CBCGenerateCmd(pas::EGenerateType type, int i) - : CBodyStateCmd(EBodyStateCmd::Generate), x8_type(type) {} + : CBodyStateCmd(EBodyStateCmd::Generate), x8_type(type) { x1c_24_targetTransform = false; x1c_25_overrideAnim = false; } CBCGenerateCmd(pas::EGenerateType type, const zeus::CVector3f& vec) - : CBodyStateCmd(EBodyStateCmd::Generate), x8_type(type) {} + : CBodyStateCmd(EBodyStateCmd::Generate), x8_type(type) { x1c_24_targetTransform = false; x1c_25_overrideAnim = false; } + pas::EGenerateType GetGenerateType() const { return x8_type; } + const zeus::CVector3f& GetExitTargetPos() const { return xc_targetPos; } + bool HasExitTargetPos() const { return x1c_24_targetTransform; } + s32 GetSpecialAnimId() const { return x18_animId; } + bool UseSpecialAnimId() const { return x1c_25_overrideAnim; } }; class CBCKnockBackCmd : public CBodyStateCmd { - zeus::CVector3f x8_; + zeus::CVector3f x8_dir; pas::ESeverity x14_severity = pas::ESeverity::Invalid; public: CBCKnockBackCmd() : CBodyStateCmd(EBodyStateCmd::KnockBack) {} CBCKnockBackCmd(const zeus::CVector3f& vec, pas::ESeverity severity) - : CBodyStateCmd(EBodyStateCmd::KnockBack), x8_(vec), x14_severity(severity) {} + : CBodyStateCmd(EBodyStateCmd::KnockBack), x8_dir(vec), x14_severity(severity) {} + const zeus::CVector3f& GetHitDirection() const { return x8_dir; } + pas::ESeverity GetHitSeverity() const { return x14_severity; } }; class CBCHurledCmd : public CBodyStateCmd { - zeus::CVector3f x8_v0; - zeus::CVector3f x14_v1; - bool x20_ = false; + zeus::CVector3f x8_direction; + zeus::CVector3f x14_launchVel; + bool x20_startInKnockLoop = false; public: CBCHurledCmd() : CBodyStateCmd(EBodyStateCmd::Hurled) {} - CBCHurledCmd(const zeus::CVector3f& v0, const zeus::CVector3f& v1) - : CBodyStateCmd(EBodyStateCmd::Hurled), x8_v0(v0), x14_v1(v1) {} + CBCHurledCmd(const zeus::CVector3f& dir, const zeus::CVector3f& launchVel, + bool startInLoop = false) + : CBodyStateCmd(EBodyStateCmd::Hurled), x8_direction(dir), x14_launchVel(launchVel), + x20_startInKnockLoop(startInLoop) {} + const zeus::CVector3f& GetHitDirection() const { return x8_direction; } + const zeus::CVector3f& GetLaunchVelocity() const { return x14_launchVel; } + bool GetSkipLaunchState() const { return x20_startInKnockLoop; } + void SetSkipLaunchState(bool s) { x20_startInKnockLoop = s; } }; class CBCGetupCmd : public CBodyStateCmd @@ -102,6 +136,7 @@ public: CBCGetupCmd() : CBodyStateCmd(EBodyStateCmd::Getup) {} CBCGetupCmd(pas::EGetupType type) : CBodyStateCmd(EBodyStateCmd::Getup), x8_type(type) {} + pas::EGetupType GetGetupType() const { return x8_type; } }; class CBCLoopReactionCmd : public CBodyStateCmd @@ -111,6 +146,7 @@ public: CBCLoopReactionCmd() : CBodyStateCmd(EBodyStateCmd::LoopReaction) {} CBCLoopReactionCmd(pas::EReactionType type) : CBodyStateCmd(EBodyStateCmd::LoopReaction), x8_type(type) {} + pas::EReactionType GetReactionType() const { return x8_type; } }; class CBCLoopHitReactionCmd : public CBodyStateCmd @@ -120,58 +156,73 @@ public: CBCLoopHitReactionCmd() : CBodyStateCmd(EBodyStateCmd::LoopHitReaction) {} CBCLoopHitReactionCmd(pas::EReactionType type) : CBodyStateCmd(EBodyStateCmd::LoopHitReaction), x8_type(type) {} + pas::EReactionType GetReactionType() const { return x8_type; } }; class CBCKnockDownCmd : public CBodyStateCmd { - zeus::CVector3f x8_; + zeus::CVector3f x8_dir; pas::ESeverity x14_severity = pas::ESeverity::Invalid; public: CBCKnockDownCmd() : CBodyStateCmd(EBodyStateCmd::KnockDown) {} CBCKnockDownCmd(const zeus::CVector3f& vec, pas::ESeverity severity) - : CBodyStateCmd(EBodyStateCmd::KnockDown), x8_(vec), x14_severity(severity) {} + : CBodyStateCmd(EBodyStateCmd::KnockDown), x8_dir(vec), x14_severity(severity) {} + const zeus::CVector3f& GetHitDirection() const { return x8_dir; } + pas::ESeverity GetHitSeverity() const { return x14_severity; } }; class CBCSlideCmd : public CBodyStateCmd { pas::ESlideType x8_type = pas::ESlideType::Invalid; - zeus::CVector3f xc_; + zeus::CVector3f xc_dir; public: CBCSlideCmd() : CBodyStateCmd(EBodyStateCmd::Slide) {} - CBCSlideCmd(pas::ESlideType type, const zeus::CVector3f& vec) - : CBodyStateCmd(EBodyStateCmd::Slide), x8_type(type), xc_(vec) {} + CBCSlideCmd(pas::ESlideType type, const zeus::CVector3f& dir) + : CBodyStateCmd(EBodyStateCmd::Slide), x8_type(type), xc_dir(dir) {} + pas::ESlideType GetSlideType() const { return x8_type; } + const zeus::CVector3f& GetSlideDirection() const { return xc_dir; } }; class CBCScriptedCmd : public CBodyStateCmd { - int x8_ = -1; - bool xc_24_ : 1; - bool xc_25_ : 1; - float x10_ = 0.f; + s32 x8_anim = -1; + bool xc_24_loopAnim : 1; + bool xc_25_timedLoop : 1; + float x10_loopDur = 0.f; public: - CBCScriptedCmd() : CBodyStateCmd(EBodyStateCmd::Scripted) { xc_24_ = false; xc_25_ = false; } - CBCScriptedCmd(int i, bool b1, bool b2, float f) : CBodyStateCmd(EBodyStateCmd::Scripted), - x8_(i), x10_(f) { xc_24_ = b1; xc_25_ = b2; } + CBCScriptedCmd() : CBodyStateCmd(EBodyStateCmd::Scripted) + { xc_24_loopAnim = false; xc_25_timedLoop = false; } + CBCScriptedCmd(int i, bool b1, bool b2, float f) + : CBodyStateCmd(EBodyStateCmd::Scripted), + x8_anim(i), x10_loopDur(f) { xc_24_loopAnim = b1; xc_25_timedLoop = b2; } + s32 GetAnimId() const { return x8_anim; } + bool IsLooped() const { return xc_24_loopAnim; } + bool GetUseLoopDuration() const { return xc_25_timedLoop; } + float GetLoopDuration() const { return x10_loopDur; } }; class CBCCoverCmd : public CBodyStateCmd { pas::ECoverDirection x8_dir = pas::ECoverDirection::Invalid; - zeus::CVector3f xc_; - zeus::CVector3f x18_; + zeus::CVector3f xc_targetPos; + zeus::CVector3f x18_alignDir; public: CBCCoverCmd() : CBodyStateCmd(EBodyStateCmd::Cover) {} CBCCoverCmd(pas::ECoverDirection dir, const zeus::CVector3f& v1, const zeus::CVector3f& v2) : - CBodyStateCmd(EBodyStateCmd::Cover), x8_dir(dir), xc_(v1), x18_(v2) {} + CBodyStateCmd(EBodyStateCmd::Cover), x8_dir(dir), xc_targetPos(v1), x18_alignDir(v2) {} + pas::ECoverDirection GetDirection() const { return x8_dir; } + const zeus::CVector3f& GetTarget() const { return xc_targetPos; } + const zeus::CVector3f& GetAlignDirection() const { return x18_alignDir; } }; class CBCWallHangCmd : public CBodyStateCmd { - TUniqueId x8_uid = kInvalidUniqueId; + TUniqueId x8_wpId = kInvalidUniqueId; public: CBCWallHangCmd() : CBodyStateCmd(EBodyStateCmd::WallHang) {} CBCWallHangCmd(TUniqueId uid) : - CBodyStateCmd(EBodyStateCmd::WallHang), x8_uid(uid) {} + CBodyStateCmd(EBodyStateCmd::WallHang), x8_wpId(uid) {} + TUniqueId GetTarget() const { return x8_wpId; } }; class CBCAdditiveAimCmd : public CBodyStateCmd @@ -202,11 +253,13 @@ public: class CBCLoopAttackCmd : public CBodyStateCmd { pas::ELoopAttackType x8_type = pas::ELoopAttackType::Invalid; - u32 xc_ = 0; + u32 xc_waitForAnimOver = 0; public: CBCLoopAttackCmd() : CBodyStateCmd(EBodyStateCmd::LoopAttack) {} CBCLoopAttackCmd(pas::ELoopAttackType type) : CBodyStateCmd(EBodyStateCmd::LoopAttack), x8_type(type) {} + pas::ELoopAttackType GetAttackType() const { return x8_type; } + bool WaitForAnimOver() const { return xc_waitForAnimOver == 1; } }; class CBCTauntCmd : public CBodyStateCmd @@ -216,32 +269,44 @@ public: CBCTauntCmd() : CBodyStateCmd(EBodyStateCmd::Taunt) {} CBCTauntCmd(pas::ETauntType type) : CBodyStateCmd(EBodyStateCmd::Taunt), x8_type(type) {} + pas::ETauntType GetTauntType() const { return x8_type; } }; class CBCLocomotionCmd { + zeus::CVector3f x0_move; + zeus::CVector3f xc_face; + float x18_weight; public: + CBCLocomotionCmd(const zeus::CVector3f& v1, const zeus::CVector3f& v2, float f) + : x0_move(v1), xc_face(v2), x18_weight(f) {} + const zeus::CVector3f& GetMoveVector() const { return x0_move; } + const zeus::CVector3f& GetFaceVector() const { return xc_face; } + float GetWeight() const { return x18_weight; } +}; + +enum class ESteeringBlendMode +{ + Normal, + FullSpeed, + Clamped }; class CBodyStateCmdMgr { -public: - enum class ESteeringBlendMode - { - }; -private: - zeus::CVector3f x0_; - zeus::CVector3f xc_; - zeus::CVector3f x18_; - zeus::CVector3f x24_; - u32 x30_ = 0; - float x34_steeringSpeedMin; - float x38_steeringSpeedMax; + zeus::CVector3f x0_move; + zeus::CVector3f xc_face; + zeus::CVector3f x18_target; + zeus::CVector3f x24_additiveTarget; + ESteeringBlendMode x30_steeringMode = ESteeringBlendMode::Normal; + float x34_steeringSpeedMin = 0.f; + float x38_steeringSpeedMax = 1.f; + float x3c_steeringSpeed = 0.f; rstl::reserved_vector x40_commandTable; u32 xb4_deliveredCmdMask = 0; CBCGetupCmd xb8_getup; CBCStepCmd xc4_step; - CBodyStateCmd xd4_ = {EBodyStateCmd::Two}; + CBodyStateCmd xd4_ = {EBodyStateCmd::Die}; CBCKnockDownCmd xdc_knockDown; CBCKnockBackCmd xf4_knockBack; CBCMeleeAttackCmd x10c_meleeAttack; @@ -249,9 +314,9 @@ private: CBCLoopAttackCmd x144_loopAttack; CBCLoopReactionCmd x154_loopReaction; CBCLoopHitReactionCmd x160_loopHitReaction; - CBodyStateCmd x16c_ = {EBodyStateCmd::Ten}; - CBodyStateCmd x174_ = {EBodyStateCmd::Eleven}; - CBodyStateCmd x17c_ = {EBodyStateCmd::Twelve}; + CBodyStateCmd x16c_ = {EBodyStateCmd::ExitState}; + CBodyStateCmd x174_ = {EBodyStateCmd::LeanFromCover}; + CBodyStateCmd x17c_ = {EBodyStateCmd::NextState}; CBodyStateCmd x184_ = {EBodyStateCmd::Thirteen}; CBCGenerateCmd x18c_generate; CBCHurledCmd x1ac_hurled; @@ -261,7 +326,7 @@ private: CBCScriptedCmd x21c_scripted; CBCCoverCmd x230_cover; CBCWallHangCmd x254_wallHang; - CBodyStateCmd x260_ = {EBodyStateCmd::TwentyTwo}; + CBodyStateCmd x260_ = {EBodyStateCmd::Locomotion}; CBodyStateCmd x268_ = {EBodyStateCmd::TwentyThree}; CBCAdditiveAimCmd x270_additiveAim; CBCAdditiveFlinchCmd x278_additiveFlinch; @@ -376,20 +441,25 @@ public: DeliverCmd(EBodyStateCmd::AdditiveReaction); } void DeliverCmd(const CBCLocomotionCmd& cmd); - void DeliverTargetVector(const zeus::CVector3f&); - void DeliverAdditiveTargetVector(const zeus::CVector3f&); - void SetSteeringBlendSpeed(float); - void SetSteeringBlendMode(ESteeringBlendMode); - void SetSteeringSpeedRange(float, float); + void DeliverTargetVector(const zeus::CVector3f& t) { x18_target = t; } + void DeliverAdditiveTargetVector(const zeus::CVector3f& t) { x24_additiveTarget = t; } + void SetSteeringBlendSpeed(float s) { x3c_steeringSpeed = s; } + void SetSteeringBlendMode(ESteeringBlendMode m) { x30_steeringMode = m; } + void SetSteeringSpeedRange(float rmin, float rmax) + { x34_steeringSpeedMin = rmin; x38_steeringSpeedMax = rmax; } void BlendSteeringCmds(); void Reset(); - void ClearLocomtionCmds(); - void GetCmd(EBodyStateCmd); - zeus::CVector3f GetTargetVector() const; - void GetFaceVector() const; - void GetMoveVector() const; - s32 GetNumSteerCmds() const; - zeus::CVector3f GetAdditiveTargetVector() const; + void ClearLocomotionCmds(); + const CBodyStateCmd* GetCmd(EBodyStateCmd cmd) const + { + if (xb4_deliveredCmdMask & (1 << int(cmd))) + return x40_commandTable[int(cmd)]; + return nullptr; + } + const zeus::CVector3f& GetMoveVector() const { return x0_move; } + const zeus::CVector3f& GetFaceVector() const { return xc_face; } + const zeus::CVector3f& GetTargetVector() const { return x18_target; } + const zeus::CVector3f& GetAdditiveTargetVector() const { return x24_additiveTarget; } }; } diff --git a/Runtime/Character/CBodyStateInfo.cpp b/Runtime/Character/CBodyStateInfo.cpp new file mode 100644 index 000000000..638e79849 --- /dev/null +++ b/Runtime/Character/CBodyStateInfo.cpp @@ -0,0 +1,416 @@ +#include "CBodyStateInfo.hpp" +#include "World/CActor.hpp" +#include "CBodyController.hpp" + +namespace urde +{ + +CBodyStateInfo::CBodyStateInfo(CActor& actor, EBodyType type) +{ + x34_24_changeLocoAtEndOfAnimOnly = false; + const CPASDatabase& pasDatabase = + actor.GetModelData()->GetAnimationData()->GetCharacterInfo().GetPASDatabase(); + for (int i=0 ; i bs; + + switch (type) + { + case EBodyType::BiPedal: + bs = SetupBiPedalBodyStates(state->GetStateId(), actor); + break; + case EBodyType::Restricted: + default: + bs = SetupRestrictedBodyStates(state->GetStateId(), actor); + break; + case EBodyType::Flyer: + bs = SetupFlyerBodyStates(state->GetStateId(), actor); + break; + case EBodyType::Pitchable: + bs = SetupPitchableBodyStates(state->GetStateId(), actor); + break; + case EBodyType::WallWalker: + bs = SetupWallWalkerBodyStates(state->GetStateId(), actor); + break; + case EBodyType::NewFlyer: + bs = SetupNewFlyerBodyStates(state->GetStateId(), actor); + break; + case EBodyType::RestrictedFlyer: + bs = SetupRestrictedFlyerBodyStates(state->GetStateId(), actor); + break; + } + + if (bs) + x0_stateMap[state->GetStateId()] = std::move(bs); + } + + x1c_additiveStates.reserve(4); + x1c_additiveStates.push_back({21, std::make_unique()}); + x1c_additiveStates.push_back({22, std::make_unique()}); + x1c_additiveStates.push_back({23, std::make_unique()}); + x1c_additiveStates.push_back({24, std::make_unique()}); +} + +std::unique_ptr CBodyStateInfo::SetupRestrictedFlyerBodyStates(int stateId, CActor& actor) +{ + switch (stateId) + { + case 0: + return std::make_unique(); + case 1: + return std::make_unique(); + case 2: + return std::make_unique(actor); + case 3: + return std::make_unique(); + case 4: + return std::make_unique(); + case 5: + return std::make_unique(actor); + case 6: + return std::make_unique(); + case 7: + return std::make_unique(); + case 18: + return std::make_unique(); + case 9: + return std::make_unique(); + case 8: + return std::make_unique(); + case 10: + return std::make_unique(); + case 11: + return std::make_unique(); + case 12: + return std::make_unique(); + case 13: + return std::make_unique(); + case 14: + return std::make_unique(); + case 15: + return std::make_unique(); + case 16: + return std::make_unique(); + case 17: + return std::make_unique(); + default: + return {}; + } +} + +std::unique_ptr CBodyStateInfo::SetupNewFlyerBodyStates(int stateId, CActor& actor) +{ + switch (stateId) + { + case 0: + return std::make_unique(); + case 1: + return std::make_unique(); + case 2: + return std::make_unique(actor); + case 3: + return std::make_unique(); + case 4: + return std::make_unique(); + case 5: + return std::make_unique(actor); + case 6: + return std::make_unique(); + case 7: + return std::make_unique(); + case 18: + return std::make_unique(); + case 9: + return std::make_unique(); + case 8: + return std::make_unique(); + case 10: + return std::make_unique(); + case 11: + return std::make_unique(); + case 12: + return std::make_unique(); + case 13: + return std::make_unique(); + case 14: + return std::make_unique(); + case 15: + return std::make_unique(); + case 16: + return std::make_unique(); + case 17: + return std::make_unique(); + default: + return {}; + } +} + +std::unique_ptr CBodyStateInfo::SetupWallWalkerBodyStates(int stateId, CActor& actor) +{ + switch (stateId) + { + case 0: + return std::make_unique(); + case 1: + return std::make_unique(); + case 2: + return std::make_unique(actor); + case 3: + return std::make_unique(); + case 4: + return std::make_unique(); + case 5: + return std::make_unique(actor); + case 6: + return std::make_unique(); + case 7: + return std::make_unique(); + case 18: + return std::make_unique(); + case 9: + return std::make_unique(); + case 8: + return std::make_unique(); + case 10: + return std::make_unique(); + case 11: + return std::make_unique(); + case 12: + return std::make_unique(); + case 13: + return std::make_unique(); + case 14: + return std::make_unique(); + case 15: + return std::make_unique(); + case 16: + return std::make_unique(); + case 17: + return std::make_unique(); + default: + return {}; + } +} + +std::unique_ptr CBodyStateInfo::SetupPitchableBodyStates(int stateId, CActor& actor) +{ + switch (stateId) + { + case 0: + return std::make_unique(); + case 1: + return std::make_unique(); + case 2: + return std::make_unique(actor); + case 3: + return std::make_unique(); + case 4: + return std::make_unique(); + case 5: + return std::make_unique(actor, true); + case 6: + return std::make_unique(); + case 7: + return std::make_unique(); + case 18: + return std::make_unique(); + case 9: + return std::make_unique(); + case 8: + return std::make_unique(); + case 10: + return std::make_unique(); + case 11: + return std::make_unique(); + case 12: + return std::make_unique(); + case 13: + return std::make_unique(); + case 14: + return std::make_unique(); + case 15: + return std::make_unique(); + case 16: + return std::make_unique(); + case 17: + return std::make_unique(); + default: + return {}; + } +} + +std::unique_ptr CBodyStateInfo::SetupFlyerBodyStates(int stateId, CActor& actor) +{ + switch (stateId) + { + case 0: + return std::make_unique(); + case 1: + return std::make_unique(); + case 2: + return std::make_unique(actor); + case 3: + return std::make_unique(); + case 4: + return std::make_unique(); + case 5: + return std::make_unique(actor, false); + case 6: + return std::make_unique(); + case 7: + return std::make_unique(); + case 18: + return std::make_unique(); + case 9: + return std::make_unique(); + case 8: + return std::make_unique(); + case 10: + return std::make_unique(); + case 11: + return std::make_unique(); + case 12: + return std::make_unique(); + case 13: + return std::make_unique(); + case 14: + return std::make_unique(); + case 15: + return std::make_unique(); + case 16: + return std::make_unique(); + case 17: + return std::make_unique(); + default: + return {}; + } +} + +std::unique_ptr CBodyStateInfo::SetupRestrictedBodyStates(int stateId, CActor& actor) +{ + switch (stateId) + { + case 0: + return std::make_unique(); + case 1: + return std::make_unique(); + case 2: + return std::make_unique(actor); + case 3: + return std::make_unique(); + case 4: + return std::make_unique(); + case 5: + return std::make_unique(actor); + case 6: + return std::make_unique(); + case 7: + return std::make_unique(); + case 18: + return std::make_unique(); + case 9: + return std::make_unique(); + case 8: + return std::make_unique(); + case 10: + return std::make_unique(); + case 11: + return std::make_unique(); + case 12: + return std::make_unique(); + case 13: + return std::make_unique(); + case 14: + return std::make_unique(); + case 15: + return std::make_unique(); + case 16: + return std::make_unique(); + case 17: + return std::make_unique(); + case 19: + return std::make_unique(); + default: + return {}; + } +} + +std::unique_ptr CBodyStateInfo::SetupBiPedalBodyStates(int stateId, CActor& actor) +{ + switch (stateId) + { + case 0: + return std::make_unique(); + case 1: + return std::make_unique(); + case 2: + return std::make_unique(actor); + case 3: + return std::make_unique(); + case 4: + return std::make_unique(); + case 5: + return std::make_unique(actor); + case 6: + return std::make_unique(); + case 7: + return std::make_unique(); + case 18: + return std::make_unique(); + case 9: + return std::make_unique(); + case 8: + return std::make_unique(); + case 10: + return std::make_unique(); + case 11: + return std::make_unique(); + case 12: + return std::make_unique(); + case 13: + return std::make_unique(); + case 14: + return std::make_unique(); + case 15: + return std::make_unique(); + case 16: + return std::make_unique(); + case 17: + return std::make_unique(); + case 19: + return std::make_unique(); + case 20: + return std::make_unique(); + default: + return {}; + } +} + +float CBodyStateInfo::GetLocomotionSpeed(pas::ELocomotionAnim anim) const +{ + auto search = x0_stateMap.find(5); + if (search != x0_stateMap.cend() && search->second && x18_bodyController) + { + const CBSLocomotion& bs = static_cast(*search->second); + return bs.GetLocomotionSpeed(x18_bodyController->GetLocomotionType(), anim); + } + return 0.f; +} + +float CBodyStateInfo::GetMaxSpeed() const +{ + float ret = GetLocomotionSpeed(pas::ELocomotionAnim::Run); + if (std::fabs(ret) < 0.00001f) + { + for (int i=0 ; i<8 ; ++i) + { + float tmp = GetLocomotionSpeed(pas::ELocomotionAnim(i)); + if (tmp > ret) + ret = tmp; + } + } + return ret; +} + +} diff --git a/Runtime/Character/CBodyStateInfo.hpp b/Runtime/Character/CBodyStateInfo.hpp new file mode 100644 index 000000000..a0023f521 --- /dev/null +++ b/Runtime/Character/CBodyStateInfo.hpp @@ -0,0 +1,41 @@ +#ifndef __URDE_CBODYSTATEINFO_HPP__ +#define __URDE_CBODYSTATEINFO_HPP__ + +#include "RetroTypes.hpp" +#include "CharacterCommon.hpp" +#include "CBodyState.hpp" +#include "CAdditiveBodyState.hpp" + +namespace urde +{ +class CActor; + +class CBodyStateInfo +{ + friend class CBodyController; + std::map> x0_stateMap; + int x14_ = -1; + CBodyController* x18_bodyController = nullptr; + std::vector>> x1c_additiveStates; + u32 x2c_ = 0x15; + float x30_maxPitch = 0.f; + bool x34_24_changeLocoAtEndOfAnimOnly; + std::unique_ptr SetupRestrictedFlyerBodyStates(int stateId, CActor& actor); + std::unique_ptr SetupNewFlyerBodyStates(int stateId, CActor& actor); + std::unique_ptr SetupWallWalkerBodyStates(int stateId, CActor& actor); + std::unique_ptr SetupPitchableBodyStates(int stateId, CActor& actor); + std::unique_ptr SetupFlyerBodyStates(int stateId, CActor& actor); + std::unique_ptr SetupRestrictedBodyStates(int stateId, CActor& actor); + std::unique_ptr SetupBiPedalBodyStates(int stateId, CActor& actor); +public: + CBodyStateInfo(CActor& actor, EBodyType type); + float GetLocomotionSpeed(pas::ELocomotionAnim anim) const; + float GetMaxSpeed() const; + float GetMaximumPitch() const { return x30_maxPitch; } + bool GetLocoAnimChangeAtEndOfAnimOnly() const { return x34_24_changeLocoAtEndOfAnimOnly; } + void SetLocoAnimChangeAtEndOfAnimOnly(bool s) { x34_24_changeLocoAtEndOfAnimOnly = s; } +}; + +} + +#endif // __URDE_CBODYSTATEINFO_HPP__ diff --git a/Runtime/Character/CCharAnimTime.cpp b/Runtime/Character/CCharAnimTime.cpp index 45bf7f5bd..489889023 100644 --- a/Runtime/Character/CCharAnimTime.cpp +++ b/Runtime/Character/CCharAnimTime.cpp @@ -80,7 +80,7 @@ bool CCharAnimTime::operator !=(const CCharAnimTime& other) const return !(*this == other); } -bool CCharAnimTime::operator>=(const CCharAnimTime& other) +bool CCharAnimTime::operator>=(const CCharAnimTime& other) const { if (*this == other) return true; @@ -88,7 +88,7 @@ bool CCharAnimTime::operator>=(const CCharAnimTime& other) return (*this > other); } -bool CCharAnimTime::operator<=(const CCharAnimTime& other) +bool CCharAnimTime::operator<=(const CCharAnimTime& other) const { if (*this == other) return true; @@ -162,7 +162,7 @@ CCharAnimTime& CCharAnimTime::operator+=(const CCharAnimTime& other) return *this; } -CCharAnimTime CCharAnimTime::operator+(const CCharAnimTime& other) +CCharAnimTime CCharAnimTime::operator+(const CCharAnimTime& other) const { if (x4_type == EType::Infinity && other.x4_type == EType::Infinity) { @@ -216,7 +216,7 @@ CCharAnimTime& CCharAnimTime::operator-=(const CCharAnimTime& other) return *this; } -CCharAnimTime CCharAnimTime::operator-(const CCharAnimTime& other) +CCharAnimTime CCharAnimTime::operator-(const CCharAnimTime& other) const { if (x4_type == EType::Infinity && other.x4_type == EType::Infinity) { @@ -266,7 +266,7 @@ CCharAnimTime CCharAnimTime::operator-(const CCharAnimTime& other) return ret; } -CCharAnimTime CCharAnimTime::operator*(const CCharAnimTime& other) +CCharAnimTime CCharAnimTime::operator*(const CCharAnimTime& other) const { if (x4_type == EType::Infinity && other.x4_type == EType::Infinity) { @@ -313,7 +313,7 @@ CCharAnimTime CCharAnimTime::operator*(const CCharAnimTime& other) return ret; } -CCharAnimTime CCharAnimTime::operator*(const float& other) +CCharAnimTime CCharAnimTime::operator*(const float& other) const { CCharAnimTime ret; if (other == 0.f) @@ -331,7 +331,7 @@ CCharAnimTime CCharAnimTime::operator*(const float& other) return ret; } -float CCharAnimTime::operator/(const CCharAnimTime& other) +float CCharAnimTime::operator/(const CCharAnimTime& other) const { if (other.EqualsZero()) return 0.f; diff --git a/Runtime/Character/CCharAnimTime.hpp b/Runtime/Character/CCharAnimTime.hpp index 7bef80bde..8772797b5 100644 --- a/Runtime/Character/CCharAnimTime.hpp +++ b/Runtime/Character/CCharAnimTime.hpp @@ -36,25 +36,26 @@ public: } static CCharAnimTime Infinity(); - operator float() const {return x0_time;} + float GetSeconds() const { return x0_time; } bool EqualsZero() const; bool EpsilonZero() const; bool GreaterThanZero() const; bool operator ==(const CCharAnimTime& other) const; bool operator !=(const CCharAnimTime& other) const; - bool operator>=(const CCharAnimTime& other); - bool operator<=(const CCharAnimTime& other); + bool operator>=(const CCharAnimTime& other) const; + bool operator<=(const CCharAnimTime& other) const; bool operator >(const CCharAnimTime& other) const; bool operator <(const CCharAnimTime& other) const; CCharAnimTime& operator*=(const CCharAnimTime& other); CCharAnimTime& operator+=(const CCharAnimTime& other); - CCharAnimTime operator+(const CCharAnimTime& other); + CCharAnimTime operator+(const CCharAnimTime& other) const; CCharAnimTime& operator-=(const CCharAnimTime& other); - CCharAnimTime operator-(const CCharAnimTime& other); - CCharAnimTime operator*(const CCharAnimTime& other); - CCharAnimTime operator*(const float& other); - float operator/(const CCharAnimTime& other); + CCharAnimTime operator-(const CCharAnimTime& other) const; + CCharAnimTime operator*(const CCharAnimTime& other) const; + CCharAnimTime operator*(const float& other) const; + float operator/(const CCharAnimTime& other) const; + }; } diff --git a/Runtime/Character/CFBStreamedCompression.cpp b/Runtime/Character/CFBStreamedCompression.cpp index 5aa691f0f..b8d9326a5 100644 --- a/Runtime/Character/CFBStreamedCompression.cpp +++ b/Runtime/Character/CFBStreamedCompression.cpp @@ -280,7 +280,7 @@ float CFBStreamedCompression::CalculateAverageVelocity(const u8* chans) transCompA = transCompB; } - return accumMag / GetAnimationDuration(); + return accumMag / GetAnimationDuration().GetSeconds(); } } diff --git a/Runtime/Character/CMakeLists.txt b/Runtime/Character/CMakeLists.txt index d3cbb6f4e..1d85951bf 100644 --- a/Runtime/Character/CMakeLists.txt +++ b/Runtime/Character/CMakeLists.txt @@ -85,9 +85,11 @@ set(CHARACTER_SOURCES CActorLights.hpp CActorLights.cpp CAnimSysContext.hpp CBodyState.hpp CBodyState.cpp + CAdditiveBodyState.hpp CAdditiveBodyState.cpp CBodyStateCmdMgr.hpp CBodyStateCmdMgr.cpp CBodyController.hpp CBodyController.cpp CGroundMovement.hpp CGroundMovement.cpp - CSteeringBehaviors.hpp CSteeringBehaviors.cpp) + CSteeringBehaviors.hpp CSteeringBehaviors.cpp + CBodyStateInfo.hpp CBodyStateInfo.cpp) runtime_add_list(Character CHARACTER_SOURCES) diff --git a/Runtime/Character/CMetaTransTrans.cpp b/Runtime/Character/CMetaTransTrans.cpp index 94d4a1a3e..f64eae35a 100644 --- a/Runtime/Character/CMetaTransTrans.cpp +++ b/Runtime/Character/CMetaTransTrans.cpp @@ -17,6 +17,6 @@ std::shared_ptr CMetaTransTrans::VGetTransitionTree(const std::we const CAnimSysContext& animSys) const { return std::make_shared(xc_, a, b, x4_animTime, xd_, x10_, - CAnimTreeTransition::CreatePrimitiveName(a, b, x4_animTime)); + CAnimTreeTransition::CreatePrimitiveName(a, b, x4_animTime.GetSeconds())); } } diff --git a/Runtime/Character/CPASAnimInfo.hpp b/Runtime/Character/CPASAnimInfo.hpp index bc5159483..0ebdc4d21 100644 --- a/Runtime/Character/CPASAnimInfo.hpp +++ b/Runtime/Character/CPASAnimInfo.hpp @@ -13,8 +13,9 @@ class CPASAnimInfo u32 x0_id; rstl::reserved_vector x4_parms; public: + CPASAnimInfo(u32 id) : x0_id(id) {} CPASAnimInfo(u32 id, rstl::reserved_vector&& parms); - u32 GetAnimId() const {return x0_id;} + u32 GetAnimId() const { return x0_id; } CPASAnimParm::UParmValue GetAnimParmValue(u32 idx) const; CPASAnimParm GetAnimParmData(u32, CPASAnimParm::EParmType) const; }; diff --git a/Runtime/Character/CPASAnimParmData.hpp b/Runtime/Character/CPASAnimParmData.hpp index f6a77a4a7..32ef1fa61 100644 --- a/Runtime/Character/CPASAnimParmData.hpp +++ b/Runtime/Character/CPASAnimParmData.hpp @@ -13,9 +13,15 @@ class CPASAnimParmData public: CPASAnimParmData() = default; - CPASAnimParmData(s32 stateId, const CPASAnimParm& parm1, const CPASAnimParm& parm2, const CPASAnimParm& parm3, - const CPASAnimParm& parm4, const CPASAnimParm& parm5, const CPASAnimParm& parm6, - const CPASAnimParm& parm7, const CPASAnimParm& parm8); + CPASAnimParmData(s32 stateId, + const CPASAnimParm& parm1 = CPASAnimParm::NoParameter(), + const CPASAnimParm& parm2 = CPASAnimParm::NoParameter(), + const CPASAnimParm& parm3 = CPASAnimParm::NoParameter(), + const CPASAnimParm& parm4 = CPASAnimParm::NoParameter(), + const CPASAnimParm& parm5 = CPASAnimParm::NoParameter(), + const CPASAnimParm& parm6 = CPASAnimParm::NoParameter(), + const CPASAnimParm& parm7 = CPASAnimParm::NoParameter(), + const CPASAnimParm& parm8 = CPASAnimParm::NoParameter()); s32 GetStateId() const { return x0_stateId; } const rstl::reserved_vector& GetAnimParmData() const { return x4_parms; } diff --git a/Runtime/Character/CPASAnimState.cpp b/Runtime/Character/CPASAnimState.cpp index e031e3645..bde8526ce 100644 --- a/Runtime/Character/CPASAnimState.cpp +++ b/Runtime/Character/CPASAnimState.cpp @@ -60,16 +60,17 @@ CPASAnimState::CPASAnimState(int stateId) : x0_id(stateId) {} -CPASAnimParm CPASAnimState::GetAnimParmData(s32 idx, u32 id) const +CPASAnimParm CPASAnimState::GetAnimParmData(s32 animId, u32 parmIdx) const { - auto search = std::lower_bound(x14_anims.begin(), x14_anims.end(), id, - [](const CPASAnimInfo& item, const u32& testId) -> - bool {return item.GetAnimId() < testId;}); - if (search == x14_anims.end()) + CPASAnimInfo key(animId); + auto search = std::lower_bound(x14_anims.begin(), x14_anims.end(), key, + [](const CPASAnimInfo& item, const CPASAnimInfo& testId) -> + bool {return item.GetAnimId() < testId.GetAnimId();}); + if (search == x14_anims.end() || search->GetAnimId() > animId) return CPASAnimParm::NoParameter(); - CPASParmInfo parm = x4_parms.at(idx); - return (*search).GetAnimParmData(idx, parm.GetParameterType()); + CPASParmInfo parm = x4_parms.at(parmIdx); + return (*search).GetAnimParmData(parmIdx, parm.GetParameterType()); } s32 CPASAnimState::PickRandomAnimation(CRandom16& rand) const @@ -208,7 +209,7 @@ float CPASAnimState::ComputePercentErrorWeight(u32 idx, const CPASAnimParm& parm case CPASAnimParm::EParmType::Enum: { const CPASParmInfo& info = x4_parms[idx]; - range = info.GetWeightMaxValue().m_float - info.GetWeightMinValue().m_float; + range = info.GetWeightMaxValue().m_int - info.GetWeightMinValue().m_int; val = std::fabs(parm.GetEnumValue() - parmVal.m_int); break; } diff --git a/Runtime/Character/CPASDatabase.cpp b/Runtime/Character/CPASDatabase.cpp index 0b041f457..d758daff3 100644 --- a/Runtime/Character/CPASDatabase.cpp +++ b/Runtime/Character/CPASDatabase.cpp @@ -41,7 +41,7 @@ std::pair CPASDatabase::FindBestAnimation(const CPASAnimParmData& da auto it = std::lower_bound(x0_states.cbegin(), x0_states.cend(), key, [](const CPASAnimState& item, const CPASAnimState& test) -> bool {return item.GetStateId() < test.GetStateId();}); - if (it->GetStateId() < key.GetStateId() || it == x0_states.cend()) + if (it == x0_states.cend() || it->GetStateId() > key.GetStateId()) return {0.f, -1}; return (*it).FindBestAnimation(data.GetAnimParmData(), rand, ignoreAnim); diff --git a/Runtime/Character/CTimeScaleFunctions.cpp b/Runtime/Character/CTimeScaleFunctions.cpp index 47a3e1409..7520f64d5 100644 --- a/Runtime/Character/CTimeScaleFunctions.cpp +++ b/Runtime/Character/CTimeScaleFunctions.cpp @@ -51,10 +51,10 @@ std::shared_ptr CLinearAnimationTimeScale::VClone() CLinearAnimationTimeScale* ret = new CLinearAnimationTimeScale(); float f30 = x4_ * xc_ + x8_; - ret->x4_ = (x4_ * x10_ + x8_ - f30) / timeB; - ret->x8_ = -((x4_ * x10_ + x8_ - f30) / (timeA - timeB) * timeB - f30); - ret->xc_ = timeB; - ret->x10_ = timeA; + ret->x4_ = (x4_ * x10_ + x8_ - f30) / timeB.GetSeconds(); + ret->x8_ = -((x4_ * x10_ + x8_ - f30) / (timeA - timeB).GetSeconds() * timeB.GetSeconds() - f30); + ret->xc_ = timeB.GetSeconds(); + ret->x10_ = timeA.GetSeconds(); return std::shared_ptr(ret); } @@ -68,10 +68,11 @@ CLinearAnimationTimeScale::VGetFunctionMirrored(const float& parm) const CCharAnimTime timeB(2.f * parm - x10_); CLinearAnimationTimeScale* ret = new CLinearAnimationTimeScale(); - ret->x4_ = (-x4_ * 2.f * parm - xc_ + f27 - f31) / (timeA - timeB); - ret->x8_ = -(((-x4_ * 2.f * parm - xc_ + f27 - f31) / (timeA - timeB)) * timeB - f31); - ret->xc_ = timeB; - ret->x10_ = timeA; + ret->x4_ = (-x4_ * 2.f * parm - xc_ + f27 - f31) / (timeA - timeB).GetSeconds(); + ret->x8_ = -(((-x4_ * 2.f * parm - xc_ + f27 - f31) / + (timeA - timeB).GetSeconds()) * timeB.GetSeconds() - f31); + ret->xc_ = timeB.GetSeconds(); + ret->x10_ = timeA.GetSeconds(); return std::shared_ptr(ret); } diff --git a/Runtime/Character/CharacterCommon.hpp b/Runtime/Character/CharacterCommon.hpp index c51cf4f47..2892a165e 100644 --- a/Runtime/Character/CharacterCommon.hpp +++ b/Runtime/Character/CharacterCommon.hpp @@ -7,23 +7,80 @@ namespace pas { enum class ELocomotionType { + Invalid = -1, + Crouch, + Relaxed, + Lurk, + Combat, + Internal4, + Internal5, + Internal6, + Internal7, + Internal8, + Internal9, + Internal10, + Internal11, + Internal12, + Internal13, + Internal14 }; enum class ELocomotionAnim { + Invalid = -1, + Idle, + Walk, + Run, + BackUp, + StrafeLeft, + StrafeRight, + StrafeUp, + StrafeDown }; enum class EAnimationState { + Invalid = -1, + Fall, + Getup, + LieOnGround, + Step, + Death, + Locomotion, + KnockBack, + MeleeAttack, + Turn, + LoopAttack, + LoopReaction, + GroundHit, + Generate, + Jump, + Hurled, + Slide, + Taunt, + Scripted, + ProjectileAttack, + Cover, + WallHang }; -enum class EHurledType +enum class EHurledState { - + Invalid = -1, + KnockIntoAir, + KnockLoop, + KnockDown, + StrikeWall, + StrikeWallFallLoop, + OutOfStrikeWall, + Six, + Seven }; enum class EFallState { + Invalid = -1, + Zero }; enum class EReactionType @@ -38,16 +95,31 @@ enum class EAdditiveReactionType enum class EJumpType { - Zero + Normal, + One, + Ambush +}; + +enum class EJumpState +{ + Invalid = -1, + IntoJump, + AmbushJump, + Loop, + OutOfJump, + WallBounceLeft, + WallBounceRight }; enum class EStepDirection { Invalid = -1, - Up = 0, - Down = 1, + Forward = 0, + Backward = 1, Left = 2, - Right = 3 + Right = 3, + Up = 4, + Down = 5 }; enum class EStepType @@ -66,6 +138,14 @@ enum class EGetupType Invalid = -1 }; +enum class ELoopState +{ + Invalid = -1, + Begin, + Loop, + End +}; + enum class ELoopAttackType { Invalid = -1 @@ -86,25 +166,62 @@ enum class ETauntType Invalid = -1 }; +enum class ECoverState +{ + Invalid = -1, + IntoCover, + Cover, + Lean, + OutOfCover +}; + enum class ECoverDirection { - Invalid = -1 + Invalid = -1, + Left, + Right +}; + +enum class ETurnDirection +{ + Invalid = -1, + Right, + Left +}; + +enum class EWallHangState +{ + Invalid = -1, + IntoJump, + JumpArc, + JumpAirLoop, + IntoWallHang, + WallHang, + Five, + OutOfWallHang, + OutOfWallHangTurn, + DetachJumpLoop, + DetachOutOfJump }; } enum class EBodyType { - Zero, - One, - Two, - Three + Invalid, + BiPedal, + Restricted, + Flyer, + Pitchable, + RestrictedFlyer, + WallWalker, + NewFlyer }; enum class EBodyStateCmd { Getup, Step, - Two, + Die, KnockDown, KnockBack, MeleeAttack, @@ -112,9 +229,9 @@ enum class EBodyStateCmd LoopAttack, LoopReaction, LoopHitReaction, - Ten, - Eleven, - Twelve, + ExitState, + LeanFromCover, + NextState, Thirteen, Generate, Hurled, @@ -124,7 +241,7 @@ enum class EBodyStateCmd Scripted, Cover, WallHang, - TwentyTwo, + Locomotion, TwentyThree, AdditiveAim, AdditiveFlinch, diff --git a/Runtime/MP1/World/CBeetle.cpp b/Runtime/MP1/World/CBeetle.cpp index b682a2c9c..64dfd36d5 100644 --- a/Runtime/MP1/World/CBeetle.cpp +++ b/Runtime/MP1/World/CBeetle.cpp @@ -13,7 +13,7 @@ CBeetle::CBeetle(TUniqueId uid, const std::string& name, const CEntityInfo& info const CDamageVulnerability&, const zeus::CVector3f&, float, float, float, const CDamageVulnerability&, const CActorParameters& aParams, const rstl::optional_object) : CPatterned(ECharacter::Beetle, uid, name, flavor, info, xf, std::move(mData), pInfo, EMovementType::Ground, - EColliderType::One, EBodyType::One, aParams, bool(flavor)) + EColliderType::One, EBodyType::BiPedal, aParams, bool(flavor)) { } diff --git a/Runtime/MP1/World/CMetroid.cpp b/Runtime/MP1/World/CMetroid.cpp index 313fd7f36..2c57dc3bb 100644 --- a/Runtime/MP1/World/CMetroid.cpp +++ b/Runtime/MP1/World/CMetroid.cpp @@ -26,7 +26,7 @@ CMetroid::CMetroid(TUniqueId uid, const std::string& name, EFlavorType flavor, c const zeus::CTransform& xf, CModelData&& mData, const CPatternedInfo& pInfo, const CActorParameters& aParms, const CMetroidData& metroidData) : CPatterned(ECharacter::Metroid, uid, name, flavor, info, xf, std::move(mData), pInfo, - EMovementType::Flyer, EColliderType::One, EBodyType::Three, aParms, true) + EMovementType::Flyer, EColliderType::One, EBodyType::Flyer, aParms, true) { } diff --git a/Runtime/MP1/World/CMetroidBeta.cpp b/Runtime/MP1/World/CMetroidBeta.cpp index 76d1424be..eec431cfe 100644 --- a/Runtime/MP1/World/CMetroidBeta.cpp +++ b/Runtime/MP1/World/CMetroidBeta.cpp @@ -10,7 +10,7 @@ CMetroidBeta::CMetroidBeta(TUniqueId uid, const std::string& name, const CEntity const zeus::CTransform& xf, CModelData&& mData, const CPatternedInfo& pInfo, const CActorParameters& aParms, const CMetroidData& metroidData) : CPatterned(ECharacter::MetroidBeta, uid, name, EFlavorType::One, info, xf, std::move(mData), pInfo, - EMovementType::Flyer, EColliderType::One, EBodyType::Three, aParms, 2) + EMovementType::Flyer, EColliderType::One, EBodyType::Flyer, aParms, 2) { } diff --git a/Runtime/MP1/World/CNewIntroBoss.cpp b/Runtime/MP1/World/CNewIntroBoss.cpp index 3858cbacb..3fb50d8b4 100644 --- a/Runtime/MP1/World/CNewIntroBoss.cpp +++ b/Runtime/MP1/World/CNewIntroBoss.cpp @@ -12,7 +12,7 @@ CNewIntroBoss::CNewIntroBoss(TUniqueId uid, const std::string& name, const CEnti const CActorParameters& actParms, float, u32, const CDamageInfo& dInfo, u32, u32, u32, u32) : CPatterned(ECharacter::NewIntroBoss, uid, name, EFlavorType::Zero, info, xf, std::move(mData), pInfo, - EMovementType::Flyer, EColliderType::One, EBodyType::Two, actParms, true) + EMovementType::Flyer, EColliderType::One, EBodyType::Restricted, actParms, true) { } diff --git a/Runtime/MP1/World/CSpacePirate.cpp b/Runtime/MP1/World/CSpacePirate.cpp index 13f9ffc90..aa4bb0121 100644 --- a/Runtime/MP1/World/CSpacePirate.cpp +++ b/Runtime/MP1/World/CSpacePirate.cpp @@ -10,7 +10,7 @@ CSpacePirate::CSpacePirate(TUniqueId uid, const std::string& name, const CEntity CModelData&& mData, const CActorParameters& aParams, const CPatternedInfo& pInfo, CInputStream& in, u32 propCount) : CPatterned(ECharacter::SpacePirate, uid, name, EFlavorType::Zero, info, xf, std::move(mData), pInfo, EMovementType::Ground, - EColliderType::One, EBodyType::One, aParams, true) + EColliderType::One, EBodyType::BiPedal, aParams, true) { } diff --git a/Runtime/MP1/World/CThardusRockProjectile.cpp b/Runtime/MP1/World/CThardusRockProjectile.cpp index 1f6d96e4d..d9c2f07a0 100644 --- a/Runtime/MP1/World/CThardusRockProjectile.cpp +++ b/Runtime/MP1/World/CThardusRockProjectile.cpp @@ -8,7 +8,7 @@ CThardusRockProjectile::CThardusRockProjectile( const zeus::CTransform& xf, CModelData&& modelData, const CActorParameters& aParms, const CPatternedInfo& patternedInfo, const std::vector& mDataVec, u32) : CPatterned(ECharacter::ThardusRockProjectile, uid, name, EFlavorType::Zero, info, xf, std::move(modelData), - patternedInfo, EMovementType::Flyer, EColliderType::One, EBodyType::Three, aParms, true) + patternedInfo, EMovementType::Flyer, EColliderType::One, EBodyType::Flyer, aParms, true) { } diff --git a/Runtime/MP1/World/CWarWasp.cpp b/Runtime/MP1/World/CWarWasp.cpp index 33f5d6db7..9ff2b2e5a 100644 --- a/Runtime/MP1/World/CWarWasp.cpp +++ b/Runtime/MP1/World/CWarWasp.cpp @@ -11,7 +11,7 @@ CWarWasp::CWarWasp(TUniqueId uid, const std::string& name, const CEntityInfo& in CPatterned::EColliderType collider, const CDamageInfo& dInfo1, const CActorParameters& actorParms, ResId weapon, const CDamageInfo& dInfo2, ResId particle, u32 w3) : CPatterned(ECharacter::WarWasp, uid, name, flavor, info, xf, std::move(mData), pInfo, EMovementType::Flyer, collider, - EBodyType::Three, actorParms, false) + EBodyType::Flyer, actorParms, false) { } diff --git a/Runtime/World/CActor.cpp b/Runtime/World/CActor.cpp index 3a70a3775..f1a18bd78 100644 --- a/Runtime/World/CActor.cpp +++ b/Runtime/World/CActor.cpp @@ -504,4 +504,11 @@ void CActor::SetCalculateLighting(bool c) 4, 4, false, false, false, 0.1f); xe4_31_lightsDirty = c; } + +float CActor::GetAverageAnimVelocity(int anim) const +{ + if (HasModelData() && GetModelData()->HasAnimData()) + return GetModelData()->GetAnimationData()->GetAverageVelocity(anim); + return 0.f; +} } diff --git a/Runtime/World/CActor.hpp b/Runtime/World/CActor.hpp index f17bd4018..0a424983c 100644 --- a/Runtime/World/CActor.hpp +++ b/Runtime/World/CActor.hpp @@ -174,6 +174,7 @@ public: const CHealthInfo* GetHealthInfo() const { return const_cast(this)->HealthInfo(); } bool GetDoTargetDistanceTest() const { return xe7_30_doTargetDistanceTest; } void SetCalculateLighting(bool c); + float GetAverageAnimVelocity(int anim) const; }; } diff --git a/Runtime/World/CPatterned.hpp b/Runtime/World/CPatterned.hpp index f5ff50459..520fbf34f 100644 --- a/Runtime/World/CPatterned.hpp +++ b/Runtime/World/CPatterned.hpp @@ -70,6 +70,17 @@ public: }; private: + union + { + struct + { + bool x328_24_ : 1; + bool x328_25_ : 1; + bool x328_26_ : 1; + bool x328_27_onGround : 1; + }; + u32 _dummy = 0; + }; ECharacter x34c_character; public: CPatterned(ECharacter character, TUniqueId uid, const std::string& name, EFlavorType flavor, const CEntityInfo& info, @@ -97,6 +108,11 @@ public: return static_cast(patterned.GetPtr()); return nullptr; } + + bool GetX328_26() const { return x328_26_; } + + virtual bool IsOnGround() const { return x328_27_onGround; } + virtual float GetGravityConstant() const { return 24.525002f; } }; } diff --git a/Runtime/World/CPhysicsActor.hpp b/Runtime/World/CPhysicsActor.hpp index 89783900f..8bfe55c0a 100644 --- a/Runtime/World/CPhysicsActor.hpp +++ b/Runtime/World/CPhysicsActor.hpp @@ -159,6 +159,8 @@ public: void SetVelocityWR(const zeus::CVector3f& vel); void SetVelocityOR(const zeus::CVector3f& vel); void SetMomentumWR(const zeus::CVector3f& m) { x150_momentum = m; } + void SetConstantForce(const zeus::CVector3f& f) { xfc_constantForce = f; } + void SetAngularMomentum(const zeus::CAxisAngle& m) { x108_angularMomentum = m; } const zeus::CVector3f& GetMomentum() const { return x150_momentum; } const zeus::CVector3f& GetVelocity() const { return x138_velocity; } zeus::CVector3f GetTotalForcesWR() const; diff --git a/Runtime/World/CScriptWaypoint.hpp b/Runtime/World/CScriptWaypoint.hpp index 25165fba6..b41d9a3f9 100644 --- a/Runtime/World/CScriptWaypoint.hpp +++ b/Runtime/World/CScriptWaypoint.hpp @@ -8,6 +8,7 @@ namespace urde class CScriptWaypoint : public CActor { + u16 xfa_jumpFlags; public: CScriptWaypoint(TUniqueId, const std::string&, const CEntityInfo&, const zeus::CTransform&, bool, float, float, diff --git a/specter b/specter index c35ccdd3c..34bdfdcad 160000 --- a/specter +++ b/specter @@ -1 +1 @@ -Subproject commit c35ccdd3cd7517e4e277d02e336997b519d00162 +Subproject commit 34bdfdcad75e16e877e1962478ff195372df82a3