Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/sr2echa/TF2-Source-Code/llms.txt

Use this file to discover all available pages before exploring further.

TF2 overrides the Source Engine’s stock movement system with CTFGameMovement, defined entirely in game/shared/tf/tf_gamemovement.cpp. Because the file is compiled into both server.dll and client.dll, the same movement code runs on the server (authoritative) and on the client (predictive), keeping the two in sync without a round-trip for every input frame.

Inheritance

CTFGameMovement inherits from CGameMovement, which implements the standard Source locomotion model (walking, jumping, ladders, water):
// game/shared/tf/tf_gamemovement.cpp:94
class CTFGameMovement : public CGameMovement
{
public:
    DECLARE_CLASS( CTFGameMovement, CGameMovement );

    virtual void ProcessMovement( CBasePlayer *pBasePlayer, CMoveData *pMove );
    virtual bool CheckJumpButton();
    virtual void AirMove();
    virtual void Duck();
    virtual float GetAirSpeedCap();
    // ...

private:
    void AirDash();
    void PreventBunnyJumping();
    // ...
};
ProcessMovement() is the per-tick entry point called by the engine. TF2’s override sets the global speed cap, then dispatches special movement modes before falling through to PlayerMove():
// game/shared/tf/tf_gamemovement.cpp:293
void CTFGameMovement::ProcessMovement( CBasePlayer *pBasePlayer, CMoveData *pMove )
{
    // ...
    mv->m_flMaxSpeed = TF_MAX_SPEED;  // 400 * 1.3 — Scout max + 30% headroom

    ChargeMove();       // Demoman shield charge
    StunMove();         // stun conditions
    TauntMove();        // taunt locomotion
    GrapplingHookMove();
    HighMaxSpeedMove(); // Soda Popper hype, rune agility
    PlayerMove();
}

Per-class speed

mv->m_flMaxSpeed starts at TF_MAX_SPEED (≈ 520 hu/s) each tick. The actual cap for the current player is set by CTFPlayer::TeamFortress_SetSpeed(), which calls TeamFortress_CalculateMaxSpeed():
// game/shared/tf/tf_player_shared.cpp:10306
float default_speed = GetPlayerClassData( playerclass )->m_flMaxSpeed;
GetPlayerClassData() returns the TFPlayerClassData_t for the player’s current class. m_flMaxSpeed is parsed from the speed_max key in each class’s script file at startup. Conditions and disguise can lower the effective cap. For example, a Spy disguised as a slower class is capped at the disguised class’s speed:
// game/shared/tf/tf_player_shared.cpp:10329
else if ( m_Shared.InCond( TF_COND_DISGUISED ) && !m_Shared.IsStealthed() )
{
    float flMaxDisguiseSpeed = GetPlayerClassData( m_Shared.GetDisguiseClass() )->m_flMaxSpeed;
    maxfbspeed = MIN( flMaxDisguiseSpeed, maxfbspeed );
}
When a Sniper or Heavy is aiming (TF_COND_AIMING), their cap is reduced to 80 hu/s (110 hu/s for Heavy).

Scout double jump

Scout’s double jump is handled by CTFGameMovement::AirDash(). The check in CheckJumpButton() determines whether a mid-air jump is allowed:
// game/shared/tf/tf_gamemovement.cpp:1215
bool bScout = m_pTFPlayer->GetPlayerClass()->IsClass( TF_CLASS_SCOUT );
// ...
bool bAllow = ( bScout && !bOnGround );
if ( m_pTFPlayer->CanAirDash() )
    bAirDash = true;
AirDash() replaces the current XY velocity with the player’s wish direction and adds an upward impulse of 268.33 hu/s:
// game/shared/tf/tf_gamemovement.cpp:1016
float flDashZ = 268.3281572999747f * flJumpMod;
// ...
mv->m_vecVelocity = vecWishDirection;
mv->m_vecVelocity.z += flDashZ;
The air-dash counter is stored in CTFPlayerShared::m_iAirDash. A second call (Atomizer) deals 10 points of self-damage unless the player is under TF_COND_HALLOWEEN_SPEED_BOOST or Soda Popper hype.

Rocket and sticky jumping

TF_COND_BLASTJUMPING is set when a player leaves the ground due to explosion self-damage. CTFGameMovement uses this condition to skip the bunny-jump velocity clamp that would otherwise cap speed on re-land, allowing the player to retain their blast-launched velocity.
Ducking constants are shared with the base engine: TF_TIME_TO_DUCK is 0.3 seconds and TF_AIRDUCKED_COUNT limits the number of air ducks to 2 (configurable via tf_clamp_airducks).

Build docs developers (and LLMs) love