Modified source engine (2017) developed by valve and leaked in 2020. Not for commercial purporses
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

606 lines
19 KiB

5 years ago
//========= 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 <KeyValues.h>
#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,
};