//========= Copyright Valve Corporation, All rights reserved. ============// // // Purpose: Data shared between the client & game dlls // // $NoKeywords: $ //=============================================================================// #include "cbase.h" #include "tf_shareddefs.h" #include "tier0/dbg.h" #include "basetypes.h" #include #ifndef CLIENT_DLL #include "tf_team.h" #include "tf_class_commando.h" #include "tf_class_defender.h" #include "tf_class_escort.h" #include "tf_class_infiltrator.h" #include "tf_class_medic.h" #include "tf_class_recon.h" #include "tf_class_sniper.h" #include "tf_class_support.h" #include "tf_class_sapper.h" #include "tf_class_pyro.h" #else #include "c_tfteam.h" #include "c_tf_class_commando.h" #include "c_tf_class_defender.h" #include "c_tf_class_escort.h" #include "c_tf_class_infiltrator.h" #include "c_tf_class_medic.h" #include "c_tf_class_recon.h" #include "c_tf_class_sniper.h" #include "c_tf_class_support.h" #include "c_tf_class_sapper.h" #include "c_tf_class_pyro.h" #define CTFTeam C_TFTeam #endif // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" ConVar inv_demo( "inv_demo","0", FCVAR_REPLICATED, "Invasion demo." ); ConVar lod_effect_distance( "lod_effect_distance","3240000", FCVAR_REPLICATED, "Distance at which effects LOD." ); ConVar tf_cheapobjects( "tf_cheapobjects","0", FCVAR_REPLICATED, "Set to 1 and all objects will cost 0" ); //-------------------------------------------------------------------------- // OBJECTS //-------------------------------------------------------------------------- static int g_iClassInfo_Undecided[] = { OBJ_LAST }; static int g_iClassInfo_Recon[] = { OBJ_WAGON, OBJ_LAST }; static int g_iClassInfo_Commando[] = { OBJ_POWERPACK, OBJ_VEHICLE_BOOST, OBJ_DRAGONSTEETH, OBJ_MANNED_MISSILELAUNCHER, OBJ_SANDBAG_BUNKER, OBJ_DRAGONSTEETH, OBJ_LAST }; static int g_iClassInfo_Medic[] = { OBJ_POWERPACK, OBJ_SELFHEAL, OBJ_BUFF_STATION, OBJ_MANNED_PLASMAGUN, OBJ_SANDBAG_BUNKER, OBJ_BUNKER, OBJ_DRAGONSTEETH, OBJ_SHIELDWALL, OBJ_LAST }; static int g_iClassInfo_Defender[] = { OBJ_POWERPACK, OBJ_SENTRYGUN_PLASMA, OBJ_MANNED_MISSILELAUNCHER, OBJ_BARBED_WIRE, OBJ_DRAGONSTEETH, OBJ_TOWER, OBJ_SANDBAG_BUNKER, OBJ_BUNKER, OBJ_DRIVER_MACHINEGUN, //OBJ_MORTAR, OBJ_LAST }; static int g_iClassInfo_Sniper[] = { OBJ_WAGON, OBJ_LAST }; static int g_iClassInfo_Support[] = { OBJ_WAGON, OBJ_LAST }; static int g_iClassInfo_Escort[] = { OBJ_SHIELDWALL, OBJ_MANNED_SHIELD, OBJ_SANDBAG_BUNKER, OBJ_BUNKER, OBJ_LAST }; static int g_iClassInfo_Sapper[] = { OBJ_POWERPACK, OBJ_DRAGONSTEETH, OBJ_TOWER, OBJ_SANDBAG_BUNKER, OBJ_MANNED_PLASMAGUN, OBJ_LAST }; static int g_iClassInfo_Infiltrator[] = { OBJ_WAGON, OBJ_LAST }; static int g_iClassInfo_Pyro[] = { OBJ_WAGON, OBJ_LAST }; //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- bool IsObjectAnUpgrade( int iObjectType ) { return ( iObjectType >= OBJ_SELFHEAL && iObjectType < OBJ_BATTERING_RAM ); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- bool IsObjectAVehicle( int iObjectType ) { return ( iObjectType >= OBJ_BATTERING_RAM && iObjectType < OBJ_TOWER ); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- bool IsObjectADefensiveBuilding( int iObjectType ) { return ( iObjectType >= OBJ_TOWER ); } //-------------------------------------------------------------------------- // PLAYER CLASSES //-------------------------------------------------------------------------- #if defined( CLIENT_DLL ) #define DEFINE_PLAYERCLASS_ALLOC_FNS( className, iClass ) \ C_PlayerClass* AllocClient##className##( C_BaseTFPlayer *pPlayer ) \ { \ return new C_PlayerClass##className##( pPlayer ); \ } \ CPlayerClass* AllocServer##className##( CBaseTFPlayer *pPlayer ) \ { \ Assert( false ); \ return NULL; \ } #define GENERATE_PLAYERCLASS_INFO( className ) \ AllocClient##className##, AllocServer##className, NULL // ------------------------------------------------------------------------------------- // // DT_AllPlayerClasses recv table. // ------------------------------------------------------------------------------------- // BEGIN_RECV_TABLE_NOBASE( C_AllPlayerClasses, DT_AllPlayerClasses ) RecvPropDataTable( RECVINFO_DT(m_pClasses[TFCLASS_COMMANDO]), 0, &REFERENCE_RECV_TABLE( DT_PlayerClassCommandoData ), DataTableRecvProxy_PointerDataTable ), RecvPropDataTable( RECVINFO_DT(m_pClasses[TFCLASS_DEFENDER]), 0, &REFERENCE_RECV_TABLE( DT_PlayerClassDefenderData ), DataTableRecvProxy_PointerDataTable ), RecvPropDataTable( RECVINFO_DT(m_pClasses[TFCLASS_ESCORT]), 0, &REFERENCE_RECV_TABLE( DT_PlayerClassEscortData ), DataTableRecvProxy_PointerDataTable ), RecvPropDataTable( RECVINFO_DT(m_pClasses[TFCLASS_INFILTRATOR]), 0, &REFERENCE_RECV_TABLE( DT_PlayerClassInfiltratorData ), DataTableRecvProxy_PointerDataTable ), RecvPropDataTable( RECVINFO_DT(m_pClasses[TFCLASS_MEDIC]), 0, &REFERENCE_RECV_TABLE( DT_PlayerClassMedicData ), DataTableRecvProxy_PointerDataTable ), RecvPropDataTable( RECVINFO_DT(m_pClasses[TFCLASS_RECON]), 0, &REFERENCE_RECV_TABLE( DT_PlayerClassReconData ), DataTableRecvProxy_PointerDataTable ), RecvPropDataTable( RECVINFO_DT(m_pClasses[TFCLASS_SNIPER]), 0, &REFERENCE_RECV_TABLE( DT_PlayerClassSniperData ), DataTableRecvProxy_PointerDataTable ), RecvPropDataTable( RECVINFO_DT(m_pClasses[TFCLASS_SUPPORT]), 0, &REFERENCE_RECV_TABLE( DT_PlayerClassSupportData ), DataTableRecvProxy_PointerDataTable ), RecvPropDataTable( RECVINFO_DT(m_pClasses[TFCLASS_SAPPER]), 0, &REFERENCE_RECV_TABLE( DT_PlayerClassSapperData ), DataTableRecvProxy_PointerDataTable ) END_RECV_TABLE() #else #define DEFINE_PLAYERCLASS_ALLOC_FNS( className, iClass ) \ ConVar class_##className##_health( "class_" #className "_health", "0", FCVAR_NONE, #className "'s max health" ); \ C_PlayerClass* AllocClient##className##( C_BaseTFPlayer *pPlayer ) \ { \ Assert( false ); \ return NULL; \ } \ CPlayerClass* AllocServer##className##( CBaseTFPlayer *pPlayer ) \ { \ return new CPlayerClass##className##( pPlayer, iClass ); \ } #define GENERATE_PLAYERCLASS_INFO( className ) \ AllocClient##className##, AllocServer##className, &class_##className##_health // ------------------------------------------------------------------------------------- // // DT_AllPlayerClasses recv table. // ------------------------------------------------------------------------------------- // BEGIN_SEND_TABLE_NOBASE( CAllPlayerClasses, DT_AllPlayerClasses ) SendPropDataTable( SENDINFO_DT(m_pClasses[TFCLASS_COMMANDO]), &REFERENCE_SEND_TABLE( DT_PlayerClassCommandoData ), SendProxy_DataTablePtrToDataTable ), SendPropDataTable( SENDINFO_DT(m_pClasses[TFCLASS_DEFENDER]), &REFERENCE_SEND_TABLE( DT_PlayerClassDefenderData ), SendProxy_DataTablePtrToDataTable ), SendPropDataTable( SENDINFO_DT(m_pClasses[TFCLASS_ESCORT]), &REFERENCE_SEND_TABLE( DT_PlayerClassEscortData ), SendProxy_DataTablePtrToDataTable ), SendPropDataTable( SENDINFO_DT(m_pClasses[TFCLASS_INFILTRATOR]),&REFERENCE_SEND_TABLE( DT_PlayerClassInfiltratorData ), SendProxy_DataTablePtrToDataTable ), SendPropDataTable( SENDINFO_DT(m_pClasses[TFCLASS_MEDIC]), &REFERENCE_SEND_TABLE( DT_PlayerClassMedicData ), SendProxy_DataTablePtrToDataTable ), SendPropDataTable( SENDINFO_DT(m_pClasses[TFCLASS_RECON]), &REFERENCE_SEND_TABLE( DT_PlayerClassReconData ), SendProxy_DataTablePtrToDataTable ), SendPropDataTable( SENDINFO_DT(m_pClasses[TFCLASS_SNIPER]), &REFERENCE_SEND_TABLE( DT_PlayerClassSniperData ), SendProxy_DataTablePtrToDataTable ), SendPropDataTable( SENDINFO_DT(m_pClasses[TFCLASS_SUPPORT]), &REFERENCE_SEND_TABLE( DT_PlayerClassSupportData ), SendProxy_DataTablePtrToDataTable ), SendPropDataTable( SENDINFO_DT(m_pClasses[TFCLASS_SAPPER]), &REFERENCE_SEND_TABLE( DT_PlayerClassSapperData ), SendProxy_DataTablePtrToDataTable ) END_SEND_TABLE() #endif // ------------------------------------------------------------------------------------- // // CAllPlayerClasses implementation. // ------------------------------------------------------------------------------------- // CAllPlayerClasses::CAllPlayerClasses( PLAYER_TYPE *pPlayer ) { for ( int i=0; i < TFCLASS_CLASS_COUNT; i++ ) { m_pClasses[i] = NULL; #if defined( CLIENT_DLL ) if ( GetTFClassInfo( i )->m_pClientAlloc ) m_pClasses[i] = GetTFClassInfo( i )->m_pClientAlloc( pPlayer ); #else if ( GetTFClassInfo( i )->m_pServerAlloc ) m_pClasses[i] = GetTFClassInfo( i )->m_pServerAlloc( pPlayer ); #endif } } CAllPlayerClasses::~CAllPlayerClasses() { for ( int i=0; i < TFCLASS_CLASS_COUNT; i++ ) { delete m_pClasses[i]; } } PLAYER_CLASS_TYPE* CAllPlayerClasses::GetPlayerClass( int iClass ) { Assert( iClass >= 0 && iClass < TFCLASS_CLASS_COUNT ); return m_pClasses[iClass]; } DEFINE_PLAYERCLASS_ALLOC_FNS( Recon, TFCLASS_RECON ); DEFINE_PLAYERCLASS_ALLOC_FNS( Commando, TFCLASS_COMMANDO ); DEFINE_PLAYERCLASS_ALLOC_FNS( Medic, TFCLASS_MEDIC ); DEFINE_PLAYERCLASS_ALLOC_FNS( Defender, TFCLASS_DEFENDER ); DEFINE_PLAYERCLASS_ALLOC_FNS( Sniper, TFCLASS_SNIPER ); DEFINE_PLAYERCLASS_ALLOC_FNS( Support, TFCLASS_SUPPORT ); DEFINE_PLAYERCLASS_ALLOC_FNS( Escort, TFCLASS_ESCORT ); DEFINE_PLAYERCLASS_ALLOC_FNS( Sapper, TFCLASS_SAPPER ); DEFINE_PLAYERCLASS_ALLOC_FNS( Infiltrator, TFCLASS_INFILTRATOR ); DEFINE_PLAYERCLASS_ALLOC_FNS( Pyro, TFCLASS_PYRO ); CTFClassInfo g_TFClassInfos[ TFCLASS_CLASS_COUNT ] = { { "Undecided", g_iClassInfo_Undecided, false, NULL, NULL, NULL }, { "Recon", g_iClassInfo_Recon, false, GENERATE_PLAYERCLASS_INFO( Recon ) }, { "Commando", g_iClassInfo_Commando, true, GENERATE_PLAYERCLASS_INFO( Commando ) }, { "Medic", g_iClassInfo_Medic, true, GENERATE_PLAYERCLASS_INFO( Medic ) }, { "Defender", g_iClassInfo_Defender, true, GENERATE_PLAYERCLASS_INFO( Defender ) }, { "Sniper", g_iClassInfo_Sniper, false, GENERATE_PLAYERCLASS_INFO( Sniper ) }, { "Support", g_iClassInfo_Support, false, GENERATE_PLAYERCLASS_INFO( Support ) }, { "Escort", g_iClassInfo_Escort, true, GENERATE_PLAYERCLASS_INFO( Escort ) }, { "Sapper", g_iClassInfo_Sapper, true, GENERATE_PLAYERCLASS_INFO( Sapper ) }, { "Infiltrator",g_iClassInfo_Infiltrator, false, GENERATE_PLAYERCLASS_INFO( Infiltrator ) }, { "Pyro", g_iClassInfo_Pyro, false, GENERATE_PLAYERCLASS_INFO( Pyro ) } }; const CTFClassInfo* GetTFClassInfo( int i ) { Assert( i >= 0 && i < TFCLASS_CLASS_COUNT ); return &g_TFClassInfos[i]; } // ------------------------------------------------------------------------------------------------ // // CObjectInfo tables. // ------------------------------------------------------------------------------------------------ // CObjectInfo::CObjectInfo( char *pObjectName ) { m_pObjectName = pObjectName; m_pClassName = NULL; m_flBuildTime = -9999; m_nMaxObjects = -9999; m_Cost = -9999; m_CostMultiplierPerInstance = -999; m_UpgradeCost = -9999; m_MaxUpgradeLevel = -9999; m_pBuilderWeaponName = NULL; m_pBuilderPlacementString = NULL; m_SelectionSlot = -9999; m_SelectionPosition = -9999; m_bSolidToPlayerMovement = false; m_flSapperAttachTime = -9999; m_pIconActive = NULL; } CObjectInfo::~CObjectInfo() { delete [] m_pClassName; delete [] m_pStatusName; delete [] m_pBuilderWeaponName; delete [] m_pBuilderPlacementString; delete [] m_pIconActive; } CObjectInfo g_ObjectInfos[OBJ_LAST] = { CObjectInfo( "OBJ_POWERPACK" ), CObjectInfo( "OBJ_RESUPPLY" ), CObjectInfo( "OBJ_SENTRYGUN_PLASMA" ), CObjectInfo( "OBJ_SENTRYGUN_ROCKET_LAUNCHER" ), CObjectInfo( "OBJ_SHIELDWALL" ), CObjectInfo( "OBJ_RESOURCEPUMP" ), CObjectInfo( "OBJ_RESPAWN_STATION" ), CObjectInfo( "OBJ_RALLYFLAG" ), CObjectInfo( "OBJ_MANNED_PLASMAGUN" ), CObjectInfo( "OBJ_MANNED_MISSILELAUNCHER" ), CObjectInfo( "OBJ_MANNED_SHIELD" ), CObjectInfo( "OBJ_EMPGENERATOR" ), CObjectInfo( "OBJ_BUFF_STATION" ), CObjectInfo( "OBJ_BARBED_WIRE" ), CObjectInfo( "OBJ_MCV_SELECTION_PANEL" ), CObjectInfo( "OBJ_MAPDEFINED" ), CObjectInfo( "OBJ_MORTAR" ), CObjectInfo( "OBJ_SELFHEAL" ), CObjectInfo( "OBJ_ARMOR_UPGRADE" ), CObjectInfo( "OBJ_VEHICLE_BOOST" ), CObjectInfo( "OBJ_EXPLOSIVES" ), CObjectInfo( "OBJ_DRIVER_MACHINEGUN" ), CObjectInfo( "OBJ_BATTERING_RAM" ), CObjectInfo( "OBJ_SIEGE_TOWER" ), CObjectInfo( "OBJ_WAGON" ), CObjectInfo( "OBJ_FLATBED" ), CObjectInfo( "OBJ_VEHICLE_MORTAR" ), CObjectInfo( "OBJ_VEHICLE_TELEPORT_STATION" ), CObjectInfo( "OBJ_VEHICLE_TANK" ), CObjectInfo( "OBJ_VEHICLE_MOTORCYCLE" ), CObjectInfo( "OBJ_WALKER_STRIDER" ), CObjectInfo( "OBJ_WALKER_MINI_STRIDER" ), CObjectInfo( "OBJ_TOWER" ), CObjectInfo( "OBJ_TUNNEL" ), CObjectInfo( "OBJ_SANDBAG_BUNKER" ), CObjectInfo( "OBJ_BUNKER" ), CObjectInfo( "OBJ_DRAGONSTEETH" ), }; char* ReadAndAllocStringValue( KeyValues *pSub, const char *pName, const char *pFilename ) { const char *pValue = pSub->GetString( pName, NULL ); if ( !pValue ) { DevWarning( "Can't get key value '%s' from file '%s'.\n", pName, pFilename ); return ""; } int len = Q_strlen( pValue ) + 1; char *pAlloced = new char[ len ]; Assert( pAlloced ); Q_strncpy( pAlloced, pValue, len ); return pAlloced; } bool AreObjectInfosLoaded() { return g_ObjectInfos[0].m_pClassName != NULL; } void LoadObjectInfos( IBaseFileSystem *pFileSystem ) { const char *pFilename = "scripts/objects.txt"; // Make sure this stuff hasn't already been loaded. Assert( !AreObjectInfosLoaded() ); KeyValues *pValues = new KeyValues( "Object descriptions" ); if ( !pValues->LoadFromFile( pFileSystem, pFilename, "GAME" ) ) { Error( "Can't open %s for object info.", pFilename ); pValues->deleteThis(); return; } // Now read each class's information in. for ( int iObj=0; iObj < ARRAYSIZE( g_ObjectInfos ); iObj++ ) { CObjectInfo *pInfo = &g_ObjectInfos[iObj]; KeyValues *pSub = pValues->FindKey( pInfo->m_pObjectName ); if ( !pSub ) { Error( "Missing section '%s' from %s.", pInfo->m_pObjectName, pFilename ); pValues->deleteThis(); return; } // Read all the info in. if ( (pInfo->m_flBuildTime = pSub->GetFloat( "BuildTime", -999 )) == -999 || (pInfo->m_nMaxObjects = pSub->GetInt( "MaxObjects", -999 )) == -999 || (pInfo->m_Cost = pSub->GetInt( "Cost", -999 )) == -999 || (pInfo->m_CostMultiplierPerInstance = pSub->GetFloat( "CostMultiplier", -999 )) == -999 || (pInfo->m_UpgradeCost = pSub->GetInt( "UpgradeCost", -999 )) == -999 || (pInfo->m_MaxUpgradeLevel = pSub->GetInt( "MaxUpgradeLevel", -999 )) == -999 || (pInfo->m_SelectionSlot = pSub->GetInt( "SelectionSlot", -999 )) == -999 || (pInfo->m_SelectionPosition = pSub->GetInt( "SelectionPosition", -999 )) == -999 || (pInfo->m_flSapperAttachTime = pSub->GetInt( "SapperAttachTime", -999 )) == -999 ) { Error( "Missing data for object '%s' in %s.", pInfo->m_pObjectName, pFilename ); pValues->deleteThis(); return; } pInfo->m_pClassName = ReadAndAllocStringValue( pSub, "ClassName", pFilename ); pInfo->m_pStatusName = ReadAndAllocStringValue( pSub, "StatusName", pFilename ); pInfo->m_pBuilderWeaponName = ReadAndAllocStringValue( pSub, "BuilderWeaponName", pFilename ); pInfo->m_pBuilderPlacementString = ReadAndAllocStringValue( pSub, "BuilderPlacementString", pFilename ); pInfo->m_bSolidToPlayerMovement = pSub->GetInt( "SolidToPlayerMovement", 0 ) ? true : false; pInfo->m_pIconActive = ReadAndAllocStringValue( pSub, "Icon", pFilename ); } pValues->deleteThis(); } const CObjectInfo* GetObjectInfo( int iObject ) { Assert( iObject >= 0 && iObject < OBJ_LAST ); Assert( AreObjectInfosLoaded() ); return &g_ObjectInfos[iObject]; } //----------------------------------------------------------------------------- // Purpose: Return true if the specified class is allowed to build the specified object type //----------------------------------------------------------------------------- bool ClassCanBuild( int iClass, int iObjectType ) { for ( int i = 0; i < OBJ_LAST; i++ ) { // Hit the end? if ( g_TFClassInfos[iClass].m_pClassObjects[i] == OBJ_LAST ) return false; // Found it? if ( g_TFClassInfos[iClass].m_pClassObjects[i] == iObjectType ) return true; } return false; } //----------------------------------------------------------------------------- // Purpose: Return the cost of another object of the specified type // If bLast is set, return the cost of the last built object of the specified type //----------------------------------------------------------------------------- int CalculateObjectCost( int iObjectType, int iNumberOfObjects, int iTeam, bool bLast ) { if ( tf_cheapobjects.GetInt() ) { return 0; } // Find out how much the next object should cost if ( bLast ) { iNumberOfObjects = MAX(0,iNumberOfObjects-1); } int iCost = GetObjectInfo( iObjectType )->m_Cost; // If a cost is negative, it means the first object of that type is free, and then // it counts up as normal, using the negative value. if ( iCost < 0 ) { if ( iNumberOfObjects == 0 ) return 0; iCost *= -1; iNumberOfObjects--; } // MCVs have special rules: The team's first one is always free if ( iObjectType == OBJ_VEHICLE_TELEPORT_STATION ) { CTFTeam *pTeam = (CTFTeam *)GetGlobalTeam(iTeam); if ( pTeam && pTeam->GetNumObjects(OBJ_VEHICLE_TELEPORT_STATION) == 0 ) { iCost = 0; } } // Human objects cost less across the board if ( iTeam == TEAM_HUMANS ) { iCost = ( ((float)iCost) * 0.8 ); } // Calculate the cost based upon the number of objects for ( int i = 0; i < iNumberOfObjects; i++ ) { iCost *= GetObjectInfo( iObjectType )->m_CostMultiplierPerInstance; } return iCost; } //----------------------------------------------------------------------------- // Purpose: Calculate the cost to upgrade an object of a specific type //----------------------------------------------------------------------------- int CalculateObjectUpgrade( int iObjectType, int iObjectLevel ) { // Max level? if ( iObjectLevel >= GetObjectInfo( iObjectType )->m_MaxUpgradeLevel ) return 0; int iCost = GetObjectInfo( iObjectType )->m_UpgradeCost; for ( int i = 0; i < (iObjectLevel - 1); i++ ) { iCost *= OBJECT_UPGRADE_COST_MULTIPLIER_PER_LEVEL; } return iCost; } //-------------------------------------------------------------------------- // MORTAR //-------------------------------------------------------------------------- // Names for each mortar ammo type char *MortarAmmoNames[ MA_LASTAMMOTYPE ] = { "Normal Rounds", //"Smoke Rounds", "Cluster Rounds", "Starburst Rounds", }; // Techs needs for each mortar ammo type char *MortarAmmoTechs[ MA_LASTAMMOTYPE ] = { "", //"mortar_ammo_smoke", "mortar_ammo_cluster", "mortar_ammo_starburst", }; // Max amounts of each mortar ammo type in a single mortar int MortarAmmoMax[ MA_LASTAMMOTYPE ] = { -1, // -1 is infinite ammo //20, 20, 10, };