//========= Copyright © 1996-2004, Valve LLC, All rights reserved. ============ // //============================================================================= #include "cbase.h" #include "KeyValues.h" #include "tf_playerclass_shared.h" #include "materialsystem/imaterialsystemhardwareconfig.h" #include "tier2/tier2.h" #ifdef CLIENT_DLL bool UseHWMorphModels(); #endif #define TF_CLASS_UNDEFINED_FILE "" #define TF_CLASS_SCOUT_FILE "scripts/playerclasses/scout" #define TF_CLASS_SNIPER_FILE "scripts/playerclasses/sniper" #define TF_CLASS_SOLDIER_FILE "scripts/playerclasses/soldier" #define TF_CLASS_DEMOMAN_FILE "scripts/playerclasses/demoman" #define TF_CLASS_MEDIC_FILE "scripts/playerclasses/medic" #define TF_CLASS_HEAVYWEAPONS_FILE "scripts/playerclasses/heavyweapons" #define TF_CLASS_PYRO_FILE "scripts/playerclasses/pyro" #define TF_CLASS_SPY_FILE "scripts/playerclasses/spy" #define TF_CLASS_ENGINEER_FILE "scripts/playerclasses/engineer" #define TF_CLASS_CIVILIAN_FILE "scripts/playerclasses/civilian" const char *s_aPlayerClassFiles[] = { TF_CLASS_UNDEFINED_FILE, TF_CLASS_SCOUT_FILE, TF_CLASS_SNIPER_FILE, TF_CLASS_SOLDIER_FILE, TF_CLASS_DEMOMAN_FILE, TF_CLASS_MEDIC_FILE, TF_CLASS_HEAVYWEAPONS_FILE, TF_CLASS_PYRO_FILE, TF_CLASS_SPY_FILE, TF_CLASS_ENGINEER_FILE, TF_CLASS_CIVILIAN_FILE }; TFPlayerClassData_t s_aTFPlayerClassData[TF_CLASS_COUNT_ALL]; //----------------------------------------------------------------------------- // Purpose: Constructor //----------------------------------------------------------------------------- TFPlayerClassData_t::TFPlayerClassData_t() { m_szClassName[0] = '\0'; m_szModelName[0] = '\0'; m_szHWMModelName[0] = '\0'; m_szLocalizableName[0] = '\0'; m_flMaxSpeed = 0.0f; m_nMaxHealth = 0; m_nMaxArmor = 0; #ifdef GAME_DLL m_szDeathSound[0] = '\0'; m_szCritDeathSound[0] = '\0'; m_szMeleeDeathSound[0] = '\0'; m_szExplosionDeathSound[0] = '\0'; #endif for ( int iWeapon = 0; iWeapon < TF_PLAYER_WEAPON_COUNT; ++iWeapon ) { m_aWeapons[iWeapon] = TF_WEAPON_NONE; } for ( int iGrenade = 0; iGrenade < TF_PLAYER_GRENADE_COUNT; ++iGrenade ) { m_aGrenades[iGrenade] = TF_WEAPON_NONE; } for ( int iAmmo = 0; iAmmo < TF_AMMO_COUNT; ++iAmmo ) { m_aAmmoMax[iAmmo] = TF_AMMO_DUMMY; } for ( int iBuildable = 0; iBuildable < TF_PLAYER_BUILDABLE_COUNT; ++iBuildable ) { m_aBuildable[iBuildable] = OBJ_LAST; } m_bParsed = false; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void TFPlayerClassData_t::Parse( const char *szName ) { // Have we parsed this file already? if ( m_bParsed ) return; // No filesystem at this point???? Hmmmm...... // Parse class file. const unsigned char *pKey = NULL; if ( g_pGameRules ) { pKey = g_pGameRules->GetEncryptionKey(); } KeyValues *pKV = ReadEncryptedKVFile( filesystem, szName, pKey ); if ( pKV ) { ParseData( pKV ); pKV->deleteThis(); } } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- const char *TFPlayerClassData_t::GetModelName() const { #ifdef CLIENT_DLL if ( UseHWMorphModels() ) { if ( m_szHWMModelName[0] != '\0' ) { return m_szHWMModelName; } } return m_szModelName; #else return m_szModelName; #endif } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void TFPlayerClassData_t::ParseData( KeyValues *pKeyValuesData ) { // Attributes. Q_strncpy( m_szClassName, pKeyValuesData->GetString( "name" ), TF_NAME_LENGTH ); // Load the high res model or the lower res model. if ( !IsX360() ) { Q_strncpy( m_szHWMModelName, pKeyValuesData->GetString( "model_hwm" ), TF_NAME_LENGTH ); } Q_strncpy( m_szModelName, pKeyValuesData->GetString( "model" ), TF_NAME_LENGTH ); Q_strncpy( m_szLocalizableName, pKeyValuesData->GetString( "localize_name" ), TF_NAME_LENGTH ); m_flMaxSpeed = pKeyValuesData->GetFloat( "speed_max" ); m_nMaxHealth = pKeyValuesData->GetInt( "health_max" ); m_nMaxArmor = pKeyValuesData->GetInt( "armor_max" ); // Weapons. int i; char buf[32]; for ( i=0;iGetString( buf ) ); } // Grenades. m_aGrenades[0] = GetWeaponId( pKeyValuesData->GetString( "grenade1" ) ); m_aGrenades[1] = GetWeaponId( pKeyValuesData->GetString( "grenade2" ) ); // Ammo Max. KeyValues *pAmmoKeyValuesData = pKeyValuesData->FindKey( "AmmoMax" ); if ( pAmmoKeyValuesData ) { for ( int iAmmo = 1; iAmmo < TF_AMMO_COUNT; ++iAmmo ) { m_aAmmoMax[iAmmo] = pAmmoKeyValuesData->GetInt( g_aAmmoNames[iAmmo], 0 ); } } // Buildables for ( i=0;iGetString( buf ) ); } // Temp animation flags m_bDontDoAirwalk = ( pKeyValuesData->GetInt( "DontDoAirwalk", 0 ) > 0 ); m_bDontDoNewJump = ( pKeyValuesData->GetInt( "DontDoNewJump", 0 ) > 0 ); #ifdef GAME_DLL // right now we only emit these sounds from server. if that changes we can do this in both dlls // Death Sounds Q_strncpy( m_szDeathSound, pKeyValuesData->GetString( "sound_death", "Player.Death" ), MAX_PLAYERCLASS_SOUND_LENGTH ); Q_strncpy( m_szCritDeathSound, pKeyValuesData->GetString( "sound_crit_death", "TFPlayer.CritDeath" ), MAX_PLAYERCLASS_SOUND_LENGTH ); Q_strncpy( m_szMeleeDeathSound, pKeyValuesData->GetString( "sound_melee_death", "Player.MeleeDeath" ), MAX_PLAYERCLASS_SOUND_LENGTH ); Q_strncpy( m_szExplosionDeathSound, pKeyValuesData->GetString( "sound_explosion_death", "Player.ExplosionDeath" ), MAX_PLAYERCLASS_SOUND_LENGTH ); #endif // The file has been parsed. m_bParsed = true; } //----------------------------------------------------------------------------- // Purpose: Initialize the player class data (keep a cache). //----------------------------------------------------------------------------- void InitPlayerClasses( void ) { // Special case the undefined class. TFPlayerClassData_t *pClassData = &s_aTFPlayerClassData[TF_CLASS_UNDEFINED]; Assert( pClassData ); Q_strncpy( pClassData->m_szClassName, "undefined", TF_NAME_LENGTH ); Q_strncpy( pClassData->m_szModelName, "models/player/scout.mdl", TF_NAME_LENGTH ); // Undefined players still need a model Q_strncpy( pClassData->m_szLocalizableName, "undefined", TF_NAME_LENGTH ); // Initialize the classes. for ( int iClass = 1; iClass < TF_CLASS_COUNT_ALL; ++iClass ) { TFPlayerClassData_t *pClassData = &s_aTFPlayerClassData[iClass]; Assert( pClassData ); pClassData->Parse( s_aPlayerClassFiles[iClass] ); } } //----------------------------------------------------------------------------- // Purpose: Helper function to get player class data. //----------------------------------------------------------------------------- TFPlayerClassData_t *GetPlayerClassData( int iClass ) { Assert ( ( iClass >= 0 ) && ( iClass < TF_CLASS_COUNT_ALL ) ); return &s_aTFPlayerClassData[iClass]; } //============================================================================= // // Shared player class data. // //============================================================================= // // Tables. // // Client specific. #ifdef CLIENT_DLL BEGIN_RECV_TABLE_NOBASE( CTFPlayerClassShared, DT_TFPlayerClassShared ) RecvPropInt( RECVINFO( m_iClass ) ), END_RECV_TABLE() // Server specific. #else BEGIN_SEND_TABLE_NOBASE( CTFPlayerClassShared, DT_TFPlayerClassShared ) SendPropInt( SENDINFO( m_iClass ), Q_log2( TF_CLASS_COUNT_ALL )+1, SPROP_UNSIGNED ), END_SEND_TABLE() #endif //----------------------------------------------------------------------------- // Purpose: Constructor //----------------------------------------------------------------------------- CTFPlayerClassShared::CTFPlayerClassShared() { m_iClass.Set( TF_CLASS_UNDEFINED ); } //----------------------------------------------------------------------------- // Purpose: Initialize the player class. //----------------------------------------------------------------------------- bool CTFPlayerClassShared::Init( int iClass ) { Assert ( ( iClass >= TF_FIRST_NORMAL_CLASS ) && ( iClass <= TF_LAST_NORMAL_CLASS ) ); m_iClass = iClass; return true; } // If needed, put this into playerclass scripts bool CTFPlayerClassShared::CanBuildObject( int iObjectType ) { bool bFound = false; TFPlayerClassData_t *pData = GetData(); int i; for ( i=0;im_aBuildable[i] ) { bFound = true; break; } } return bFound; }