//====== Copyright © 1996-2004, Valve Corporation, All rights reserved. ======= // // Purpose: // //============================================================================= #include "cbase.h" #include "tf_shareddefs.h" #include "KeyValues.h" #include "takedamageinfo.h" #include "tf_gamerules.h" //----------------------------------------------------------------------------- // Teams. //----------------------------------------------------------------------------- const char *g_aTeamNames[TF_TEAM_COUNT] = { "Unassigned", "Spectator", "Red", "Blue" }; color32 g_aTeamColors[TF_TEAM_COUNT] = { { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 255, 0, 0, 0 }, { 0, 0, 255, 0 } }; //----------------------------------------------------------------------------- // Classes. //----------------------------------------------------------------------------- const char *g_aPlayerClassNames[] = { "#TF_Class_Name_Undefined", "#TF_Class_Name_Scout", "#TF_Class_Name_Sniper", "#TF_Class_Name_Soldier", "#TF_Class_Name_Demoman", "#TF_Class_Name_Medic", "#TF_Class_Name_HWGuy", "#TF_Class_Name_Pyro", "#TF_Class_Name_Spy", "#TF_Class_Name_Engineer" }; const char *g_aPlayerClassNames_NonLocalized[] = { "Undefined", "Scout", "Sniper", "Soldier", "Demoman", "Medic", "Heavy", "Pyro", "Spy", "Engineer" }; //----------------------------------------------------------------------------- // Gametypes. //----------------------------------------------------------------------------- const char *g_aGameTypeNames[] = { "Undefined", "#Gametype_CTF", "#Gametype_CP", }; //----------------------------------------------------------------------------- // Ammo. //----------------------------------------------------------------------------- const char *g_aAmmoNames[] = { "DUMMY AMMO", "TF_AMMO_PRIMARY", "TF_AMMO_SECONDARY", "TF_AMMO_METAL", "TF_AMMO_GRENADES1", "TF_AMMO_GRENADES2" }; //----------------------------------------------------------------------------- // Weapons. //----------------------------------------------------------------------------- const char *g_aWeaponNames[] = { "TF_WEAPON_NONE", "TF_WEAPON_BAT", "TF_WEAPON_BOTTLE", "TF_WEAPON_FIREAXE", "TF_WEAPON_CLUB", "TF_WEAPON_CROWBAR", "TF_WEAPON_KNIFE", "TF_WEAPON_FISTS", "TF_WEAPON_SHOVEL", "TF_WEAPON_WRENCH", "TF_WEAPON_BONESAW", "TF_WEAPON_SHOTGUN_PRIMARY", "TF_WEAPON_SHOTGUN_SOLDIER", "TF_WEAPON_SHOTGUN_HWG", "TF_WEAPON_SHOTGUN_PYRO", "TF_WEAPON_SCATTERGUN", "TF_WEAPON_SNIPERRIFLE", "TF_WEAPON_MINIGUN", "TF_WEAPON_SMG", "TF_WEAPON_SYRINGEGUN_MEDIC", "TF_WEAPON_TRANQ", "TF_WEAPON_ROCKETLAUNCHER", "TF_WEAPON_GRENADELAUNCHER", "TF_WEAPON_PIPEBOMBLAUNCHER", "TF_WEAPON_FLAMETHROWER", "TF_WEAPON_GRENADE_NORMAL", "TF_WEAPON_GRENADE_CONCUSSION", "TF_WEAPON_GRENADE_NAIL", "TF_WEAPON_GRENADE_MIRV", "TF_WEAPON_GRENADE_MIRV_DEMOMAN", "TF_WEAPON_GRENADE_NAPALM", "TF_WEAPON_GRENADE_GAS", "TF_WEAPON_GRENADE_EMP", "TF_WEAPON_GRENADE_CALTROP", "TF_WEAPON_GRENADE_PIPEBOMB", "TF_WEAPON_GRENADE_SMOKE_BOMB", "TF_WEAPON_GRENADE_HEAL", "TF_WEAPON_PISTOL", "TF_WEAPON_PISTOL_SCOUT", "TF_WEAPON_REVOLVER", "TF_WEAPON_NAILGUN", "TF_WEAPON_PDA", "TF_WEAPON_PDA_ENGINEER_BUILD", "TF_WEAPON_PDA_ENGINEER_DESTROY", "TF_WEAPON_PDA_SPY", "TF_WEAPON_BUILDER", "TF_WEAPON_MEDIGUN", "TF_WEAPON_GRENADE_MIRVBOMB", "TF_WEAPON_FLAMETHROWER_ROCKET", "TF_WEAPON_GRENADE_DEMOMAN", "TF_WEAPON_SENTRY_BULLET", "TF_WEAPON_SENTRY_ROCKET", "TF_WEAPON_DISPENSER", "TF_WEAPON_INVIS", "TF_WEAPON_COUNT", // end marker, do not add below here }; int g_aWeaponDamageTypes[] = { DMG_GENERIC, // TF_WEAPON_NONE DMG_CLUB, // TF_WEAPON_BAT, DMG_CLUB, // TF_WEAPON_BOTTLE, DMG_CLUB, // TF_WEAPON_FIREAXE, DMG_CLUB, // TF_WEAPON_CLUB, DMG_CLUB, // TF_WEAPON_CROWBAR, DMG_SLASH, // TF_WEAPON_KNIFE, DMG_CLUB, // TF_WEAPON_FISTS, DMG_CLUB, // TF_WEAPON_SHOVEL, DMG_CLUB, // TF_WEAPON_WRENCH, DMG_SLASH, // TF_WEAPON_BONESAW, DMG_BUCKSHOT | DMG_USEDISTANCEMOD, // TF_WEAPON_SHOTGUN_PRIMARY, DMG_BUCKSHOT | DMG_USEDISTANCEMOD, // TF_WEAPON_SHOTGUN_SOLDIER, DMG_BUCKSHOT | DMG_USEDISTANCEMOD, // TF_WEAPON_SHOTGUN_HWG, DMG_BUCKSHOT | DMG_USEDISTANCEMOD, // TF_WEAPON_SHOTGUN_PYRO, DMG_BUCKSHOT | DMG_USEDISTANCEMOD, // TF_WEAPON_SCATTERGUN, DMG_BULLET | DMG_USE_HITLOCATIONS, // TF_WEAPON_SNIPERRIFLE, DMG_BULLET | DMG_USEDISTANCEMOD, // TF_WEAPON_MINIGUN, DMG_BULLET | DMG_USEDISTANCEMOD, // TF_WEAPON_SMG, DMG_BULLET | DMG_USEDISTANCEMOD | DMG_NOCLOSEDISTANCEMOD | DMG_PREVENT_PHYSICS_FORCE, // TF_WEAPON_SYRINGEGUN_MEDIC, DMG_BULLET | DMG_USEDISTANCEMOD | DMG_PREVENT_PHYSICS_FORCE | DMG_PARALYZE, // TF_WEAPON_TRANQ, DMG_BLAST | DMG_HALF_FALLOFF | DMG_USEDISTANCEMOD, // TF_WEAPON_ROCKETLAUNCHER, DMG_BLAST | DMG_HALF_FALLOFF | DMG_USEDISTANCEMOD, // TF_WEAPON_GRENADELAUNCHER, DMG_BLAST | DMG_HALF_FALLOFF, // TF_WEAPON_PIPEBOMBLAUNCHER, DMG_IGNITE | DMG_PREVENT_PHYSICS_FORCE | DMG_PREVENT_PHYSICS_FORCE, // TF_WEAPON_FLAMETHROWER, DMG_BLAST | DMG_HALF_FALLOFF, // TF_WEAPON_GRENADE_NORMAL, DMG_SONIC | DMG_HALF_FALLOFF, // TF_WEAPON_GRENADE_CONCUSSION, DMG_BULLET | DMG_HALF_FALLOFF, // TF_WEAPON_GRENADE_NAIL, DMG_BLAST | DMG_HALF_FALLOFF, // TF_WEAPON_GRENADE_MIRV, DMG_BLAST | DMG_HALF_FALLOFF, // TF_WEAPON_GRENADE_MIRV_DEMOMAN, DMG_BURN | DMG_RADIUS_MAX, // TF_WEAPON_GRENADE_NAPALM, DMG_POISON | DMG_HALF_FALLOFF, // TF_WEAPON_GRENADE_GAS, DMG_BLAST | DMG_HALF_FALLOFF | DMG_PREVENT_PHYSICS_FORCE, // TF_WEAPON_GRENADE_EMP, DMG_GENERIC, // TF_WEAPON_GRENADE_CALTROP, DMG_BLAST | DMG_HALF_FALLOFF, // TF_WEAPON_GRENADE_PIPEBOMB, DMG_GENERIC, // TF_WEAPON_GRENADE_SMOKE_BOMB, DMG_GENERIC, // TF_WEAPON_GRENADE_HEAL DMG_BULLET | DMG_USEDISTANCEMOD, // TF_WEAPON_PISTOL, DMG_BULLET | DMG_USEDISTANCEMOD, // TF_WEAPON_PISTOL_SCOUT, DMG_BULLET | DMG_USEDISTANCEMOD, // TF_WEAPON_REVOLVER, DMG_BULLET | DMG_USEDISTANCEMOD | DMG_NOCLOSEDISTANCEMOD | DMG_PREVENT_PHYSICS_FORCE, // TF_WEAPON_NAILGUN, DMG_BULLET, // TF_WEAPON_PDA, DMG_BULLET, // TF_WEAPON_PDA_ENGINEER_BUILD, DMG_BULLET, // TF_WEAPON_PDA_ENGINEER_DESTROY, DMG_BULLET, // TF_WEAPON_PDA_SPY, DMG_BULLET, // TF_WEAPON_BUILDER DMG_BULLET, // TF_WEAPON_MEDIGUN DMG_BLAST, // TF_WEAPON_GRENADE_MIRVBOMB DMG_BLAST | DMG_IGNITE | DMG_RADIUS_MAX, // TF_WEAPON_FLAMETHROWER_ROCKET DMG_BLAST | DMG_HALF_FALLOFF, // TF_WEAPON_GRENADE_DEMOMAN DMG_GENERIC, // TF_WEAPON_SENTRY_BULLET DMG_GENERIC, // TF_WEAPON_SENTRY_ROCKET DMG_GENERIC, // TF_WEAPON_DISPENSER DMG_GENERIC, // TF_WEAPON_INVIS // This is a special entry that must match with TF_WEAPON_COUNT // to protect against updating the weapon list without updating this list TF_DMG_SENTINEL_VALUE }; const char *g_szProjectileNames[] = { "", "projectile_bullet", "projectile_rocket", "projectile_pipe", "projectile_pipe_remote", "projectile_syringe", }; // these map to the projectiles named in g_szProjectileNames int g_iProjectileWeapons[] = { TF_WEAPON_NONE, TF_WEAPON_PISTOL, TF_WEAPON_ROCKETLAUNCHER, TF_WEAPON_PIPEBOMBLAUNCHER, TF_WEAPON_GRENADELAUNCHER, TF_WEAPON_SYRINGEGUN_MEDIC }; const char *g_pszHintMessages[] = { "#Hint_spotted_a_friend", "#Hint_spotted_an_enemy", "#Hint_killing_enemies_is_good", "#Hint_out_of_ammo", "#Hint_turn_off_hints", "#Hint_pickup_ammo", "#Hint_Cannot_Teleport_With_Flag", "#Hint_Cannot_Cloak_With_Flag", "#Hint_Cannot_Disguise_With_Flag", "#Hint_Cannot_Attack_While_Cloaked", "#Hint_ClassMenu", // Grenades "#Hint_gren_caltrops", "#Hint_gren_concussion", "#Hint_gren_emp", "#Hint_gren_gas", "#Hint_gren_mirv", "#Hint_gren_nail", "#Hint_gren_napalm", "#Hint_gren_normal", // Altfires "#Hint_altfire_sniperrifle", "#Hint_altfire_flamethrower", "#Hint_altfire_grenadelauncher", "#Hint_altfire_pipebomblauncher", "#Hint_altfire_rotate_building", // Soldier "#Hint_Soldier_rpg_reload", // Engineer "#Hint_Engineer_use_wrench_onown", "#Hint_Engineer_use_wrench_onother", "#Hint_Engineer_use_wrench_onfriend", "#Hint_Engineer_build_sentrygun", "#Hint_Engineer_build_dispenser", "#Hint_Engineer_build_teleporters", "#Hint_Engineer_pickup_metal", "#Hint_Engineer_repair_object", "#Hint_Engineer_metal_to_upgrade", "#Hint_Engineer_upgrade_sentrygun", "#Hint_object_has_sapper", "#Hint_object_your_object_sapped", "#Hint_enemy_using_dispenser", "#Hint_enemy_using_tp_entrance", "#Hint_enemy_using_tp_exit", }; //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- int GetWeaponId( const char *pszWeaponName ) { // if this doesn't match, you need to add missing weapons to the array assert( ARRAYSIZE( g_aWeaponNames ) == ( TF_WEAPON_COUNT + 1 ) ); for ( int iWeapon = 0; iWeapon < ARRAYSIZE( g_aWeaponNames ); ++iWeapon ) { if ( !Q_stricmp( pszWeaponName, g_aWeaponNames[iWeapon] ) ) return iWeapon; } return TF_WEAPON_NONE; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- const char *WeaponIdToAlias( int iWeapon ) { // if this doesn't match, you need to add missing weapons to the array assert( ARRAYSIZE( g_aWeaponNames ) == ( TF_WEAPON_COUNT + 1 ) ); if ( ( iWeapon >= ARRAYSIZE( g_aWeaponNames ) ) || ( iWeapon < 0 ) ) return NULL; return g_aWeaponNames[iWeapon]; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- const char *WeaponIdToClassname( int iWeapon ) { const char *pszAlias = WeaponIdToAlias( iWeapon ); if ( pszAlias == NULL ) return NULL; static char szClassname[128]; V_strncpy( szClassname, pszAlias, sizeof( szClassname ) ); V_strlower( szClassname ); return szClassname; } #ifdef GAME_DLL //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- int GetWeaponFromDamage( const CTakeDamageInfo &info ) { int iWeapon = TF_WEAPON_NONE; // Work out what killed the player, and send a message to all clients about it const char *killer_weapon_name = TFGameRules()->GetKillingWeaponName( info, NULL ); if ( !Q_strnicmp( killer_weapon_name, "tf_projectile", 13 ) ) { for( int i = 0; i < ARRAYSIZE( g_szProjectileNames ); i++ ) { if ( !Q_stricmp( &killer_weapon_name[ 3 ], g_szProjectileNames[ i ] ) ) { iWeapon = g_iProjectileWeapons[ i ]; break; } } } else { int iLen = Q_strlen( killer_weapon_name ); // strip off _projectile from projectiles shot from other projectiles if ( ( iLen < 256 ) && ( iLen > 11 ) && !Q_stricmp( &killer_weapon_name[ iLen - 11 ], "_projectile" ) ) { char temp[ 256 ]; Q_strcpy( temp, killer_weapon_name ); temp[ iLen - 11 ] = 0; // set the weapon used iWeapon = GetWeaponId( temp ); } else { // set the weapon used iWeapon = GetWeaponId( killer_weapon_name ); } } return iWeapon; } #endif // ------------------------------------------------------------------------------------------------ // // 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_pIconActive = NULL; m_pIconInactive = NULL; m_pViewModel = NULL; m_pPlayerModel = NULL; m_iDisplayPriority = 0; m_bVisibleInWeaponSelection = true; m_pExplodeSound = NULL; m_pExplosionParticleEffect = NULL; m_bAutoSwitchTo = false; } CObjectInfo::~CObjectInfo() { delete [] m_pClassName; delete [] m_pStatusName; delete [] m_pBuilderWeaponName; delete [] m_pBuilderPlacementString; delete [] m_pIconActive; delete [] m_pIconInactive; delete [] m_pViewModel; delete [] m_pPlayerModel; delete [] m_pExplodeSound; delete [] m_pExplosionParticleEffect; } CObjectInfo g_ObjectInfos[OBJ_LAST] = { CObjectInfo( "OBJ_DISPENSER" ), CObjectInfo( "OBJ_TELEPORTER_ENTRANCE" ), CObjectInfo( "OBJ_TELEPORTER_EXIT" ), CObjectInfo( "OBJ_SENTRYGUN" ), CObjectInfo( "OBJ_ATTACHMENT_SAPPER" ), }; //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- int GetBuildableId( const char *pszBuildableName ) { for ( int iBuildable = 0; iBuildable < OBJ_LAST; ++iBuildable ) { if ( !Q_stricmp( pszBuildableName, g_ObjectInfos[iBuildable].m_pObjectName ) ) return iBuildable; } return OBJ_LAST; } 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 ) { 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, "IconActive", pFilename ); pInfo->m_pIconInactive = ReadAndAllocStringValue( pSub, "IconInactive", pFilename ); pInfo->m_pViewModel = ReadAndAllocStringValue( pSub, "Viewmodel", pFilename ); pInfo->m_pPlayerModel = ReadAndAllocStringValue( pSub, "Playermodel", pFilename ); pInfo->m_iDisplayPriority = pSub->GetInt( "DisplayPriority", 0 ); pInfo->m_pHudStatusIcon = ReadAndAllocStringValue( pSub, "HudStatusIcon", pFilename ); pInfo->m_bVisibleInWeaponSelection = ( pSub->GetInt( "VisibleInWeaponSelection", 1 ) > 0 ); pInfo->m_pExplodeSound = ReadAndAllocStringValue( pSub, "ExplodeSound", pFilename ); pInfo->m_pExplosionParticleEffect = ReadAndAllocStringValue( pSub, "ExplodeEffect", pFilename ); pInfo->m_bAutoSwitchTo = ( pSub->GetInt( "autoswitchto", 0 ) > 0 ); pInfo->m_iMetalToDropInGibs = pSub->GetInt( "MetalToDropInGibs", 0 ); } pValues->deleteThis(); } const CObjectInfo* GetObjectInfo( int iObject ) { Assert( iObject >= 0 && iObject < OBJ_LAST ); Assert( AreObjectInfosLoaded() ); return &g_ObjectInfos[iObject]; } ConVar tf_cheapobjects( "tf_cheapobjects","0", FCVAR_CHEAT | FCVAR_DEVELOPMENTONLY | FCVAR_REPLICATED, "Set to 1 and all objects will cost 0" ); //----------------------------------------------------------------------------- // 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 // // Note: Used to contain logic from tf2 that multiple instances of the same object // cost different amounts. See tf2/game_shared/tf_shareddefs.cpp for details //----------------------------------------------------------------------------- int CalculateObjectCost( int iObjectType ) { if ( tf_cheapobjects.GetInt() ) { return 0; } int iCost = GetObjectInfo( iObjectType )->m_Cost; 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; } //----------------------------------------------------------------------------- // 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; */ return ( iClass == TF_CLASS_ENGINEER ); }