Work on CPlayer

This commit is contained in:
Jack Andersen 2017-07-15 20:13:37 -10:00
parent e23a61876e
commit 6c257e1811
21 changed files with 697 additions and 63 deletions

View File

@ -5,6 +5,9 @@
<value>
<Objective-C>
<option name="INDENT_NAMESPACE_MEMBERS" value="0" />
<option name="NAMESPACE_BRACE_PLACEMENT" value="5" />
<option name="FUNCTION_BRACE_PLACEMENT" value="5" />
<option name="BLOCK_BRACE_PLACEMENT" value="5" />
<option name="SPACE_WITHIN_TEMPLATE_DOUBLE_GT" value="false" />
<option name="SPACE_BEFORE_POINTER_IN_DECLARATION" value="false" />
<option name="SPACE_AFTER_POINTER_IN_DECLARATION" value="true" />
@ -37,6 +40,9 @@
</extensions>
</Objective-C-extensions>
<codeStyleSettings language="ObjectiveC">
<option name="BRACE_STYLE" value="5" />
<option name="CLASS_BRACE_STYLE" value="5" />
<option name="INDENT_CASE_FROM_SWITCH" value="false" />
<indentOptions>
<option name="CONTINUATION_INDENT_SIZE" value="4" />
</indentOptions>

View File

@ -7,7 +7,9 @@ namespace DataSpec
{
struct ITweakBall : ITweak
{
virtual float GetBallCameraControlDistance() const=0;
virtual float GetLeftStickDivisor() const=0;
virtual float GetRightStickDivisor() const=0;
};
}

View File

@ -49,6 +49,7 @@ struct ITweakPlayer : ITweak
virtual float GetLeftLogicalThreshold() const=0;
virtual float GetRightLogicalThreshold() const=0;
virtual float GetX164(int type) const=0;
virtual uint32_t GetIceBreakPressCount() const=0;
virtual float GetVariaDamageReduction() const=0;
virtual float GetGravityDamageReduction() const=0;
virtual float GetPhazonDamageReduction() const=0;

View File

@ -192,15 +192,15 @@ void CTweakBall::read(athena::io::IStreamReader& __dna_reader)
/* x1cc_ */
x1cc_ = __dna_reader.readFloatBig();
/* x1d0_ */
x1d0_ = __dna_reader.readFloatBig();
x1d0_ballCameraControlDistance = __dna_reader.readFloatBig();
/* x1d4_ */
x1d4_ = __dna_reader.readFloatBig();
/* x1d8_ */
x1d8_ = __dna_reader.readFloatBig();
/* x1e4_ */
x1e4_ = __dna_reader.readFloatBig();
x1e4_leftStickDivisor = __dna_reader.readFloatBig();
/* x1e8_ */
x1e8_ = __dna_reader.readFloatBig();
x1e8_rightStickDivisor = __dna_reader.readFloatBig();
/* x200_ */
x200_ = __dna_reader.readFloatBig();
/* x204_ */
@ -412,15 +412,15 @@ void CTweakBall::write(athena::io::IStreamWriter& __dna_writer) const
/* x1cc_ */
__dna_writer.writeFloatBig(x1cc_);
/* x1d0_ */
__dna_writer.writeFloatBig(x1d0_);
__dna_writer.writeFloatBig(x1d0_ballCameraControlDistance);
/* x1d4_ */
__dna_writer.writeFloatBig(x1d4_);
/* x1d8_ */
__dna_writer.writeFloatBig(x1d8_);
/* x1e4_ */
__dna_writer.writeFloatBig(x1e4_);
__dna_writer.writeFloatBig(x1e4_leftStickDivisor);
/* x1e8_ */
__dna_writer.writeFloatBig(x1e8_);
__dna_writer.writeFloatBig(x1e8_rightStickDivisor);
/* x200_ */
__dna_writer.writeFloatBig(x200_);
/* x204_ */
@ -652,15 +652,15 @@ void CTweakBall::read(athena::io::YAMLDocReader& __dna_docin)
/* x1cc_ */
x1cc_ = __dna_docin.readFloat("x1cc_");
/* x1d0_ */
x1d0_ = __dna_docin.readFloat("x1d0_");
x1d0_ballCameraControlDistance = __dna_docin.readFloat("x1d0_");
/* x1d4_ */
x1d4_ = __dna_docin.readFloat("x1d4_");
/* x1d8_ */
x1d8_ = __dna_docin.readFloat("x1d8_");
/* x1e4_ */
x1e4_ = __dna_docin.readFloat("x1e4_");
x1e4_leftStickDivisor = __dna_docin.readFloat("x1e4_");
/* x1e8_ */
x1e8_ = __dna_docin.readFloat("x1e8_");
x1e8_rightStickDivisor = __dna_docin.readFloat("x1e8_");
/* x200_ */
x200_ = __dna_docin.readFloat("x200_");
/* x204_ */
@ -888,15 +888,15 @@ void CTweakBall::write(athena::io::YAMLDocWriter& __dna_docout) const
/* x1cc_ */
__dna_docout.writeFloat("x1cc_", x1cc_);
/* x1d0_ */
__dna_docout.writeFloat("x1d0_", x1d0_);
__dna_docout.writeFloat("x1d0_", x1d0_ballCameraControlDistance);
/* x1d4_ */
__dna_docout.writeFloat("x1d4_", x1d4_);
/* x1d8_ */
__dna_docout.writeFloat("x1d8_", x1d8_);
/* x1e4_ */
__dna_docout.writeFloat("x1e4_", x1e4_);
__dna_docout.writeFloat("x1e4_", x1e4_leftStickDivisor);
/* x1e8_ */
__dna_docout.writeFloat("x1e8_", x1e8_);
__dna_docout.writeFloat("x1e8_", x1e8_rightStickDivisor);
/* x200_ */
__dna_docout.writeFloat("x200_", x200_);
/* x204_ */

View File

@ -93,7 +93,7 @@ struct CTweakBall : public ITweakBall
Value<float> x1c4_;
Value<float> x1c8_;
Value<float> x1cc_;
Value<float> x1d0_;
Value<float> x1d0_ballCameraControlDistance;
Value<float> x1d4_;
Value<float> x1d8_;
Value<float> x1dc_;
@ -103,8 +103,8 @@ struct CTweakBall : public ITweakBall
Value<float> x1f4_;
Value<float> x1f8_;
Value<float> x1fc_;
Value<float> x1e4_;
Value<float> x1e8_;
Value<float> x1e4_leftStickDivisor;
Value<float> x1e8_rightStickDivisor;
Value<float> x200_;
Value<float> x204_;
float x208_;
@ -136,6 +136,10 @@ struct CTweakBall : public ITweakBall
x1b4_ = zeus::degToRad(x1b4_);
x1ec_ = zeus::degToRad(x1ec_);
}
float GetBallCameraControlDistance() const { return x1d0_ballCameraControlDistance; }
float GetLeftStickDivisor() const { return x1e4_leftStickDivisor; }
float GetRightStickDivisor() const { return x1e8_rightStickDivisor; }
};
}
}

View File

@ -436,7 +436,7 @@ void CTweakPlayer::read(athena::io::IStreamReader& __dna_reader)
/* x2f8_ */
x2f8_ = __dna_reader.readFloatBig();
/* x2fc_ */
x2fc_ = __dna_reader.readUint32Big();
x2fc_iceBreakPressCount = __dna_reader.readUint32Big();
/* x300_variaDamageReduction */
x300_variaDamageReduction = __dna_reader.readFloatBig();
/* x304_gravityDamageReduction */
@ -876,7 +876,7 @@ void CTweakPlayer::write(athena::io::IStreamWriter& __dna_writer) const
/* x2f8_ */
__dna_writer.writeFloatBig(x2f8_);
/* x2fc_ */
__dna_writer.writeUint32Big(x2fc_);
__dna_writer.writeUint32Big(x2fc_iceBreakPressCount);
/* x300_variaDamageReduction */
__dna_writer.writeFloatBig(x300_variaDamageReduction);
/* x304_gravityDamageReduction */
@ -1390,8 +1390,8 @@ void CTweakPlayer::read(athena::io::YAMLDocReader& __dna_docin)
x2f4_ = __dna_docin.readBool("x2f4_");
/* x2f8_ */
x2f8_ = __dna_docin.readFloat("x2f8_");
/* x2fc_ */
x2fc_ = __dna_docin.readUint32("x2fc_");
/* x2fc_iceBreakPressCount */
x2fc_iceBreakPressCount = __dna_docin.readUint32("x2fc_iceBreakPressCount");
/* x300_variaDamageReduction */
x300_variaDamageReduction = __dna_docin.readFloat("x300_variaDamageReduction");
/* x304_gravityDamageReduction */
@ -1890,8 +1890,8 @@ void CTweakPlayer::CTweakPlayer::write(athena::io::YAMLDocWriter& __dna_docout)
__dna_docout.writeBool("x2f4_", x2f4_);
/* x2f8_ */
__dna_docout.writeFloat("x2f8_", x2f8_);
/* x2fc_ */
__dna_docout.writeUint32("x2fc_", x2fc_);
/* x2fc_iceBreakPressCount */
__dna_docout.writeUint32("x2fc_iceBreakPressCount", x2fc_iceBreakPressCount);
/* x300_variaDamageReduction */
__dna_docout.writeFloat("x300_variaDamageReduction", x300_variaDamageReduction);
/* x304_gravityDamageReduction */

View File

@ -171,7 +171,7 @@ struct CTweakPlayer : ITweakPlayer
Value<float> x2f0_;
Value<bool> x2f4_;
Value<float> x2f8_;
Value<atUint32> x2fc_;
Value<atUint32> x2fc_iceBreakPressCount;
Value<float> x300_variaDamageReduction;
Value<float> x304_gravityDamageReduction;
Value<float> x308_phazonDamageReduction;
@ -215,6 +215,7 @@ struct CTweakPlayer : ITweakPlayer
float GetLeftLogicalThreshold() const { return x150_leftDiv; }
float GetRightLogicalThreshold() const { return x154_rightDiv; }
float GetX164(int type) const { return x164_[type]; }
uint32_t GetIceBreakPressCount() const { return x2fc_iceBreakPressCount; }
float GetVariaDamageReduction() const { return x300_variaDamageReduction; }
float GetGravityDamageReduction() const { return x304_gravityDamageReduction; }
float GetPhazonDamageReduction() const { return x308_phazonDamageReduction; }

View File

@ -65,8 +65,8 @@ CPersistentOptions::CPersistentOptions(CBitStreamReader& stream)
for (int b=0 ; b<64 ; ++b)
x68_[b] = stream.ReadEncoded(1);
xc0_ = stream.ReadEncoded(2);
xc4_freezeBreakCount = stream.ReadEncoded(2);
xc0_freezeBreakCount = stream.ReadEncoded(2);
xc4_frozenCount = stream.ReadEncoded(2);
xc8_ = stream.ReadEncoded(1);
xcc_logScanCount = stream.ReadEncoded(7);
xd0_24_fusionLinked = stream.ReadEncoded(1);
@ -111,8 +111,8 @@ void CPersistentOptions::PutTo(CBitStreamWriter& w) const
for (int b=0 ; b<64 ; ++b)
w.WriteEncoded(x68_[b], 1);
w.WriteEncoded(xc0_, 2);
w.WriteEncoded(xc4_freezeBreakCount, 2);
w.WriteEncoded(xc0_freezeBreakCount, 2);
w.WriteEncoded(xc4_frozenCount, 2);
w.WriteEncoded(xc8_, 1);
w.WriteEncoded(xcc_logScanCount, 7);
w.WriteEncoded(xd0_24_fusionLinked, 1);

View File

@ -59,8 +59,8 @@ class CPersistentOptions
bool x68_[64] = {};
std::vector<std::pair<ResId, TEditorId>> xac_cinematicStates; /* (MLVL, Cinematic) */
u32 xbc_autoMapperKeyState = 0;
u32 xc0_ = 0;
u32 xc4_freezeBreakCount = 0;
u32 xc0_freezeBreakCount = 0;
u32 xc4_frozenCount = 0;
u32 xc8_ = 0;
u32 xcc_logScanCount = 0;
@ -100,8 +100,10 @@ public:
void SetAllItemsCollected(bool v) { xd0_29_allItemsCollected = v; }
u32 GetLogScanCount() const { return xcc_logScanCount; }
void SetLogScanCount(u32 v) { xcc_logScanCount = v; }
void IncrFreezeBreakCount() { xc4_freezeBreakCount = std::min(int(xc4_freezeBreakCount + 1), 3); }
bool GetShowFrozenMessage() const { return xc4_freezeBreakCount != 3; }
void IncrFreezeBreakCount() { xc0_freezeBreakCount = std::min(int(xc0_freezeBreakCount + 1), 3); }
bool GetShowBreakMessage() const { return xc0_freezeBreakCount != 3; }
void IncrFrozenCount() { xc4_frozenCount = std::min(int(xc4_frozenCount + 1), 3); }
bool GetShowFrozenMessage() const { return xc4_frozenCount != 3; }
void PutTo(CBitStreamWriter& w) const;
u8* GetNESState() { return x0_; }

View File

@ -1362,7 +1362,7 @@ void CStateManager::ApplyDamageToWorld(TUniqueId damager, const CActor& actor, c
{
if (player->GetFrozenState())
{
g_GameState->SystemOptions().IncrFreezeBreakCount();
g_GameState->SystemOptions().IncrFrozenCount();
CHUDMemoParms info = {0.f, true, true, true};
MP1::CSamusHud::DisplayHudMemo(u"", info);
player->Stop(*this);

View File

@ -245,4 +245,14 @@ CFinalInput& CFinalInput::operator|=(const CFinalInput& other)
x2e_b31_PStart |= other.x2e_b31_PStart;
return *this;
}
CFinalInput CFinalInput::ScaleAnalogueSticks(float leftDiv, float rightDiv) const
{
CFinalInput ret = *this;
ret.x8_anaLeftX = zeus::clamp(-1.f, x8_anaLeftX / leftDiv, 1.f);
ret.xc_anaLeftY = zeus::clamp(-1.f, xc_anaLeftY / leftDiv, 1.f);
ret.x10_anaRightX = zeus::clamp(-1.f, x10_anaRightX / rightDiv, 1.f);
ret.x14_anaRightY = zeus::clamp(-1.f, x14_anaRightY / rightDiv, 1.f);
return ret;
}
}

View File

@ -151,6 +151,8 @@ public:
float ARightY() const {return x14_anaRightY;}
float ALeftTrigger() const {return x18_anaLeftTrigger;}
float ARightTrigger() const {return x1c_anaRightTrigger;}
CFinalInput ScaleAnalogueSticks(float leftDiv, float rightDiv) const;
};
}

View File

@ -245,6 +245,7 @@ public:
FourCC Get4CharId() const { return FOURCC('PART'); }
size_t GetNumActiveChildParticles() const { return x290_activePartChildren.size(); }
CParticleGen& GetActiveChildParticle(size_t idx) const { return *x290_activePartChildren[idx]; }
bool IsIndirectTextured() const { return x28_loadedGenDesc->x54_x40_TEXR && x28_loadedGenDesc->x58_x44_TIND; }
static void SetMoveRedToAlphaBuffer(bool);
};

View File

@ -46,4 +46,9 @@ void CPlayerGun::DamageRumble(const zeus::CVector3f& location, float damage, con
x3dc_damageLocation = location;
}
void CPlayerGun::ProcessInput(const CFinalInput& input, CStateManager& mgr)
{
}
}

View File

@ -22,6 +22,7 @@
namespace urde
{
class CFinalInput;
class CPlayerGun
{
@ -222,6 +223,7 @@ public:
void SetX3e8(const zeus::CTransform& xf) { x3e8_ = xf; }
CGrappleArm& GetGrappleArm() { return *x740_grappleArm; }
void DamageRumble(const zeus::CVector3f& location, float damage, const CStateManager& mgr);
void ProcessInput(const CFinalInput& input, CStateManager& mgr);
};
}

View File

@ -276,11 +276,11 @@ bool CActor::HasModelData() const { return bool(x64_modelData); }
void CActor::SetSoundEventPitchBend(s32 val)
{
xe6_30_enablePitchBend = true;
xc0_pitchBend = val / 8192.f;
xc0_pitchBend = val / 8192.f - 1.f;
if (!x8c_loopingSfxHandle)
return;
CSfxManager::PitchBend(x8c_loopingSfxHandle, val);
CSfxManager::PitchBend(x8c_loopingSfxHandle, xc0_pitchBend);
}
void CActor::SetRotation(const zeus::CQuaternion &q)

View File

@ -0,0 +1,63 @@
#include "CHUDBillboardEffect.hpp"
#include "TCastTo.hpp"
#include "CStateManager.hpp"
#include "Camera/CGameCamera.hpp"
namespace urde
{
u32 CHUDBillboardEffect::g_IndirectTexturedBillboardCount = 0;
u32 CHUDBillboardEffect::g_BillboardCount = 0;
CHUDBillboardEffect::CHUDBillboardEffect(const std::experimental::optional<TToken<CGenDescription>>& particle,
const std::experimental::optional<TToken<CElectricDescription>>& electric,
TUniqueId uid, bool active, const std::string& name, float f,
const zeus::CVector3f& v0, const zeus::CColor& color,
const zeus::CVector3f& v1, const zeus::CVector3f& v2)
: CEffect(uid, CEntityInfo(kInvalidAreaId, CEntity::NullConnectionList), active, name, zeus::CTransform::Identity())
{
xec_v2 = v2;
xec_v2.y += f;
xf8_ = v1 * v0;
x104_24_ = true;
x104_25_ = false;
x104_26_ = false;
x104_27_ = false;
if (particle)
{
xe8_generator = std::make_unique<CElementGen>(*particle, CElementGen::EModelOrientationType::Normal,
CElementGen::EOptionalSystemFlags::One);
if (static_cast<CElementGen&>(*xe8_generator).IsIndirectTextured())
++g_IndirectTexturedBillboardCount;
}
else
{
xe8_generator = std::make_unique<CParticleElectric>(*electric);
}
++g_BillboardCount;
xe8_generator->SetModulationColor(color);
xe8_generator->SetLocalScale(xf8_);
}
CHUDBillboardEffect::~CHUDBillboardEffect()
{
--g_BillboardCount;
if (xe8_generator->Get4CharId() == FOURCC('PART'))
if (static_cast<CElementGen&>(*xe8_generator).IsIndirectTextured())
--g_IndirectTexturedBillboardCount;
}
void CHUDBillboardEffect::Accept(IVisitor& visitor) { visitor.Visit(this); }
float CHUDBillboardEffect::GetNearClipDistance(CStateManager& mgr)
{
return mgr.GetCameraManager()->GetCurrentCamera(mgr)->GetNearClipDistance() + 0.01f;
}
zeus::CVector3f CHUDBillboardEffect::GetScaleForPOV(CStateManager& mgr)
{
return {0.155f, 1.f, 0.155f};
}
}

View File

@ -0,0 +1,42 @@
#ifndef __URDE_CHUDBILLBOARDEFFECT_HPP__
#define __URDE_CHUDBILLBOARDEFFECT_HPP__
#include "RetroTypes.hpp"
#include "World/CEffect.hpp"
#include "CToken.hpp"
#include "Particle/CElementGen.hpp"
#include "Particle/CParticleElectric.hpp"
#include "Particle/CParticleSwoosh.hpp"
namespace urde
{
class CGenDescription;
class CElectricDescription;
class CHUDBillboardEffect : public CEffect
{
std::unique_ptr<CParticleGen> xe8_generator;
zeus::CVector3f xec_v2;
zeus::CVector3f xf8_;
bool x104_24_ : 1;
bool x104_25_ : 1;
bool x104_26_ : 1;
bool x104_27_ : 1;
float x108_ = 0.f;
static u32 g_IndirectTexturedBillboardCount;
static u32 g_BillboardCount;
public:
CHUDBillboardEffect(const std::experimental::optional<TToken<CGenDescription>>& particle,
const std::experimental::optional<TToken<CElectricDescription>>& electric,
TUniqueId uid, bool active, const std::string& name, float, const zeus::CVector3f& v0,
const zeus::CColor& color, const zeus::CVector3f& v1, const zeus::CVector3f& v2);
~CHUDBillboardEffect();
void Accept(IVisitor& visitor);
static float GetNearClipDistance(CStateManager& mgr);
static zeus::CVector3f GetScaleForPOV(CStateManager& mgr);
};
}
#endif // __URDE_CHUDBILLBOARDEFFECT_HPP__

View File

@ -43,7 +43,7 @@ public:
bool InSpiderBallMode() const { return false; }
zeus::CVector3f GetBallContactSurfaceNormal() const { return {}; }
void GetModel() const {}
CCollidableSphere* GetCollidableSphere() const { return nullptr; }
const CCollidableSphere* GetCollidableSphere() const { return nullptr; }
bool IsProjectile() const { return false; }
void GetBallContactMeterials() const {}
void GetWallBumpCounter() const {}
@ -138,6 +138,7 @@ public:
void StartLandingSfx() {}
bool GetX187c() const { return x187c_; }
void SetDamageTimer(float t) { x191c_damageTimer = t; }
void Stop() {}
};
}

View File

@ -18,6 +18,12 @@
#include "Weapon/CEnergyProjectile.hpp"
#include "MP1/World/CThardusRockProjectile.hpp"
#include "MP1/World/CMetroidBeta.hpp"
#include "Collision/CMetroidAreaCollider.hpp"
#include "Collision/CGameCollision.hpp"
#include "Input/ControlMapper.hpp"
#include "CGameState.hpp"
#include "MP1/CSamusHud.hpp"
#include "CHUDBillboardEffect.hpp"
namespace urde
{
@ -36,7 +42,7 @@ CPlayer::CPlayer(TUniqueId uid, const zeus::CTransform& xf, const zeus::CAABox&
{
x490_gun.reset(new CPlayerGun(uid));
x49c_gunNotFiringTimeout = g_tweakPlayerGun->GetGunNotFiringTime();
x4a0_inputFilter.reset(new CInputFilter());
x4a0_failsafeTest.reset(new CFailsafeTest());
x76c_cameraBob.reset(new CPlayerCameraBob(CPlayerCameraBob::ECameraBobType::One,
zeus::CVector2f{CPlayerCameraBob::kCameraBobExtentX,
CPlayerCameraBob::kCameraBobExtentY},
@ -428,6 +434,8 @@ void CPlayer::AddToRenderer(const zeus::CFrustum&, const CStateManager&) {}
void CPlayer::ComputeFreeLook(const CFinalInput& input) {}
void CPlayer::UpdateFreeLookState(const CFinalInput&, float dt, CStateManager&) {}
void CPlayer::UpdateFreeLook(float dt) {}
float CPlayer::GetMaximumPlayerPositiveVerticalVelocity(CStateManager&) const { return 0.f; }
@ -466,14 +474,416 @@ void CPlayer::ProcessFrozenInput(float dt, CStateManager& mgr)
}
}
void CPlayer::ProcessInput(const CFinalInput&, CStateManager&) {}
void CPlayer::ProcessInput(const CFinalInput& input, CStateManager& mgr)
{
if (input.ControllerIdx() != 0)
return;
if (x2f8_morphTransState != EPlayerMorphBallState::Morphed)
UpdateScanningState(input, mgr, input.DeltaTime());
if (mgr.GetGameState() != CStateManager::EGameState::Running)
return;
if (!mgr.GetPlayerState()->IsPlayerAlive())
return;
if (GetFrozenState())
UpdateFrozenState(input, mgr);
if (GetFrozenState())
{
if (x258_movementState == EPlayerMovementState::OnGround ||
x258_movementState == EPlayerMovementState::FallingMorphed)
return;
CFinalInput dummyInput;
if (x2f8_morphTransState == EPlayerMorphBallState::Morphed)
{
x768_morphball->ComputeBallMovement(dummyInput, mgr, input.DeltaTime());
x768_morphball->UpdateBallDynamics(mgr, input.DeltaTime());
}
else
{
ComputeMovement(dummyInput, mgr, input.DeltaTime());
}
return;
}
if (x760_controlsFrozen)
{
ProcessFrozenInput(input.DeltaTime(), mgr);
return;
}
if (x2f8_morphTransState == EPlayerMorphBallState::Unmorphed && x4a0_failsafeTest->Passes())
{
const CCollidableAABox* prim = static_cast<const CCollidableAABox*>(GetCollisionPrimitive());
zeus::CAABox tmpAABB(prim->GetAABB().min - 0.2f, prim->GetAABB().max + 0.2f);
CCollidableAABox tmpBox(tmpAABB, prim->GetMaterial());
CPhysicsActor::Stop();
zeus::CAABox testBounds = prim->GetAABB().getTransformedAABox(x34_transform);
zeus::CAABox expandedBounds(testBounds.min - 3.f, testBounds.max + 3.f);
CAreaCollisionCache cache(expandedBounds);
CGameCollision::BuildAreaCollisionCache(mgr, cache);
rstl::reserved_vector<TUniqueId, 1024> nearList;
mgr.BuildColliderList(nearList, *this, expandedBounds);
std::experimental::optional<zeus::CVector3f> nonIntVec =
CGameCollision::FindNonIntersectingVector(mgr, cache, *this, *prim, nearList);
if (nonIntVec)
{
x4a0_failsafeTest->Reset();
SetTranslation(GetTranslation() + *nonIntVec);
}
}
UpdateGrappleState(input, mgr);
if (x2f8_morphTransState == EPlayerMorphBallState::Morphed)
{
float leftDiv = g_tweakBall->GetLeftStickDivisor();
float rightDiv = g_tweakBall->GetRightStickDivisor();
if (x26c_ != kInvalidUniqueId || IsUnderBetaMetroidAttack(mgr))
leftDiv = 2.f;
CFinalInput scaledInput = input.ScaleAnalogueSticks(leftDiv, rightDiv);
x768_morphball->ComputeBallMovement(scaledInput, mgr, input.DeltaTime());
x768_morphball->UpdateBallDynamics(mgr, input.DeltaTime());
x4a0_failsafeTest->Reset();
}
else
{
if (x304_orbitState == EPlayerOrbitState::Five)
{
ApplyGrappleForces(input, mgr, input.DeltaTime());
}
else
{
CFinalInput scaledInput = input.ScaleAnalogueSticks(IsUnderBetaMetroidAttack(mgr) ? 3.f : 1.f, 1.f);
ComputeMovement(scaledInput, mgr, input.DeltaTime());
}
if (ShouldSampleFailsafe(mgr))
{
CFailsafeTest::EInputState inputState = CFailsafeTest::EInputState::Moving;
if (x258_movementState == EPlayerMovementState::StartingJump)
inputState = CFailsafeTest::EInputState::StartingJump;
else if (x258_movementState == EPlayerMovementState::Jump)
inputState = CFailsafeTest::EInputState::Jump;
x4a0_failsafeTest->AddSample(inputState, GetTranslation(), x138_velocity,
zeus::CVector2f(input.ALeftX(), input.ALeftY()));
}
}
ComputeFreeLook(input);
UpdateFreeLookState(input, input.DeltaTime(), mgr);
UpdateOrbitInput(input, mgr);
UpdateOrbitZone(mgr);
UpdateGunState(input, mgr);
UpdateVisorState(input, input.DeltaTime(), mgr);
if (x2f8_morphTransState == EPlayerMorphBallState::Morphed ||
(x2f8_morphTransState == EPlayerMorphBallState::Unmorphed && x498_ == 2))
{
x490_gun->ProcessInput(input, mgr);
if (x2f8_morphTransState == EPlayerMorphBallState::Morphed && x2fc_ != kInvalidUniqueId)
{
if (ControlMapper::GetPressInput(ControlMapper::ECommands::TurnLeft, input) ||
ControlMapper::GetPressInput(ControlMapper::ECommands::TurnRight, input) ||
ControlMapper::GetPressInput(ControlMapper::ECommands::Forward, input) ||
ControlMapper::GetPressInput(ControlMapper::ECommands::Backward, input) ||
ControlMapper::GetPressInput(ControlMapper::ECommands::JumpOrBoost, input))
{
xa28_ += input.DeltaTime() * 600.f * input.DeltaTime();
if (xa28_ > 1.f)
xa28_ = 1.f;
}
else
{
float tmp = 7.5f * input.DeltaTime();
xa28_ -= input.DeltaTime() * std::min(1.f, xa28_ * tmp + tmp);
if (xa28_ < 0.f)
xa28_ = 0.f;
}
}
}
UpdateCameraState(mgr);
UpdateMorphBallState(input, mgr);
UpdateCameraTimers(input);
UpdateFootstepSounds(input, mgr, input.DeltaTime());
x2a8_ += input.DeltaTime();
if (CheckSubmerged())
SetSoundEventPitchBend(0);
else
SetSoundEventPitchBend(8192);
CalculateLeaveMorphBallDirection(input);
}
bool CPlayer::ShouldSampleFailsafe(CStateManager& mgr) const
{
TCastToPtr<CCinematicCamera> cineCam = mgr.GetCameraManager()->GetCurrentCamera(mgr);
if (!mgr.GetPlayerState()->IsPlayerAlive())
return false;
return x2f4_cameraState != EPlayerCameraState::Four || !cineCam || !(cineCam->GetW1() & 0x80);
}
void CPlayer::CalculateLeaveMorphBallDirection(const CFinalInput& input)
{
if (x2f8_morphTransState != EPlayerMorphBallState::Morphed)
{
x518_leaveMorphDir = x50c_;
}
else
{
if (ControlMapper::GetAnalogInput(ControlMapper::ECommands::Forward, input) > 0.3f ||
ControlMapper::GetAnalogInput(ControlMapper::ECommands::Backward, input) > 0.3f ||
ControlMapper::GetAnalogInput(ControlMapper::ECommands::TurnLeft, input) > 0.3f ||
ControlMapper::GetAnalogInput(ControlMapper::ECommands::TurnRight, input) > 0.3f)
{
if (x138_velocity.magnitude() > 0.5f)
x518_leaveMorphDir = x50c_;
}
}
}
void CPlayer::CalculatePlayerControlDirection(CStateManager& mgr)
{
if (x9c4_30_)
{
if (x9d8_.canBeNormalized())
{
x540_ = x9d8_.normalized();
x54c_ = x9d8_;
x54c_.z = 0.f;
if (x54c_.canBeNormalized())
x54c_.normalize();
else
x540_ = x54c_ = zeus::CVector3f::skForward;
}
else
{
x540_ = x54c_ = zeus::CVector3f::skForward;
}
}
else
{
zeus::CVector3f camToPlayer =
GetTranslation() - mgr.GetCameraManager()->GetCurrentCamera(mgr)->GetTranslation();
if (!camToPlayer.canBeNormalized())
{
x540_ = x54c_ = zeus::CVector3f::skForward;
}
else
{
zeus::CVector3f camToPlayerFlat(camToPlayer.x, camToPlayer.y, 0.f);
if (camToPlayerFlat.canBeNormalized())
{
if (camToPlayerFlat.magnitude() > g_tweakBall->GetBallCameraControlDistance())
{
x540_ = camToPlayer.normalized();
if (camToPlayerFlat.canBeNormalized())
{
camToPlayerFlat.normalize();
switch (x2f8_morphTransState)
{
case EPlayerMorphBallState::Morphed:
x54c_ = camToPlayerFlat;
break;
default:
x540_ = GetTransform().basis[1];
x54c_ = x540_;
x54c_.z = 0.f;
if (x54c_.canBeNormalized())
x54c_.normalize();
}
}
else if (x2f8_morphTransState != EPlayerMorphBallState::Morphed)
{
x540_ = GetTransform().basis[1];
x54c_.z = 0.f;
if (x54c_.canBeNormalized())
x54c_.normalize();
}
}
else
{
if (x4fc_ < 0.25f)
{
x540_ = camToPlayer;
x54c_ = camToPlayerFlat;
}
else if (x2f8_morphTransState != EPlayerMorphBallState::Morphed)
{
x540_ = GetTransform().basis[1];
x54c_.z = 0.f;
if (x54c_.canBeNormalized())
x54c_.normalize();
}
}
}
}
}
}
void CPlayer::CalculatePlayerMovementDirection(float dt)
{
if (x2f8_morphTransState == EPlayerMorphBallState::Morphing ||
x2f8_morphTransState == EPlayerMorphBallState::Unmorphing)
return;
zeus::CVector3f delta = GetTranslation() - x524_;
if (delta.canBeNormalized() && delta.magnitude() > 0.02f)
{
x53c_ += dt;
x4f8_ = std::fabs(delta.magnitude() / dt);
x500_ = delta.normalized();
zeus::CVector3f flatDelta(delta.x, delta.y, 0.f);
if (flatDelta.canBeNormalized())
{
x4fc_ = std::fabs(flatDelta.magnitude() / dt);
flatDelta.normalize();
switch (x2f8_morphTransState)
{
case EPlayerMorphBallState::Morphed:
if (x4fc_ > 0.25f)
x50c_ = flatDelta;
x530_ = x50c_;
x524_ = GetTranslation();
break;
default:
x500_ = GetTransform().basis[1];
x50c_ = x500_;
x50c_.z = 0.f;
if (x50c_.canBeNormalized())
x50c_.normalize();
x530_ = x50c_;
x524_ = GetTranslation();
break;
}
}
else
{
if (x2f8_morphTransState != EPlayerMorphBallState::Morphed)
{
x500_ = GetTransform().basis[1];
x50c_ = x500_;
x50c_.z = 0.f;
if (x50c_.canBeNormalized())
x50c_.normalize();
x530_ = x50c_;
x524_ = GetTranslation();
}
x4fc_ = 0.f;
}
}
else
{
x53c_ = 0.f;
switch (x2f8_morphTransState)
{
case EPlayerMorphBallState::Morphed:
case EPlayerMorphBallState::Morphing:
case EPlayerMorphBallState::Unmorphing:
x500_ = x50c_;
break;
default:
x500_ = GetTransform().basis[1];
x50c_ = x500_;
x50c_.z = 0.f;
if (x50c_.canBeNormalized())
x50c_.normalize();
x530_ = x50c_;
x524_ = GetTranslation();
break;
}
x4f8_ = 0.f;
x4fc_ = 0.f;
}
x50c_.z = 0.f;
if (x50c_.canBeNormalized())
x500_.normalize();
}
void CPlayer::Stop(CStateManager& stateMgr)
{
if (GetFrozenState())
{
x750_frozenTimeout = 0.f;
x754_iceBreakPresses = 0;
CPhysicsActor::Stop();
ClearForcesAndTorques();
RemoveMaterial(EMaterialTypes::Immovable, stateMgr);
if (!stateMgr.GetCameraManager()->IsInCinematicCamera() && xa0c_ != -1)
{
std::experimental::optional<TToken<CGenDescription>> gpsm;
gpsm.emplace(g_SimplePool->GetObj(SObjectTag(FOURCC('PART'), xa0c_)));
CHUDBillboardEffect* effect = new CHUDBillboardEffect(gpsm, {}, stateMgr.AllocateUniqueId(), true,
"FrostExplosion", CHUDBillboardEffect::GetNearClipDistance(stateMgr),
CHUDBillboardEffect::GetScaleForPOV(stateMgr), zeus::CColor::skWhite,
zeus::CVector3f::skOne, zeus::CVector3f::skZero);
stateMgr.AddObject(effect);
CSfxHandle hnd = CSfxManager::SfxStart(3129, 1.f, 0.f, false, 0x7f, false, kInvalidAreaId);
ApplySubmergedPitchBend(hnd);
}
x768_morphball->Stop();
SetVisorSteam(0.f, 0.42857146f, 0.071428575f, xa08_steamTextureId, false);
}
}
bool CPlayer::GetFrozenState() const { return false; }
bool CPlayer::GetFrozenState() const { return x750_frozenTimeout > 0.f; }
void CPlayer::UpdateFrozenState(const CFinalInput& input, CStateManager& mgr)
{
x750_frozenTimeout -= input.DeltaTime();
if (x750_frozenTimeout > 0.f)
SetVisorSteam(0.7f, 0.42857146f, 0.071428575f, xa08_steamTextureId, false);
else
Stop(mgr);
if (x258_movementState == EPlayerMovementState::OnGround ||
x258_movementState == EPlayerMovementState::FallingMorphed)
{
Stop(mgr);
ClearForcesAndTorques();
}
x7a0_visorSteam.Update(input.DeltaTime());
switch (x2f8_morphTransState)
{
case EPlayerMorphBallState::Morphed:
x490_gun->ProcessInput(input, mgr);
break;
case EPlayerMorphBallState::Unmorphed:
case EPlayerMorphBallState::Morphing:
case EPlayerMorphBallState::Unmorphing:
if (ControlMapper::GetAnalogInput(ControlMapper::ECommands::JumpOrBoost, input))
{
if (x754_iceBreakPresses)
{
/* Subsequent Breaks */
CSfxHandle hnd = CSfxManager::SfxStart(3127, 1.f, 0.f, false, 0x7f, false, kInvalidAreaId);
ApplySubmergedPitchBend(hnd);
}
else
{
/* Initial Break */
CSfxHandle hnd = CSfxManager::SfxStart(3128, 1.f, 0.f, false, 0x7f, false, kInvalidAreaId);
ApplySubmergedPitchBend(hnd);
}
x754_iceBreakPresses += 1;
if (x754_iceBreakPresses > g_tweakPlayer->GetIceBreakPressCount())
{
g_GameState->SystemOptions().IncrFreezeBreakCount();
CHUDMemoParms info(0.f, true, true, true);
MP1::CSamusHud::DisplayHudMemo(u"", info);
Stop(mgr);
}
}
break;
}
}
void CPlayer::Think(float, CStateManager&) {}
@ -679,9 +1089,9 @@ void CPlayer::AcceptScriptMsg(EScriptObjectMessage msg, TUniqueId sender, CState
CActor::AcceptScriptMsg(msg, sender, mgr);
}
void CPlayer::SetVisorSteam(float, float, float, u32, bool) {}
void CPlayer::SetVisorSteam(float, float, float, ResId, bool) {}
void CPlayer::UpdateFootstepBounds(const CFinalInput& input, CStateManager&, float) {}
void CPlayer::UpdateFootstepSounds(const CFinalInput& input, CStateManager&, float) {}
u16 CPlayer::GetMaterialSoundUnderPlayer(CStateManager& mgr, const u16* table, u32 length, u16 defId)
{
@ -716,10 +1126,20 @@ void CPlayer::UpdateVisorTransition(float, CStateManager& mgr) {}
void CPlayer::UpdateVisorState(const CFinalInput&, float, CStateManager& mgr) {}
void CPlayer::UpdateGunState(const CFinalInput&, CStateManager& mgr)
{
}
void CPlayer::ForceGunOrientation(const zeus::CTransform&, CStateManager& mgr) {}
void CPlayer::UpdateCameraState(CStateManager& mgr) {}
void CPlayer::UpdateDebugCamera(CStateManager& mgr) {}
void CPlayer::UpdateCameraTimers(const CFinalInput& input) {}
void CPlayer::UpdateMorphBallState(const CFinalInput&, CStateManager& mgr) {}
CFirstPersonCamera& CPlayer::GetFirstPersonCamera(CStateManager& mgr)
{
return *mgr.GetCameraManager()->GetFirstPersonCamera();
@ -1091,7 +1511,21 @@ void CPlayer::BombJump(const zeus::CVector3f& pos, CStateManager& mgr) {}
zeus::CTransform CPlayer::CreateTransformFromMovementDirection() const { return {}; }
const CCollisionPrimitive* CPlayer::GetCollisionPrimitive() const { return CPhysicsActor::GetCollisionPrimitive(); }
const CCollisionPrimitive* CPlayer::GetCollisionPrimitive() const
{
switch (x2f8_morphTransState)
{
case EPlayerMorphBallState::Morphed:
return GetCollidableSphere();
default:
return CPhysicsActor::GetCollisionPrimitive();
}
}
const CCollidableSphere* CPlayer::GetCollidableSphere() const
{
return x768_morphball->GetCollidableSphere();
}
zeus::CTransform CPlayer::GetPrimitiveTransform() const { return {}; }
@ -1184,7 +1618,7 @@ void CPlayer::Touch() {}
void CPlayer::CVisorSteam::SetSteam(float a, float b, float c, ResId d, bool e)
{
if (x1c_ == -1 || a > x10_)
if (x1c_ == kInvalidResId || a > x10_)
{
x10_ = a;
x14_ = b;
@ -1198,7 +1632,7 @@ ResId CPlayer::CVisorSteam::GetTextureId() const { return xc_tex; }
void CPlayer::CVisorSteam::Update(float dt)
{
if (x1c_ == -1)
if (x1c_ == kInvalidResId)
x0_ = 0.f;
else
{
@ -1208,7 +1642,7 @@ void CPlayer::CVisorSteam::Update(float dt)
xc_tex = x1c_;
}
x1c_ = -1;
x1c_ = kInvalidResId;
if ((x20_alpha - x0_) < 0.000009999f || std::fabs(x20_alpha) > 0.000009999f)
return;
@ -1238,7 +1672,7 @@ void CPlayer::CVisorSteam::Update(float dt)
x24_ = 0.1f;
}
void CPlayer::CInputFilter::Reset()
void CPlayer::CFailsafeTest::Reset()
{
x0_stateSamples.clear();
x54_posSamples.clear();
@ -1246,7 +1680,7 @@ void CPlayer::CInputFilter::Reset()
x23c_inputSamples.clear();
}
void CPlayer::CInputFilter::AddSample(EInputState state, const zeus::CVector3f& pos,
void CPlayer::CFailsafeTest::AddSample(EInputState state, const zeus::CVector3f& pos,
const zeus::CVector3f& vel, const zeus::CVector2f& input)
{
if (x0_stateSamples.size() >= 20)
@ -1263,9 +1697,56 @@ void CPlayer::CInputFilter::AddSample(EInputState state, const zeus::CVector3f&
x23c_inputSamples.insert(x23c_inputSamples.begin(), input);
}
bool CPlayer::CInputFilter::Passes() const
bool CPlayer::CFailsafeTest::Passes() const
{
// TODO: Do
if (x0_stateSamples.size() != 20)
return false;
float posMag = 0.f;
zeus::CAABox velAABB(x148_velSamples[0], x148_velSamples[0]);
zeus::CAABox posAABB(x54_posSamples[0], x54_posSamples[0]);
zeus::CVector3f inputVec(x23c_inputSamples[0].x, x23c_inputSamples[0].y, 0.f);
zeus::CAABox inputAABB(inputVec, inputVec);
float maxVelMag = x148_velSamples[0].magnitude();
float minVelMag = maxVelMag;
u32 notEqualStates = 0;
for (int i=1 ; i<20 ; ++i)
{
float mag = (x54_posSamples[i-1] - x54_posSamples[i]).magSquared();
if (mag > FLT_EPSILON)
posMag += std::sqrt(mag);
posAABB.accumulateBounds(x54_posSamples[i]);
velAABB.accumulateBounds(x148_velSamples[i]);
float velMag = x148_velSamples[i].magnitude();
minVelMag = std::min(minVelMag, velMag);
maxVelMag = std::max(maxVelMag, velMag);
zeus::CVector3f inputVec2(x23c_inputSamples[i].x, x23c_inputSamples[i].y, 0.f);
inputAABB.accumulateBounds(inputVec2);
if (x0_stateSamples[i] != x0_stateSamples[i-1])
notEqualStates += 1;
}
bool test1 = true;
if (posMag >= 1.f / 30.f && posMag >= minVelMag / 30.f)
test1 = false;
if (!notEqualStates && x0_stateSamples[0] == EInputState::StartingJump)
{
float inputMag = (inputAABB.max - inputAABB.min).magnitude();
zeus::CAABox inputFrom0AABB(inputAABB);
inputFrom0AABB.accumulateBounds(zeus::CVector3f::skZero);
bool test2 = true;
if ((inputFrom0AABB.max - inputFrom0AABB.min).magnitude() >= 0.01f &&
inputMag <= 1.5f)
test2 = false;
return test1 && test2;
}
return false;
}

View File

@ -20,6 +20,7 @@ class IVisitor;
class CFinalInput;
class CPlayerCameraBob;
class CFirstPersonCamera;
class CCollidableSphere;
class CPlayer : public CPhysicsActor
{
@ -130,7 +131,7 @@ private:
float x10_ = 0.f;
float x14_ = 0.f;
float x18_ = 0.f;
ResId x1c_ = -1;
ResId x1c_ = kInvalidResId;
float x20_alpha = 0.f;
float x24_ = 0.f;
bool x28_ = false;
@ -143,7 +144,7 @@ private:
float GetAlpha() const { return x20_alpha; }
};
class CInputFilter
class CFailsafeTest
{
public:
enum class EInputState
@ -244,19 +245,20 @@ private:
float x48c_ = 0.f;
std::unique_ptr<CPlayerGun> x490_gun;
float x494_mapAlpha = 1.f;
u32 x498_ = 2;
float x49c_gunNotFiringTimeout;
std::unique_ptr<CInputFilter> x4a0_inputFilter;
std::unique_ptr<CFailsafeTest> x4a0_failsafeTest;
u32 x4a4_ = 0;
float x4f8_ = 0.f;
float x4fc_ = 0.f;
zeus::CVector3f x500_ = x34_transform.basis[1];
zeus::CVector3f x50c_ = x34_transform.basis[1];
zeus::CVector3f x518_ = x34_transform.basis[1];
zeus::CVector3f x518_leaveMorphDir = x34_transform.basis[1];
zeus::CVector3f x524_ = x34_transform.basis[1];
zeus::CVector3f x530_ = x34_transform.basis[1];
zeus::CVector3f x53c_ = x34_transform.basis[1];
zeus::CVector3f x548_ = x34_transform.basis[1];
float x554_ = x34_transform.basis[1].x;
float x53c_ = 0.f;
zeus::CVector3f x540_ = x34_transform.basis[1];
zeus::CVector3f x54c_ = x34_transform.basis[1];
bool x558_wasDamaged = false;
float x55c_damageAmt = 0.f;
float x560_prevDamageAmt = 0.f;
@ -275,8 +277,8 @@ private:
float x744_ = 0.f;
float x748_ = 0.f;
float x74c_visorStaticAlpha = 1.f;
float x750_ = 0.f;
u32 x754_ = 0;
float x750_frozenTimeout = 0.f;
u32 x754_iceBreakPresses = 0;
float x758_ = 0.f;
u32 x75c_ = 0;
bool x760_controlsFrozen = false;
@ -347,9 +349,7 @@ private:
float x9cc_ = 0.f;
u32 x9d0_ = 0;
u32 x9d4_ = 0;
float x9d8_ = 0.f;
float x9dc_ = 1.f;
float x9e0_ = 0.f;
zeus::CVector3f x9d8_ = zeus::CVector3f::skForward;
rstl::reserved_vector<TUniqueId, 5> x9e4_orbitDisableList;
float x9f4_deathTime = 0.f;
@ -419,23 +419,33 @@ public:
void CalculateRenderBounds();
void AddToRenderer(const zeus::CFrustum&, const CStateManager&);
void ComputeFreeLook(const CFinalInput& input);
void UpdateFreeLookState(const CFinalInput&, float dt, CStateManager&);
void UpdateFreeLook(float dt);
float GetMaximumPlayerPositiveVerticalVelocity(CStateManager&) const;
void ProcessInput(const CFinalInput&, CStateManager&);
bool ShouldSampleFailsafe(CStateManager& mgr) const;
void CalculateLeaveMorphBallDirection(const CFinalInput& input);
void CalculatePlayerControlDirection(CStateManager& mgr);
void CalculatePlayerMovementDirection(float dt);
void Stop(CStateManager& stateMgr);
bool GetFrozenState() const;
void UpdateFrozenState(const CFinalInput& input, CStateManager& mgr);
void Think(float, CStateManager&);
void PreThink(float, CStateManager&);
void AcceptScriptMsg(EScriptObjectMessage, TUniqueId, CStateManager&);
void SetVisorSteam(float, float, float, u32, bool);
void UpdateFootstepBounds(const CFinalInput& input, CStateManager&, float);
void SetVisorSteam(float, float, float, ResId, bool);
void UpdateFootstepSounds(const CFinalInput& input, CStateManager&, float);
u16 GetMaterialSoundUnderPlayer(CStateManager& mgr, const u16*, u32, u16);
u16 SfxIdFromMaterial(const CMaterialList&, const u16*, u32, u16);
void UpdateCrosshairsState(const CFinalInput&);
void UpdateVisorTransition(float, CStateManager& mgr);
void UpdateVisorState(const CFinalInput&, float, CStateManager& mgr);
void UpdateGunState(const CFinalInput&, CStateManager& mgr);
void ForceGunOrientation(const zeus::CTransform&, CStateManager& mgr);
void UpdateCameraState(CStateManager& mgr);
void UpdateDebugCamera(CStateManager& mgr);
void UpdateCameraTimers(const CFinalInput& input);
void UpdateMorphBallState(const CFinalInput&, CStateManager& mgr);
CFirstPersonCamera& GetFirstPersonCamera(CStateManager& mgr);
void UpdateGunTransform(const zeus::CVector3f&, float, CStateManager& mgr, bool);
void UpdateAssistedAiming(const zeus::CTransform& xf, const CStateManager& mgr);
@ -503,6 +513,7 @@ public:
void BombJump(const zeus::CVector3f& pos, CStateManager& mgr);
zeus::CTransform CreateTransformFromMovementDirection() const;
const CCollisionPrimitive* GetCollisionPrimitive() const;
const CCollidableSphere* GetCollidableSphere() const;
zeus::CTransform GetPrimitiveTransform() const;
void CollidedWith(TUniqueId, const CCollisionInfoList&, CStateManager& mgr);
float GetActualFirstPersonMaxVelocity() const;