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.

495 lines
14 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: TF Base Rockets.
  4. //
  5. //=============================================================================//
  6. #include "cbase.h"
  7. #include "tf_projectile_base.h"
  8. #include "effect_dispatch_data.h"
  9. #include "tf_shareddefs.h"
  10. #include "tf_gamerules.h"
  11. #ifdef GAME_DLL
  12. #include "te_effect_dispatch.h"
  13. #else
  14. #include "c_te_effect_dispatch.h"
  15. #endif
  16. #ifdef CLIENT_DLL
  17. #include "c_basetempentity.h"
  18. #include "c_te_legacytempents.h"
  19. #include "c_te_effect_dispatch.h"
  20. #include "input.h"
  21. #include "c_tf_player.h"
  22. #else
  23. #include "tf_player.h"
  24. #endif
  25. #ifdef _DEBUG
  26. ConVar tf_debug_projectile( "tf_debug_projectile", "0", FCVAR_REPLICATED | FCVAR_DEVELOPMENTONLY | FCVAR_CHEAT );
  27. #endif // _DEBUG
  28. IMPLEMENT_NETWORKCLASS_ALIASED( TFBaseProjectile, DT_TFBaseProjectile )
  29. BEGIN_NETWORK_TABLE( CTFBaseProjectile, DT_TFBaseProjectile )
  30. #ifdef CLIENT_DLL
  31. RecvPropVector( RECVINFO( m_vInitialVelocity ) ),
  32. RecvPropEHandle( RECVINFO( m_hLauncher ) )
  33. #else
  34. SendPropVector( SENDINFO( m_vInitialVelocity ), 20 /*nbits*/, 0 /*flags*/, -3000 /*low value*/, 3000 /*high value*/ ),
  35. SendPropEHandle( SENDINFO( m_hLauncher ) )
  36. #endif
  37. END_NETWORK_TABLE()
  38. // Server specific.
  39. #ifdef GAME_DLL
  40. BEGIN_DATADESC( CTFBaseProjectile )
  41. //DEFINE_FUNCTION( ProjectileTouch ),
  42. DEFINE_THINKFUNC( FlyThink ),
  43. END_DATADESC()
  44. #endif
  45. //-----------------------------------------------------------------------------
  46. // Purpose: Constructor.
  47. //-----------------------------------------------------------------------------
  48. CTFBaseProjectile::CTFBaseProjectile()
  49. {
  50. m_vInitialVelocity.Init();
  51. SetWeaponID( TF_WEAPON_NONE );
  52. // Client specific.
  53. #ifdef CLIENT_DLL
  54. m_flSpawnTime = 0.0f;
  55. // Server specific.
  56. #else
  57. m_flDamage = 0.0f;
  58. #endif
  59. }
  60. //-----------------------------------------------------------------------------
  61. // Purpose: Destructor.
  62. //-----------------------------------------------------------------------------
  63. CTFBaseProjectile::~CTFBaseProjectile()
  64. {
  65. }
  66. //-----------------------------------------------------------------------------
  67. // Purpose:
  68. //-----------------------------------------------------------------------------
  69. void CTFBaseProjectile::Precache( void )
  70. {
  71. #ifdef GAME_DLL
  72. PrecacheModel( GetProjectileModelName() );
  73. #endif
  74. BaseClass::Precache();
  75. }
  76. //-----------------------------------------------------------------------------
  77. // Purpose:
  78. //-----------------------------------------------------------------------------
  79. void CTFBaseProjectile::Spawn( void )
  80. {
  81. // Client specific.
  82. #ifdef CLIENT_DLL
  83. m_flSpawnTime = gpGlobals->curtime;
  84. BaseClass::Spawn();
  85. // Server specific.
  86. #else
  87. // Precache.
  88. Precache();
  89. SetModel( GetProjectileModelName() );
  90. SetSolid( SOLID_BBOX );
  91. SetMoveType( MOVETYPE_FLYGRAVITY, MOVECOLLIDE_FLY_CUSTOM );
  92. AddEFlags( EFL_NO_WATER_VELOCITY_CHANGE );
  93. UTIL_SetSize( this, -Vector( 1.0f, 1.0f, 1.0f ), Vector( 1.0f, 1.0f, 1.0f ) );
  94. // Setup attributes.
  95. SetGravity( GetGravity() );
  96. m_takedamage = DAMAGE_NO;
  97. SetDamage( 25.0f );
  98. SetCollisionGroup( COLLISION_GROUP_PROJECTILE );
  99. // Setup the touch and think functions.
  100. SetTouch( &CTFBaseProjectile::ProjectileTouch );
  101. SetThink( &CTFBaseProjectile::FlyThink );
  102. SetNextThink( gpGlobals->curtime );
  103. #endif
  104. }
  105. //-----------------------------------------------------------------------------
  106. // Purpose:
  107. //-----------------------------------------------------------------------------
  108. CTFBaseProjectile *CTFBaseProjectile::Create( const char *pszClassname, const Vector &vecOrigin,
  109. const QAngle &vecAngles, CBaseEntity *pOwner, float flVelocity, short iProjModelIndex, const char *pszDispatchEffect,
  110. CBaseEntity *pScorer, bool bCritical, Vector vColor1, Vector vColor2 )
  111. {
  112. CTFBaseProjectile *pProjectile = NULL;
  113. Vector vecForward, vecRight, vecUp;
  114. AngleVectors( vecAngles, &vecForward, &vecRight, &vecUp );
  115. Vector vecVelocity = vecForward * flVelocity;
  116. #ifdef GAME_DLL
  117. pProjectile = static_cast<CTFBaseProjectile*>( CBaseEntity::Create( pszClassname, vecOrigin, vecAngles, pOwner ) );
  118. if ( !pProjectile )
  119. return NULL;
  120. // Initialize the owner.
  121. pProjectile->SetOwnerEntity( pOwner );
  122. pProjectile->SetScorer( pScorer );
  123. // Spawn.
  124. pProjectile->Spawn();
  125. pProjectile->SetAbsVelocity( vecVelocity );
  126. //pProjectile->SetupInitialTransmittedGrenadeVelocity( vecVelocity );
  127. // Setup the initial angles.
  128. QAngle angles;
  129. VectorAngles( vecVelocity, angles );
  130. pProjectile->SetAbsAngles( angles );
  131. // Set team.
  132. pProjectile->ChangeTeam( pOwner->GetTeamNumber() );
  133. // Hide the projectile and create a fake one on the client
  134. pProjectile->AddEffects( EF_NODRAW );
  135. #endif
  136. if ( pszDispatchEffect )
  137. {
  138. // we'd like to just send this projectile to a person in the shooter's PAS. However
  139. // the projectile won't be sent to a player outside of water if shot from inside water
  140. // and vice-versa, so we do a trace here to figure out if the trace starts or stops in water.
  141. // if it crosses contents, we'll just broadcast the projectile. Otherwise, just send to PVS
  142. // of the trace's endpoint.
  143. trace_t tr;
  144. CTraceFilterSimple traceFilter( pOwner, COLLISION_GROUP_NONE );
  145. ITraceFilter *pFilterChain = NULL;
  146. CTraceFilterIgnoreFriendlyCombatItems traceFilterCombatItem( pOwner, COLLISION_GROUP_NONE, pOwner->GetTeamNumber() );
  147. if ( TFGameRules() && TFGameRules()->GameModeUsesUpgrades() )
  148. {
  149. // Ignore teammates and their (physical) upgrade items in MvM
  150. pFilterChain = &traceFilterCombatItem;
  151. }
  152. CTraceFilterChain traceFilterChain( &traceFilter, pFilterChain );
  153. UTIL_TraceLine( vecOrigin, vecOrigin + vecForward * MAX_COORD_RANGE, (CONTENTS_SOLID|CONTENTS_MOVEABLE|CONTENTS_WINDOW|CONTENTS_GRATE), &traceFilterChain, &tr );
  154. bool bBroadcast = ( UTIL_PointContents( vecOrigin ) != UTIL_PointContents( tr.endpos ) );
  155. IRecipientFilter *pFilter;
  156. if ( bBroadcast )
  157. {
  158. // The projectile is going to cross content types
  159. // (which will block PVS/PAS). Send to every client
  160. pFilter = new CReliableBroadcastRecipientFilter();
  161. }
  162. else
  163. {
  164. // just the PVS of where the projectile will hit.
  165. pFilter = new CPASFilter( tr.endpos );
  166. }
  167. CEffectData data;
  168. data.m_vOrigin = vecOrigin;
  169. data.m_vStart = vecVelocity;
  170. data.m_fFlags = 6; // Lifetime
  171. data.m_nDamageType = 0;
  172. if ( bCritical )
  173. {
  174. data.m_nDamageType |= DMG_CRITICAL;
  175. }
  176. data.m_CustomColors.m_vecColor1 = vColor1;
  177. data.m_CustomColors.m_vecColor2 = vColor2;
  178. #ifdef GAME_DLL
  179. data.m_nMaterial = pProjectile->GetModelIndex();
  180. data.m_nEntIndex = pOwner->entindex();
  181. #else
  182. data.m_nMaterial = iProjModelIndex;
  183. data.m_hEntity = ClientEntityList().EntIndexToHandle( pOwner->entindex() );
  184. #endif
  185. DispatchEffect( pszDispatchEffect, data );
  186. }
  187. return pProjectile;
  188. }
  189. const char *CTFBaseProjectile::GetProjectileModelName( void )
  190. {
  191. // should not try to init a base projectile
  192. Assert( 0 );
  193. return "";
  194. }
  195. //=============================================================================
  196. //
  197. // Client specific functions.
  198. //
  199. #ifdef CLIENT_DLL
  200. //-----------------------------------------------------------------------------
  201. // Purpose:
  202. //-----------------------------------------------------------------------------
  203. void CTFBaseProjectile::PostDataUpdate( DataUpdateType_t type )
  204. {
  205. // Pass through to the base class.
  206. BaseClass::PostDataUpdate( type );
  207. if ( type == DATA_UPDATE_CREATED )
  208. {
  209. // Now stick our initial velocity and angles into the interpolation history.
  210. CInterpolatedVar<Vector> &interpolator = GetOriginInterpolator();
  211. interpolator.ClearHistory();
  212. CInterpolatedVar<QAngle> &rotInterpolator = GetRotationInterpolator();
  213. rotInterpolator.ClearHistory();
  214. float flChangeTime = GetLastChangeTime( LATCH_SIMULATION_VAR );
  215. // Add a sample 1 second back.
  216. Vector vCurOrigin = GetLocalOrigin() - m_vInitialVelocity;
  217. interpolator.AddToHead( flChangeTime - 1.0f, &vCurOrigin, false );
  218. QAngle vCurAngles = GetLocalAngles();
  219. rotInterpolator.AddToHead( flChangeTime - 1.0f, &vCurAngles, false );
  220. // Add the current sample.
  221. vCurOrigin = GetLocalOrigin();
  222. interpolator.AddToHead( flChangeTime, &vCurOrigin, false );
  223. rotInterpolator.AddToHead( flChangeTime - 1.0, &vCurAngles, false );
  224. }
  225. }
  226. //-----------------------------------------------------------------------------
  227. // Purpose:
  228. //-----------------------------------------------------------------------------
  229. int CTFBaseProjectile::DrawModel( int flags )
  230. {
  231. // During the first 0.2 seconds of our life, don't draw ourselves.
  232. if ( gpGlobals->curtime - m_flSpawnTime < 0.1f )
  233. return 0;
  234. return BaseClass::DrawModel( flags );
  235. }
  236. //-----------------------------------------------------------------------------
  237. // Purpose:
  238. //-----------------------------------------------------------------------------
  239. C_LocalTempEntity *ClientsideProjectileCallback( const CEffectData &data, float flGravityBase, const char *pszParticleName )
  240. {
  241. // Create a nail temp ent, and give it an impact callback to use
  242. C_BaseEntity *pEnt = C_BaseEntity::Instance( data.m_hEntity );
  243. if ( !pEnt || pEnt->IsDormant() )
  244. {
  245. //Assert( 0 );
  246. return NULL;
  247. }
  248. Vector vecSrc = data.m_vOrigin;
  249. // If we're seeing another player shooting the nails, move their start point to the weapon origin
  250. if ( pEnt && pEnt->IsPlayer() )
  251. {
  252. C_BasePlayer *pLocalPlayer = C_BasePlayer::GetLocalPlayer();
  253. if ( pLocalPlayer != pEnt || C_BasePlayer::ShouldDrawLocalPlayer() )
  254. {
  255. CTFPlayer *pTFPlayer = ToTFPlayer( pEnt );
  256. if ( pTFPlayer->GetActiveWeapon() )
  257. {
  258. pTFPlayer->GetActiveWeapon()->GetAttachment( "muzzle", vecSrc );
  259. }
  260. }
  261. else
  262. {
  263. C_BaseEntity *pViewModel = pLocalPlayer->GetViewModel();
  264. if ( pViewModel )
  265. {
  266. QAngle vecAngles;
  267. Vector vecMuzzleOrigin;
  268. int iMuzzleFlashAttachment = pViewModel->LookupAttachment( "muzzle" );
  269. pViewModel->GetAttachment( iMuzzleFlashAttachment, vecMuzzleOrigin, vecAngles );
  270. Vector vForward;
  271. AngleVectors( vecAngles, &vForward );
  272. trace_t trace;
  273. UTIL_TraceLine( vecMuzzleOrigin + vForward * -50, vecMuzzleOrigin, MASK_SOLID, pEnt, COLLISION_GROUP_NONE, &trace );
  274. if ( trace.fraction != 1.0 )
  275. {
  276. vecSrc = trace.endpos;
  277. }
  278. }
  279. }
  280. }
  281. float flGravity = ( flGravityBase * 800 );
  282. Vector vecGravity(0,0,-flGravity);
  283. return tempents->ClientProjectile( vecSrc, data.m_vStart, vecGravity, data.m_nMaterial, data.m_fFlags, pEnt, "Impact", pszParticleName );
  284. }
  285. //=============================================================================
  286. //
  287. // Server specific functions.
  288. //
  289. #else
  290. //-----------------------------------------------------------------------------
  291. // Purpose:
  292. //-----------------------------------------------------------------------------
  293. unsigned int CTFBaseProjectile::PhysicsSolidMaskForEntity( void ) const
  294. {
  295. return BaseClass::PhysicsSolidMaskForEntity() | CONTENTS_HITBOX;
  296. }
  297. //-----------------------------------------------------------------------------
  298. // Purpose:
  299. //-----------------------------------------------------------------------------
  300. void CTFBaseProjectile::ProjectileTouch( CBaseEntity *pOther )
  301. {
  302. // Verify a correct "other."
  303. Assert( pOther );
  304. if ( !pOther->IsSolid() || pOther->IsSolidFlagSet( FSOLID_VOLUME_CONTENTS ) )
  305. return;
  306. // Handle hitting skybox (disappear).
  307. const trace_t *pTrace = &CBaseEntity::GetTouchTrace();
  308. trace_t *pNewTrace = const_cast<trace_t*>( pTrace );
  309. if( pTrace->surface.flags & SURF_SKY )
  310. {
  311. UTIL_Remove( this );
  312. return;
  313. }
  314. // pass through ladders
  315. if( pTrace->surface.flags & CONTENTS_LADDER )
  316. return;
  317. if ( TFGameRules() && TFGameRules()->GameModeUsesUpgrades() )
  318. {
  319. // Projectile shields
  320. if ( InSameTeam( pOther ) && pOther->IsCombatItem() )
  321. return;
  322. }
  323. if ( pOther->IsWorld() )
  324. {
  325. SetAbsVelocity( vec3_origin );
  326. AddSolidFlags( FSOLID_NOT_SOLID );
  327. // Remove immediately. Clientside projectiles will stick in the wall for a bit.
  328. UTIL_Remove( this );
  329. return;
  330. }
  331. // determine the inflictor, which is the weapon which fired this projectile
  332. CBaseEntity *pInflictor = GetLauncher();
  333. CTakeDamageInfo info;
  334. info.SetAttacker( GetOwnerEntity() ); // the player who operated the thing that emitted nails
  335. info.SetInflictor( pInflictor ); // the weapon that emitted this projectile
  336. info.SetWeapon( pInflictor );
  337. info.SetDamage( GetDamage() );
  338. info.SetDamageForce( GetDamageForce() );
  339. info.SetDamagePosition( GetAbsOrigin() );
  340. info.SetDamageType( GetDamageType() );
  341. Vector dir;
  342. AngleVectors( GetAbsAngles(), &dir );
  343. pOther->DispatchTraceAttack( info, dir, pNewTrace );
  344. ApplyMultiDamage();
  345. if ( pOther && pOther->IsPlayer() )
  346. {
  347. int iMadMilkSyringes = 0;
  348. CALL_ATTRIB_HOOK_INT_ON_OTHER( GetOwnerEntity(), iMadMilkSyringes, mad_milk_syringes );
  349. if ( iMadMilkSyringes )
  350. {
  351. CTFPlayer *pTFVictim = ToTFPlayer( pOther );
  352. CTFPlayer *pTFOwner = ToTFPlayer( GetOwnerEntity() );
  353. if ( pTFVictim && pTFOwner && pTFVictim->GetTeamNumber() != pTFOwner->GetTeamNumber() )
  354. {
  355. pTFVictim->m_Shared.AddCond( TF_COND_MAD_MILK, 1.f, pTFOwner );
  356. }
  357. }
  358. }
  359. UTIL_Remove( this );
  360. }
  361. Vector CTFBaseProjectile::GetDamageForce( void )
  362. {
  363. Vector vecVelocity = GetAbsVelocity();
  364. VectorNormalize( vecVelocity );
  365. return (vecVelocity * GetDamage());
  366. }
  367. void CTFBaseProjectile::FlyThink( void )
  368. {
  369. QAngle angles;
  370. VectorAngles( GetAbsVelocity(), angles );
  371. SetAbsAngles( angles );
  372. SetNextThink( gpGlobals->curtime + 0.1f );
  373. #ifdef _DEBUG
  374. if ( tf_debug_projectile.GetBool() )
  375. {
  376. NDebugOverlay::Box( GetAbsOrigin(), Vector( 0.5, 0.5, 0.5 ), -Vector( 0.5, 0.5, 0.5 ), 0, 255, 0, 100, 0.1 );
  377. }
  378. #endif // _DEBUG
  379. }
  380. void CTFBaseProjectile::SetScorer( CBaseEntity *pScorer )
  381. {
  382. m_Scorer = pScorer;
  383. }
  384. CBasePlayer *CTFBaseProjectile::GetScorer( void )
  385. {
  386. return dynamic_cast<CBasePlayer *>( m_Scorer.Get() );
  387. }
  388. //-----------------------------------------------------------------------------
  389. // Purpose:
  390. //-----------------------------------------------------------------------------
  391. int CTFBaseProjectile::GetDamageType( void )
  392. {
  393. Assert( GetWeaponID() != TF_WEAPON_NONE );
  394. int iDmgType = g_aWeaponDamageTypes[ GetWeaponID() ];
  395. if ( m_bCritical )
  396. {
  397. iDmgType |= DMG_CRITICAL;
  398. }
  399. return iDmgType;
  400. }
  401. #endif