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.

369 lines
10 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 "sdk_gamerules.h"
  9. #include "ammodef.h"
  10. #include "KeyValues.h"
  11. #include "weapon_sdkbase.h"
  12. #ifdef CLIENT_DLL
  13. #else
  14. #include "voice_gamemgr.h"
  15. #include "team.h"
  16. #endif
  17. // memdbgon must be the last include file in a .cpp file!!!
  18. #include "tier0/memdbgon.h"
  19. #ifndef CLIENT_DLL
  20. LINK_ENTITY_TO_CLASS(info_player_terrorist, CPointEntity);
  21. LINK_ENTITY_TO_CLASS(info_player_counterterrorist,CPointEntity);
  22. #endif
  23. REGISTER_GAMERULES_CLASS( CSDKGameRules );
  24. BEGIN_NETWORK_TABLE_NOBASE( CSDKGameRules, DT_SDKGameRules )
  25. END_NETWORK_TABLE()
  26. LINK_ENTITY_TO_CLASS( sdk_gamerules, CSDKGameRulesProxy );
  27. IMPLEMENT_NETWORKCLASS_ALIASED( SDKGameRulesProxy, DT_SDKGameRulesProxy )
  28. #ifdef CLIENT_DLL
  29. void RecvProxy_SDKGameRules( const RecvProp *pProp, void **pOut, void *pData, int objectID )
  30. {
  31. CSDKGameRules *pRules = SDKGameRules();
  32. Assert( pRules );
  33. *pOut = pRules;
  34. }
  35. BEGIN_RECV_TABLE( CSDKGameRulesProxy, DT_SDKGameRulesProxy )
  36. RecvPropDataTable( "sdk_gamerules_data", 0, 0, &REFERENCE_RECV_TABLE( DT_SDKGameRules ), RecvProxy_SDKGameRules )
  37. END_RECV_TABLE()
  38. #else
  39. void *SendProxy_SDKGameRules( const SendProp *pProp, const void *pStructBase, const void *pData, CSendProxyRecipients *pRecipients, int objectID )
  40. {
  41. CSDKGameRules *pRules = SDKGameRules();
  42. Assert( pRules );
  43. pRecipients->SetAllRecipients();
  44. return pRules;
  45. }
  46. BEGIN_SEND_TABLE( CSDKGameRulesProxy, DT_SDKGameRulesProxy )
  47. SendPropDataTable( "sdk_gamerules_data", 0, &REFERENCE_SEND_TABLE( DT_SDKGameRules ), SendProxy_SDKGameRules )
  48. END_SEND_TABLE()
  49. #endif
  50. #ifdef CLIENT_DLL
  51. #else
  52. // --------------------------------------------------------------------------------------------------- //
  53. // Voice helper
  54. // --------------------------------------------------------------------------------------------------- //
  55. class CVoiceGameMgrHelper : public IVoiceGameMgrHelper
  56. {
  57. public:
  58. virtual bool CanPlayerHearPlayer( CBasePlayer *pListener, CBasePlayer *pTalker )
  59. {
  60. // Dead players can only be heard by other dead team mates
  61. if ( pTalker->IsAlive() == false )
  62. {
  63. if ( pListener->IsAlive() == false )
  64. return ( pListener->InSameTeam( pTalker ) );
  65. return false;
  66. }
  67. return ( pListener->InSameTeam( pTalker ) );
  68. }
  69. };
  70. CVoiceGameMgrHelper g_VoiceGameMgrHelper;
  71. IVoiceGameMgrHelper *g_pVoiceGameMgrHelper = &g_VoiceGameMgrHelper;
  72. // --------------------------------------------------------------------------------------------------- //
  73. // Globals.
  74. // --------------------------------------------------------------------------------------------------- //
  75. // NOTE: the indices here must match TEAM_TERRORIST, TEAM_CT, TEAM_SPECTATOR, etc.
  76. char *sTeamNames[] =
  77. {
  78. "Unassigned",
  79. "Spectator",
  80. "Terrorist",
  81. "Counter-Terrorist"
  82. };
  83. // --------------------------------------------------------------------------------------------------- //
  84. // Global helper functions.
  85. // --------------------------------------------------------------------------------------------------- //
  86. // World.cpp calls this but we don't use it in SDK.
  87. void InitBodyQue()
  88. {
  89. }
  90. // --------------------------------------------------------------------------------------------------- //
  91. // CSDKGameRules implementation.
  92. // --------------------------------------------------------------------------------------------------- //
  93. CSDKGameRules::CSDKGameRules()
  94. {
  95. // Create the team managers
  96. for ( int i = 0; i < ARRAYSIZE( sTeamNames ); i++ )
  97. {
  98. CTeam *pTeam = static_cast<CTeam*>(CreateEntityByName( "sdk_team_manager" ));
  99. pTeam->Init( sTeamNames[i], i );
  100. g_Teams.AddToTail( pTeam );
  101. }
  102. }
  103. //-----------------------------------------------------------------------------
  104. // Purpose:
  105. //-----------------------------------------------------------------------------
  106. CSDKGameRules::~CSDKGameRules()
  107. {
  108. // Note, don't delete each team since they are in the gEntList and will
  109. // automatically be deleted from there, instead.
  110. g_Teams.Purge();
  111. }
  112. //-----------------------------------------------------------------------------
  113. // Purpose: TF2 Specific Client Commands
  114. // Input :
  115. // Output :
  116. //-----------------------------------------------------------------------------
  117. bool CSDKGameRules::ClientCommand( CBaseEntity *pEdict, const CCommand &args )
  118. {
  119. return BaseClass::ClientCommand( pEdict, args );
  120. }
  121. //-----------------------------------------------------------------------------
  122. // Purpose: Player has just spawned. Equip them.
  123. //-----------------------------------------------------------------------------
  124. void CSDKGameRules::RadiusDamage( const CTakeDamageInfo &info, const Vector &vecSrcIn, float flRadius, int iClassIgnore )
  125. {
  126. RadiusDamage( info, vecSrcIn, flRadius, iClassIgnore, false );
  127. }
  128. // Add the ability to ignore the world trace
  129. void CSDKGameRules::RadiusDamage( const CTakeDamageInfo &info, const Vector &vecSrcIn, float flRadius, int iClassIgnore, bool bIgnoreWorld )
  130. {
  131. CBaseEntity *pEntity = NULL;
  132. trace_t tr;
  133. float flAdjustedDamage, falloff;
  134. Vector vecSpot;
  135. Vector vecToTarget;
  136. Vector vecEndPos;
  137. Vector vecSrc = vecSrcIn;
  138. if ( flRadius )
  139. falloff = info.GetDamage() / flRadius;
  140. else
  141. falloff = 1.0;
  142. int bInWater = (UTIL_PointContents ( vecSrc ) & MASK_WATER) ? true : false;
  143. vecSrc.z += 1;// in case grenade is lying on the ground
  144. // iterate on all entities in the vicinity.
  145. for ( CEntitySphereQuery sphere( vecSrc, flRadius ); ( pEntity = sphere.GetCurrentEntity() ) != NULL; sphere.NextEntity() )
  146. {
  147. if ( pEntity->m_takedamage != DAMAGE_NO )
  148. {
  149. // UNDONE: this should check a damage mask, not an ignore
  150. if ( iClassIgnore != CLASS_NONE && pEntity->Classify() == iClassIgnore )
  151. {// houndeyes don't hurt other houndeyes with their attack
  152. continue;
  153. }
  154. // blast's don't tavel into or out of water
  155. if (bInWater && pEntity->GetWaterLevel() == 0)
  156. continue;
  157. if (!bInWater && pEntity->GetWaterLevel() == 3)
  158. continue;
  159. // radius damage can only be blocked by the world
  160. vecSpot = pEntity->BodyTarget( vecSrc );
  161. bool bHit = false;
  162. if( bIgnoreWorld )
  163. {
  164. vecEndPos = vecSpot;
  165. bHit = true;
  166. }
  167. else
  168. {
  169. UTIL_TraceLine( vecSrc, vecSpot, MASK_SOLID_BRUSHONLY, info.GetInflictor(), COLLISION_GROUP_NONE, &tr );
  170. if (tr.startsolid)
  171. {
  172. // if we're stuck inside them, fixup the position and distance
  173. tr.endpos = vecSrc;
  174. tr.fraction = 0.0;
  175. }
  176. vecEndPos = tr.endpos;
  177. if( tr.fraction == 1.0 || tr.m_pEnt == pEntity )
  178. {
  179. bHit = true;
  180. }
  181. }
  182. if ( bHit )
  183. {
  184. // the explosion can 'see' this entity, so hurt them!
  185. //vecToTarget = ( vecSrc - vecEndPos );
  186. vecToTarget = ( vecEndPos - vecSrc );
  187. // decrease damage for an ent that's farther from the bomb.
  188. flAdjustedDamage = vecToTarget.Length() * falloff;
  189. flAdjustedDamage = info.GetDamage() - flAdjustedDamage;
  190. if ( flAdjustedDamage > 0 )
  191. {
  192. CTakeDamageInfo adjustedInfo = info;
  193. adjustedInfo.SetDamage( flAdjustedDamage );
  194. Vector dir = vecToTarget;
  195. VectorNormalize( dir );
  196. // If we don't have a damage force, manufacture one
  197. if ( adjustedInfo.GetDamagePosition() == vec3_origin || adjustedInfo.GetDamageForce() == vec3_origin )
  198. {
  199. CalculateExplosiveDamageForce( &adjustedInfo, dir, vecSrc, 1.5 /* explosion scale! */ );
  200. }
  201. else
  202. {
  203. // Assume the force passed in is the maximum force. Decay it based on falloff.
  204. float flForce = adjustedInfo.GetDamageForce().Length() * falloff;
  205. adjustedInfo.SetDamageForce( dir * flForce );
  206. adjustedInfo.SetDamagePosition( vecSrc );
  207. }
  208. pEntity->TakeDamage( adjustedInfo );
  209. // Now hit all triggers along the way that respond to damage...
  210. pEntity->TraceAttackToTriggers( adjustedInfo, vecSrc, vecEndPos, dir );
  211. }
  212. }
  213. }
  214. }
  215. }
  216. void CSDKGameRules::Think()
  217. {
  218. BaseClass::Think();
  219. }
  220. #endif
  221. bool CSDKGameRules::ShouldCollide( int collisionGroup0, int collisionGroup1 )
  222. {
  223. if ( collisionGroup0 > collisionGroup1 )
  224. {
  225. // swap so that lowest is always first
  226. swap(collisionGroup0,collisionGroup1);
  227. }
  228. //Don't stand on COLLISION_GROUP_WEAPON
  229. if( collisionGroup0 == COLLISION_GROUP_PLAYER_MOVEMENT &&
  230. collisionGroup1 == COLLISION_GROUP_WEAPON )
  231. {
  232. return false;
  233. }
  234. return BaseClass::ShouldCollide( collisionGroup0, collisionGroup1 );
  235. }
  236. //-----------------------------------------------------------------------------
  237. // Purpose: Init CS ammo definitions
  238. //-----------------------------------------------------------------------------
  239. // shared ammo definition
  240. // JAY: Trying to make a more physical bullet response
  241. #define BULLET_MASS_GRAINS_TO_LB(grains) (0.002285*(grains)/16.0f)
  242. #define BULLET_MASS_GRAINS_TO_KG(grains) lbs2kg(BULLET_MASS_GRAINS_TO_LB(grains))
  243. // exaggerate all of the forces, but use real numbers to keep them consistent
  244. #define BULLET_IMPULSE_EXAGGERATION 1
  245. // convert a velocity in ft/sec and a mass in grains to an impulse in kg in/s
  246. #define BULLET_IMPULSE(grains, ftpersec) ((ftpersec)*12*BULLET_MASS_GRAINS_TO_KG(grains)*BULLET_IMPULSE_EXAGGERATION)
  247. CAmmoDef* GetAmmoDef()
  248. {
  249. static CAmmoDef def;
  250. static bool bInitted = false;
  251. if ( !bInitted )
  252. {
  253. bInitted = true;
  254. // def.AddAmmoType( BULLET_PLAYER_50AE, DMG_BULLET, TRACER_LINE, 0, 0, "ammo_50AE_max", 2400, 0, 10, 14 );
  255. def.AddAmmoType( AMMO_GRENADE, DMG_BLAST, TRACER_LINE, 0, 0, 1/*max carry*/, 1, 0 );
  256. def.AddAmmoType( AMMO_BULLETS, DMG_BULLET, TRACER_LINE, 0, 0, 1/*max carry*/, 1, 0 );
  257. }
  258. return &def;
  259. }
  260. #ifndef CLIENT_DLL
  261. const char *CSDKGameRules::GetChatPrefix( bool bTeamOnly, CBasePlayer *pPlayer )
  262. {
  263. return "(chat prefix)";
  264. }
  265. #endif
  266. //-----------------------------------------------------------------------------
  267. // Purpose: Find the relationship between players (teamplay vs. deathmatch)
  268. //-----------------------------------------------------------------------------
  269. int CSDKGameRules::PlayerRelationship( CBaseEntity *pPlayer, CBaseEntity *pTarget )
  270. {
  271. #ifndef CLIENT_DLL
  272. // half life multiplay has a simple concept of Player Relationships.
  273. // you are either on another player's team, or you are not.
  274. if ( !pPlayer || !pTarget || !pTarget->IsPlayer() || IsTeamplay() == false )
  275. return GR_NOTTEAMMATE;
  276. if ( (*GetTeamID(pPlayer) != '\0') && (*GetTeamID(pTarget) != '\0') && !stricmp( GetTeamID(pPlayer), GetTeamID(pTarget) ) )
  277. return GR_TEAMMATE;
  278. #endif
  279. return GR_NOTTEAMMATE;
  280. }