Team Fortress 2 Source Code as on 22/4/2020
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.

422 lines
11 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: The TF Game rules
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================//
  7. #include "cbase.h"
  8. #include "tfc_gamerules.h"
  9. #include "ammodef.h"
  10. #include "KeyValues.h"
  11. #include "weapon_tfcbase.h"
  12. #ifdef CLIENT_DLL
  13. #include "c_tfc_player.h"
  14. #else
  15. #include "voice_gamemgr.h"
  16. #include "team.h"
  17. #include "tfc_bot_temp.h"
  18. #include "tfc_player.h"
  19. #include "tfc_timer.h"
  20. #include "tfc_team.h"
  21. #endif
  22. // memdbgon must be the last include file in a .cpp file!!!
  23. #include "tier0/memdbgon.h"
  24. #ifndef CLIENT_DLL
  25. LINK_ENTITY_TO_CLASS(info_player_terrorist, CPointEntity);
  26. LINK_ENTITY_TO_CLASS(info_player_counterterrorist,CPointEntity);
  27. #endif
  28. REGISTER_GAMERULES_CLASS( CTFCGameRules );
  29. BEGIN_NETWORK_TABLE_NOBASE( CTFCGameRules, DT_TFCGameRules )
  30. END_NETWORK_TABLE()
  31. LINK_ENTITY_TO_CLASS( tfc_gamerules, CTFCGameRulesProxy );
  32. IMPLEMENT_NETWORKCLASS_ALIASED( TFCGameRulesProxy, DT_TFCGameRulesProxy )
  33. #ifdef CLIENT_DLL
  34. void RecvProxy_TFCGameRules( const RecvProp *pProp, void **pOut, void *pData, int objectID )
  35. {
  36. CTFCGameRules *pRules = TFCGameRules();
  37. Assert( pRules );
  38. *pOut = pRules;
  39. }
  40. BEGIN_RECV_TABLE( CTFCGameRulesProxy, DT_TFCGameRulesProxy )
  41. RecvPropDataTable( "tfc_gamerules_data", 0, 0, &REFERENCE_RECV_TABLE( DT_TFCGameRules ), RecvProxy_TFCGameRules )
  42. END_RECV_TABLE()
  43. #else
  44. void *SendProxy_TFCGameRules( const SendProp *pProp, const void *pStructBase, const void *pData, CSendProxyRecipients *pRecipients, int objectID )
  45. {
  46. CTFCGameRules *pRules = TFCGameRules();
  47. Assert( pRules );
  48. pRecipients->SetAllRecipients();
  49. return pRules;
  50. }
  51. BEGIN_SEND_TABLE( CTFCGameRulesProxy, DT_TFCGameRulesProxy )
  52. SendPropDataTable( "tfc_gamerules_data", 0, &REFERENCE_SEND_TABLE( DT_TFCGameRules ), SendProxy_TFCGameRules )
  53. END_SEND_TABLE()
  54. #endif
  55. ConVar mp_fadetoblack(
  56. "mp_fadetoblack",
  57. "0",
  58. FCVAR_REPLICATED,
  59. "fade a player's screen to black when he dies" );
  60. // (We clamp ammo ourselves elsewhere).
  61. ConVar ammo_max( "ammo_max", "5000", FCVAR_REPLICATED );
  62. CTFCGameRules::CTFCGameRules()
  63. {
  64. CTF_Map = true;
  65. #ifdef GAME_DLL
  66. // Create the team managers
  67. for ( int i = 0; i < ARRAYSIZE( teamnames ); i++ )
  68. {
  69. CTeam *pTeam = static_cast<CTeam*>(CreateEntityByName( "tfc_team_manager" ));
  70. pTeam->Init( teamnames[i], i );
  71. g_Teams.AddToTail( pTeam );
  72. }
  73. #endif
  74. }
  75. #ifdef CLIENT_DLL
  76. #else
  77. int cease_fire;
  78. int no_cease_fire_text;
  79. // --------------------------------------------------------------------------------------------------- //
  80. // Voice helper
  81. // --------------------------------------------------------------------------------------------------- //
  82. class CVoiceGameMgrHelper : public IVoiceGameMgrHelper
  83. {
  84. public:
  85. virtual bool CanPlayerHearPlayer( CBasePlayer *pListener, CBasePlayer *pTalker )
  86. {
  87. // Dead players can only be heard by other dead team mates
  88. if ( pTalker->IsAlive() == false )
  89. {
  90. if ( pListener->IsAlive() == false )
  91. return ( pListener->InSameTeam( pTalker ) );
  92. return false;
  93. }
  94. return ( pListener->InSameTeam( pTalker ) );
  95. }
  96. };
  97. CVoiceGameMgrHelper g_VoiceGameMgrHelper;
  98. IVoiceGameMgrHelper *g_pVoiceGameMgrHelper = &g_VoiceGameMgrHelper;
  99. // --------------------------------------------------------------------------------------------------- //
  100. // Globals.
  101. // --------------------------------------------------------------------------------------------------- //
  102. // NOTE: the indices here must match TEAM_TERRORIST, TEAM_CT, TEAM_SPECTATOR, etc.
  103. char *sTeamNames[] =
  104. {
  105. "Unassigned",
  106. "Spectator",
  107. "Terrorist",
  108. "Counter-Terrorist"
  109. };
  110. // --------------------------------------------------------------------------------------------------- //
  111. // Global helper functions.
  112. // --------------------------------------------------------------------------------------------------- //
  113. // World.cpp calls this but we don't use it in TFC.
  114. void InitBodyQue()
  115. {
  116. }
  117. //-----------------------------------------------------------------------------
  118. // Purpose:
  119. //-----------------------------------------------------------------------------
  120. CTFCGameRules::~CTFCGameRules()
  121. {
  122. // Note, don't delete each team since they are in the gEntList and will
  123. // automatically be deleted from there, instead.
  124. g_Teams.Purge();
  125. }
  126. //-----------------------------------------------------------------------------
  127. // Purpose: TF2 Specific Client Commands
  128. // Input :
  129. // Output :
  130. //-----------------------------------------------------------------------------
  131. bool CTFCGameRules::ClientCommand( CBaseEntity *pEdict, const CCommand &args )
  132. {
  133. CTFCPlayer *pPlayer = ToTFCPlayer( pEdict );
  134. if( pPlayer->ClientCommand( args ) )
  135. return true;
  136. return BaseClass::ClientCommand( pEdict, args );
  137. }
  138. //-----------------------------------------------------------------------------
  139. // Purpose: Player has just spawned. Equip them.
  140. //-----------------------------------------------------------------------------
  141. void CTFCGameRules::RadiusDamage( const CTakeDamageInfo &info, const Vector &vecSrcIn, float flRadius, int iClassIgnore )
  142. {
  143. RadiusDamage( info, vecSrcIn, flRadius, iClassIgnore, false );
  144. }
  145. // Add the ability to ignore the world trace
  146. void CTFCGameRules::RadiusDamage( const CTakeDamageInfo &info, const Vector &vecSrcIn, float flRadius, int iClassIgnore, bool bIgnoreWorld )
  147. {
  148. CBaseEntity *pEntity = NULL;
  149. trace_t tr;
  150. float flAdjustedDamage, falloff;
  151. Vector vecSpot;
  152. Vector vecToTarget;
  153. Vector vecEndPos;
  154. Vector vecSrc = vecSrcIn;
  155. if ( flRadius )
  156. falloff = info.GetDamage() / flRadius;
  157. else
  158. falloff = 1.0;
  159. int bInWater = (UTIL_PointContents ( vecSrc ) & MASK_WATER) ? true : false;
  160. vecSrc.z += 1;// in case grenade is lying on the ground
  161. // iterate on all entities in the vicinity.
  162. for ( CEntitySphereQuery sphere( vecSrc, flRadius ); pEntity = sphere.GetCurrentEntity(); sphere.NextEntity() )
  163. {
  164. if ( pEntity->m_takedamage != DAMAGE_NO )
  165. {
  166. // UNDONE: this should check a damage mask, not an ignore
  167. if ( iClassIgnore != CLASS_NONE && pEntity->Classify() == iClassIgnore )
  168. {// houndeyes don't hurt other houndeyes with their attack
  169. continue;
  170. }
  171. // blast's don't tavel into or out of water
  172. if (bInWater && pEntity->GetWaterLevel() == 0)
  173. continue;
  174. if (!bInWater && pEntity->GetWaterLevel() == 3)
  175. continue;
  176. // radius damage can only be blocked by the world
  177. vecSpot = pEntity->BodyTarget( vecSrc );
  178. bool bHit = false;
  179. if( bIgnoreWorld )
  180. {
  181. vecEndPos = vecSpot;
  182. bHit = true;
  183. }
  184. else
  185. {
  186. UTIL_TraceLine( vecSrc, vecSpot, MASK_SOLID_BRUSHONLY, info.GetInflictor(), COLLISION_GROUP_NONE, &tr );
  187. if (tr.startsolid)
  188. {
  189. // if we're stuck inside them, fixup the position and distance
  190. tr.endpos = vecSrc;
  191. tr.fraction = 0.0;
  192. }
  193. vecEndPos = tr.endpos;
  194. if( tr.fraction == 1.0 || tr.m_pEnt == pEntity )
  195. {
  196. bHit = true;
  197. }
  198. }
  199. if ( bHit )
  200. {
  201. // the explosion can 'see' this entity, so hurt them!
  202. //vecToTarget = ( vecSrc - vecEndPos );
  203. vecToTarget = ( vecEndPos - vecSrc );
  204. // decrease damage for an ent that's farther from the bomb.
  205. flAdjustedDamage = vecToTarget.Length() * falloff;
  206. flAdjustedDamage = info.GetDamage() - flAdjustedDamage;
  207. if ( flAdjustedDamage > 0 )
  208. {
  209. CTakeDamageInfo adjustedInfo = info;
  210. adjustedInfo.SetDamage( flAdjustedDamage );
  211. Vector dir = vecToTarget;
  212. VectorNormalize( dir );
  213. // If we don't have a damage force, manufacture one
  214. if ( adjustedInfo.GetDamagePosition() == vec3_origin || adjustedInfo.GetDamageForce() == vec3_origin )
  215. {
  216. CalculateExplosiveDamageForce( &adjustedInfo, dir, vecSrc, 1.5 /* explosion scale! */ );
  217. }
  218. else
  219. {
  220. // Assume the force passed in is the maximum force. Decay it based on falloff.
  221. float flForce = adjustedInfo.GetDamageForce().Length() * falloff;
  222. adjustedInfo.SetDamageForce( dir * flForce );
  223. adjustedInfo.SetDamagePosition( vecSrc );
  224. }
  225. pEntity->TakeDamage( adjustedInfo );
  226. // Now hit all triggers along the way that respond to damage...
  227. pEntity->TraceAttackToTriggers( adjustedInfo, vecSrc, vecEndPos, dir );
  228. }
  229. }
  230. }
  231. }
  232. }
  233. void CTFCGameRules::Think()
  234. {
  235. Timer_UpdateAll();
  236. BaseClass::Think();
  237. }
  238. const char *CTFCGameRules::GetChatPrefix( bool bTeamOnly, CBasePlayer *pPlayer )
  239. {
  240. return "(chat prefix)";
  241. }
  242. bool CTFCGameRules::IsInPreMatch() const
  243. {
  244. // TFCTODO return (cb_prematch_time > gpGlobals->time)
  245. return false;
  246. }
  247. float CTFCGameRules::GetPreMatchEndTime() const
  248. {
  249. //TFCTODO: implement this.
  250. return gpGlobals->curtime;
  251. }
  252. void CTFCGameRules::TFCGoToIntermission()
  253. {
  254. // TFCTODO: implement this.
  255. Assert( false );
  256. }
  257. #endif
  258. bool CTFCGameRules::ShouldCollide( int collisionGroup0, int collisionGroup1 )
  259. {
  260. if ( collisionGroup0 > collisionGroup1 )
  261. {
  262. // swap so that lowest is always first
  263. swap(collisionGroup0,collisionGroup1);
  264. }
  265. //Don't stand on COLLISION_GROUP_WEAPONs
  266. if( collisionGroup0 == COLLISION_GROUP_PLAYER_MOVEMENT &&
  267. collisionGroup1 == COLLISION_GROUP_WEAPON )
  268. {
  269. return false;
  270. }
  271. if ( collisionGroup0 == COLLISION_GROUP_PLAYER )
  272. {
  273. // Players don't collide with objects or other players
  274. if ( collisionGroup1 == COLLISION_GROUP_PLAYER )
  275. return false;
  276. }
  277. if ( collisionGroup1 == COLLISION_GROUP_PLAYER_MOVEMENT )
  278. {
  279. // This is only for probing, so it better not be on both sides!!!
  280. Assert( collisionGroup0 != COLLISION_GROUP_PLAYER_MOVEMENT );
  281. // No collide with players any more
  282. // Nor with objects or grenades
  283. switch ( collisionGroup0 )
  284. {
  285. default:
  286. break;
  287. case COLLISION_GROUP_PLAYER:
  288. return false;
  289. }
  290. }
  291. return BaseClass::ShouldCollide( collisionGroup0, collisionGroup1 );
  292. }
  293. //-----------------------------------------------------------------------------
  294. // Purpose: Init CS ammo definitions
  295. //-----------------------------------------------------------------------------
  296. // shared ammo definition
  297. // JAY: Trying to make a more physical bullet response
  298. #define BULLET_MASS_GRAINS_TO_LB(grains) (0.002285*(grains)/16.0f)
  299. #define BULLET_MASS_GRAINS_TO_KG(grains) lbs2kg(BULLET_MASS_GRAINS_TO_LB(grains))
  300. // exaggerate all of the forces, but use real numbers to keep them consistent
  301. #define BULLET_IMPULSE_EXAGGERATION 1
  302. // convert a velocity in ft/sec and a mass in grains to an impulse in kg in/s
  303. #define BULLET_IMPULSE(grains, ftpersec) ((ftpersec)*12*BULLET_MASS_GRAINS_TO_KG(grains)*BULLET_IMPULSE_EXAGGERATION)
  304. CAmmoDef* GetAmmoDef()
  305. {
  306. static CAmmoDef def;
  307. static bool bInitted = false;
  308. if ( !bInitted )
  309. {
  310. bInitted = true;
  311. // Start at 1 here and skip the dummy ammo type to make CAmmoDef use the same indices
  312. // as our #defines.
  313. for ( int i=1; i < TFC_NUM_AMMO_TYPES; i++ )
  314. {
  315. def.AddAmmoType( g_AmmoTypeNames[i], DMG_BULLET, TRACER_LINE, 0, 0, "ammo_max", 2400, 10, 14 );
  316. Assert( def.Index( g_AmmoTypeNames[i] ) == i );
  317. }
  318. }
  319. return &def;
  320. }