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.

Every playable character in TF2 is an instance of CTFPlayer, a server-side class that inherits from CBaseMultiplayerPlayer. The current class a player has selected is tracked by an embedded CTFPlayerClassShared object, which holds the class index and delegates all stat lookups to a TFPlayerClassData_t struct loaded from script files at startup.

The TF_CLASS enum

All 9 classes (plus sentinel values) are defined as an anonymous enum in tf_shareddefs.h:
// game/shared/tf/tf_shareddefs.h:201
enum
{
    TF_CLASS_UNDEFINED = 0,

    TF_CLASS_SCOUT,         // TF_FIRST_NORMAL_CLASS
    TF_CLASS_SNIPER,
    TF_CLASS_SOLDIER,
    TF_CLASS_DEMOMAN,
    TF_CLASS_MEDIC,
    TF_CLASS_HEAVYWEAPONS,
    TF_CLASS_PYRO,
    TF_CLASS_SPY,
    TF_CLASS_ENGINEER,

    // Add any new classes after Engineer
    TF_CLASS_CIVILIAN,      // TF_LAST_NORMAL_CLASS
    TF_CLASS_COUNT_ALL,

    TF_CLASS_RANDOM
};
TF_CLASS_CIVILIAN is the TF_LAST_NORMAL_CLASS sentinel. Iterating over all playable classes uses the macro FOR_EACH_NORMAL_PLAYER_CLASS(_i), which loops from TF_FIRST_NORMAL_CLASS (1) up to but not including TF_LAST_NORMAL_CLASS.

Class reference

ClassTF_CLASS_* valueRole
Scout1Fast flanker, double jump
Sniper2Long-range, scoped shots
Soldier3Rocket launcher, rocket jump
Demoman4Sticky bombs, grenades
Medic5Healing, Ubercharge
Heavy6High health, minigun
Pyro7Flamethrower, airblast
Spy8Disguise, backstab, cloak
Engineer9Buildings: sentry, dispenser, teleporter

CTFPlayer class hierarchy

CTFPlayer is declared in game/server/tf/tf_player.h and inherits from three bases:
// game/server/tf/tf_player.h:68
class CTFPlayer : public CBaseMultiplayerPlayer,
                  public IHasAttributes,
                  public IInventoryUpdateListener
CBaseMultiplayerPlayer sits above CBasePlayer in the Source engine hierarchy and adds team-play utilities. IHasAttributes plugs the player into the item attribute system. IInventoryUpdateListener receives callbacks when the loadout changes.

CTFPlayerClassShared: current class state

The CTFPlayerClassShared object (declared in game/shared/tf/tf_playerclass_shared.h) is embedded inside CTFPlayerShared and networked to the client via DT_TFPlayerClassShared. It stores:
  • m_iClass — the current TF_CLASS_* index (networked)
  • m_iszClassIcon / m_iszCustomModel — optional custom visuals (MvM robots, Halloween)
  • Custom model transform and animation flags
All stat accessors delegate to GetPlayerClassData(m_iClass):
// game/shared/tf/tf_playerclass_shared.h:66
float  GetMaxSpeed()    { return GetPlayerClassData( m_iClass )->m_flMaxSpeed; }
int    GetMaxHealth()   { return GetPlayerClassData( m_iClass )->m_nMaxHealth; }
int    GetMaxArmor()    { return GetPlayerClassData( m_iClass )->m_nMaxArmor;  }
GetPlayerClassData() returns a pointer into a global array of TFPlayerClassData_t structs. Each struct is populated at startup from a per-class script file by parsing the speed_max, health_max, and model keys via KeyValues.

Class switching flow

1

Player selects a class

The client sends a joinclass command. The server validates the request and calls CTFPlayer::HandleCommand_JoinClass().
2

Desired class is stored

m_Shared.SetDesiredPlayerClassIndex() records the new class. The switch is deferred until the next respawn.
3

Respawn applies the class

On respawn, CTFPlayerClassShared::Init( iClass ) sets m_iClass and resets all per-class state. TeamFortress_SetSpeed() is called to update m_flMaxspeed from the new class data.
4

Client receives the update

m_iClass is a CNetworkVar, so Source’s DataTable system propagates it to DT_TFPlayerClassShared on the client automatically.

Build docs developers (and LLMs) love