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.

572 lines
17 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================//
  7. // copied from portal2 code; original code came with client-predicted counterpart,
  8. // but implementing predictable triggers in tf2 wasn't trivial so this is just the
  9. // server component. it works but causes prediction errors.
  10. #include "cbase.h"
  11. #include "movevars_shared.h"
  12. #if defined( GAME_DLL )
  13. #include "trigger_catapult.h"
  14. #include "tf_player.h"
  15. #include "vcollide_parse.h"
  16. #include "props.h"
  17. #else
  18. #include "c_trigger_catapult.h"
  19. #endif
  20. // memdbgon must be the last include file in a .cpp file!!!
  21. #include "tier0/memdbgon.h"
  22. ConVar catapult_physics_drag_boost( "catapult_physics_drag_boost", "2.1", FCVAR_REPLICATED );
  23. //-----------------------------------------------------------------------------
  24. // Purpose: calculates the launch vector between the entity that touched the
  25. // catapult trigger and the catapult target
  26. //-----------------------------------------------------------------------------
  27. Vector CTriggerCatapult::CalculateLaunchVector( CBaseEntity *pVictim, CBaseEntity *pTarget )
  28. {
  29. #if defined( CLIENT_DLL )
  30. if( !GetPredictable() || !pVictim->GetPredictable() )
  31. return vec3_origin;
  32. #endif
  33. // Find where we're going
  34. Vector vecSourcePos = pVictim->GetAbsOrigin();
  35. Vector vecTargetPos = pTarget->GetAbsOrigin();
  36. // If victim is player, adjust target position so player's center will hit the target
  37. if ( pVictim->IsPlayer() )
  38. {
  39. vecTargetPos.z -= 32.0f;
  40. }
  41. float flSpeed = (pVictim->IsPlayer()) ? (float)m_flPlayerVelocity : (float)m_flPhysicsVelocity; // u/sec
  42. float flGravity = GetCurrentGravity();
  43. Vector vecVelocity = (vecTargetPos - vecSourcePos);
  44. // throw at a constant time
  45. float time = vecVelocity.Length( ) / flSpeed;
  46. vecVelocity = vecVelocity * (1.f / time); // CatapultLaunchVelocityMultiplier
  47. // adjust upward toss to compensate for gravity loss
  48. vecVelocity.z += flGravity * time * 0.5;
  49. return vecVelocity;
  50. }
  51. //-----------------------------------------------------------------------------
  52. // Purpose: calculates the launch vector between the entity that touched the
  53. // catapult trigger and the catapult target
  54. //-----------------------------------------------------------------------------
  55. Vector CTriggerCatapult::CalculateLaunchVectorPreserve( Vector vecInitialVelocity, CBaseEntity *pVictim, CBaseEntity *pTarget, bool bForcePlayer )
  56. {
  57. #if defined( CLIENT_DLL )
  58. if( !GetPredictable() || !pVictim->GetPredictable() )
  59. return vec3_origin;
  60. #endif
  61. // Find where we're going
  62. Vector vecSourcePos = pVictim->GetAbsOrigin();
  63. Vector vecTargetPos = pTarget->GetAbsOrigin();
  64. // If victim is player, adjust target position so player's center will hit the target
  65. if ( pVictim->IsPlayer() || bForcePlayer )
  66. {
  67. vecTargetPos.z -= 32.0f;
  68. }
  69. Vector vecDiff = (vecTargetPos - vecSourcePos);
  70. float flHeight = vecDiff.z;
  71. float flDist = vecDiff.Length2D();
  72. float flVelocity = (pVictim->IsPlayer() || bForcePlayer ) ? (float)m_flPlayerVelocity : (float)m_flPhysicsVelocity;
  73. float flGravity = -1.0f*GetCurrentGravity();
  74. if( flDist == 0.f )
  75. {
  76. DevWarning( "Bad location input for catapult!\n" );
  77. return CalculateLaunchVector(pVictim, pTarget);
  78. }
  79. float flRadical = flVelocity*flVelocity*flVelocity*flVelocity - flGravity*(flGravity*flDist*flDist - 2.f*flHeight*flVelocity*flVelocity);
  80. if( flRadical <= 0.f )
  81. {
  82. DevWarning( "Catapult can't hit target! Add more speed!\n" );
  83. return CalculateLaunchVector(pVictim, pTarget);
  84. }
  85. flRadical = ( sqrt( flRadical ) );
  86. float flTestAngle1 = flVelocity*flVelocity;
  87. float flTestAngle2 = flTestAngle1;
  88. flTestAngle1 = -atan( (flTestAngle1 + flRadical) / (flGravity*flDist) );
  89. flTestAngle2 = -atan( (flTestAngle2 - flRadical) / (flGravity*flDist) );
  90. Vector vecTestVelocity1 = vecDiff;
  91. vecTestVelocity1.z = 0;
  92. vecTestVelocity1.NormalizeInPlace();
  93. Vector vecTestVelocity2 = vecTestVelocity1;
  94. vecTestVelocity1 *= flVelocity*cos( flTestAngle1 );
  95. vecTestVelocity1.z = flVelocity*sin( flTestAngle1 );
  96. vecTestVelocity2 *= flVelocity*cos( flTestAngle2 );
  97. vecTestVelocity2.z = flVelocity*sin( flTestAngle2 );
  98. vecInitialVelocity.NormalizeInPlace();
  99. if( m_ExactVelocityChoice == 1 )
  100. {
  101. return vecTestVelocity1;
  102. }
  103. else if( m_ExactVelocityChoice == 2 )
  104. {
  105. return vecTestVelocity2;
  106. }
  107. if( vecInitialVelocity.Dot( vecTestVelocity1 ) > vecInitialVelocity.Dot( vecTestVelocity2 ) )
  108. {
  109. return vecTestVelocity1;
  110. }
  111. return vecTestVelocity2;
  112. }
  113. //-----------------------------------------------------------------------------
  114. // Purpose:
  115. //-----------------------------------------------------------------------------
  116. void CTriggerCatapult::LaunchByTarget( CBaseEntity *pVictim, CBaseEntity *pTarget )
  117. {
  118. #if defined( CLIENT_DLL )
  119. if( !GetPredictable() || !pVictim->GetPredictable() )
  120. return;
  121. #endif
  122. Vector vecVictim;
  123. if ( pVictim->VPhysicsGetObject() )
  124. {
  125. pVictim->VPhysicsGetObject()->GetVelocity( &vecVictim, NULL );
  126. }
  127. else
  128. {
  129. vecVictim = pVictim->GetAbsVelocity();
  130. }
  131. // get the launch vector
  132. Vector vecVelocity = m_bUseExactVelocity ?
  133. CalculateLaunchVectorPreserve( vecVictim, pVictim, pTarget ):
  134. CalculateLaunchVector( pVictim, pTarget );
  135. // Handle a player
  136. if ( pVictim->IsPlayer() )
  137. {
  138. // Send us flying
  139. if ( pVictim->GetFlags() & FL_ONGROUND )
  140. {
  141. pVictim->SetGroundEntity( NULL );
  142. pVictim->SetGroundChangeTime( gpGlobals->curtime + 0.5f );
  143. }
  144. CTFPlayer *pPlayer = ToTFPlayer( pVictim );
  145. if ( pPlayer )
  146. {
  147. float flSupressionTimeInSeconds = 0.25f;
  148. if ( m_flAirControlSupressionTime > 0 )
  149. {
  150. // If set in the map, use this override time
  151. flSupressionTimeInSeconds = m_flAirControlSupressionTime;
  152. }
  153. //pPlayer->SetAirControlSupressionTime( flSupressionTimeInSeconds * 1000.0f ); // fix units, this method expects milliseconds
  154. pVictim->Teleport( NULL, NULL, &vecVelocity );
  155. OnLaunchedVictim( pVictim );
  156. #if defined( GAME_DLL ) && !defined( _GAMECONSOLE ) && !defined( NO_STEAM )
  157. //g_PortalGameStats.Event_Catapult_LaunchByTarget( pPlayer, vecVelocity );
  158. #endif
  159. }
  160. }
  161. else
  162. {
  163. if ( pVictim->GetMoveType() == MOVETYPE_VPHYSICS )
  164. {
  165. // Launch!
  166. IPhysicsObject *pPhysObject = pVictim->VPhysicsGetObject();
  167. if ( pPhysObject )
  168. {
  169. AngularImpulse angImpulse = m_bApplyAngularImpulse ? RandomAngularImpulse( -150.0f, 150.0f ) : vec3_origin;
  170. pPhysObject->SetVelocityInstantaneous( &vecVelocity, &angImpulse );
  171. // UNDONE: don't mess with physics properties
  172. #if defined( GAME_DLL )
  173. CPhysicsProp *pProp = dynamic_cast<CPhysicsProp *>(pVictim);
  174. if ( pProp != NULL )
  175. {
  176. //HACK!
  177. pProp->OnPhysGunDrop( UTIL_GetLocalPlayer(), LAUNCHED_BY_CANNON );
  178. }
  179. #endif
  180. }
  181. }
  182. OnLaunchedVictim( pVictim );
  183. }
  184. }
  185. //-----------------------------------------------------------------------------
  186. // Purpose:
  187. //-----------------------------------------------------------------------------
  188. void CTriggerCatapult::LaunchByDirection( CBaseEntity *pVictim )
  189. {
  190. #if defined( CLIENT_DLL )
  191. if( !GetPredictable() || !pVictim->GetPredictable() )
  192. return;
  193. #endif
  194. Vector vecForward;
  195. AngleVectors( m_vecLaunchAngles, &vecForward, NULL, NULL );
  196. // Handle a player
  197. if ( pVictim->IsPlayer() )
  198. {
  199. // Simply push us forward
  200. Vector vecPush = vecForward * m_flPlayerVelocity;
  201. // Hack on top of magic
  202. if( CloseEnough( vecPush[0], 0.f ) && CloseEnough( vecPush[1],0.f ) )
  203. {
  204. vecPush[2] = m_flPlayerVelocity * 1.5f; // FIXME: Magic!
  205. }
  206. // Send us flying
  207. if ( pVictim->GetFlags() & FL_ONGROUND )
  208. {
  209. pVictim->SetGroundEntity( NULL );
  210. pVictim->SetGroundChangeTime( gpGlobals->curtime + 0.5f );
  211. }
  212. pVictim->SetAbsVelocity( vecPush );
  213. OnLaunchedVictim( pVictim );
  214. // Do air control suppression
  215. if( m_bDirectionSuppressAirControl )
  216. {
  217. float flSupressionTimeInSeconds = 0.25f;
  218. if ( m_flAirControlSupressionTime > 0 )
  219. {
  220. // If set in the map, use this override time
  221. flSupressionTimeInSeconds = m_flAirControlSupressionTime;
  222. }
  223. //CTFPlayer* pTFPlayer = static_cast<CTFPlayer*>(pVictim);
  224. //pTFPlayer->SetAirControlSupressionTime( flSupressionTimeInSeconds * 1000.0f ); // fix units, this method expects milliseconds
  225. }
  226. #if defined( GAME_DLL ) && !defined( _GAMECONSOLE ) && !defined( NO_STEAM )
  227. //g_PortalGameStats.Event_Catapult_LaunchByDirection( ToPortalPlayer(pVictim), vecPush );
  228. #endif
  229. }
  230. #if defined( GAME_DLL )
  231. else
  232. {
  233. if ( pVictim->GetMoveType() == MOVETYPE_VPHYSICS )
  234. {
  235. // Launch!
  236. IPhysicsObject *pPhysObject = pVictim->VPhysicsGetObject();
  237. if ( pPhysObject )
  238. {
  239. Vector vecVelocity = vecForward * m_flPhysicsVelocity;
  240. vecVelocity[2] = m_flPhysicsVelocity;
  241. AngularImpulse angImpulse = RandomAngularImpulse( -50.0f, 50.0f );
  242. pPhysObject->SetVelocityInstantaneous( &vecVelocity, &angImpulse );
  243. // Force this!
  244. float flNull = 0.0f;
  245. pPhysObject->SetDragCoefficient( &flNull, &flNull );
  246. pPhysObject->SetDamping( &flNull, &flNull );
  247. CPhysicsProp *pProp = dynamic_cast<CPhysicsProp *>(pVictim);
  248. if ( pProp != NULL )
  249. {
  250. //HACK!
  251. pProp->OnPhysGunDrop( UTIL_GetLocalPlayer(), LAUNCHED_BY_CANNON );
  252. }
  253. }
  254. }
  255. OnLaunchedVictim( pVictim );
  256. }
  257. #endif
  258. }
  259. //-----------------------------------------------------------------------------
  260. // Purpose:
  261. //-----------------------------------------------------------------------------
  262. void CTriggerCatapult::OnLaunchedVictim( CBaseEntity *pVictim )
  263. {
  264. #if defined( CLIENT_DLL )
  265. if( !GetPredictable() || !pVictim->GetPredictable() )
  266. return;
  267. #endif
  268. #if defined( GAME_DLL )
  269. m_OnCatapulted.FireOutput( pVictim, this );
  270. #endif
  271. if ( pVictim->IsPlayer() )
  272. {
  273. CTFPlayer *pPlayer = static_cast< CTFPlayer* >( pVictim );
  274. int nRefireIndex = pPlayer->entindex();
  275. #if defined( GAME_DLL )
  276. m_flRefireDelay[ nRefireIndex ] = gpGlobals->curtime + 0.5f; // HACK!
  277. #else
  278. m_flRefireDelay[ nRefireIndex ] = gpGlobals->curtime + 0.5f; // HACK!
  279. #endif
  280. }
  281. else
  282. {
  283. #if defined( GAME_DLL )
  284. m_flRefireDelay[ 0 ] = gpGlobals->curtime + 0.5f; // HACK!
  285. #else
  286. m_flRefireDelay[ 0 ] = gpGlobals->curtime + 0.5f; // HACK!
  287. #endif
  288. }
  289. }
  290. //-----------------------------------------------------------------------------
  291. // Purpose:
  292. //-----------------------------------------------------------------------------
  293. void CTriggerCatapult::StartTouch( CBaseEntity *pOther )
  294. {
  295. if ( pOther == NULL )
  296. return;
  297. #if defined( CLIENT_DLL )
  298. if( !GetPredictable() || !pOther->GetPredictable() )
  299. return;
  300. #endif
  301. //Warning( "CTriggerCatapult::StartTouch( %i %s %f )\n", entindex(), gpGlobals->IsClient() ? "client" : "server", gpGlobals->curtime );
  302. #if defined( GAME_DLL )
  303. if ( PassesTriggerFilters( pOther ) == false )
  304. #else
  305. if( !(pOther->IsPlayer() && m_bPlayersPassTriggerFilters) )
  306. #endif
  307. {
  308. return;
  309. }
  310. // Don't refire too quickly
  311. int nRefireIndex = pOther->IsPlayer() ? static_cast< CBasePlayer* >( pOther )->entindex() : 0;
  312. if ( nRefireIndex >= MAX_PLAYERS + 1 )
  313. {
  314. Warning( "CTriggerCatapult::StartTouch Trying to store a refire index for an entity( %d ) outside the expected range ( < %d ).\n", nRefireIndex, MAX_PLAYERS + 1 );
  315. nRefireIndex = 0;
  316. }
  317. if ( m_flRefireDelay[ nRefireIndex ] > gpGlobals->curtime )
  318. {
  319. // but also don't forget to try again
  320. if ( m_hAbortedLaunchees.Find( pOther ) == -1 )
  321. {
  322. m_hAbortedLaunchees.AddToTail( pOther );
  323. }
  324. SetThink( &CTriggerCatapult::LaunchThink );
  325. SetNextThink( gpGlobals->curtime + 0.05f );
  326. return;
  327. }
  328. #if defined( GAME_DLL )
  329. // Don't touch things the player is holding
  330. if ( pOther->VPhysicsGetObject() && (pOther->VPhysicsGetObject()->GetGameFlags() & FVPHYSICS_PLAYER_HELD) )
  331. {
  332. if ( m_hAbortedLaunchees.Find( pOther ) == -1 )
  333. {
  334. m_hAbortedLaunchees.AddToTail( pOther );
  335. }
  336. SetThink( &CTriggerCatapult::LaunchThink );
  337. SetNextThink( gpGlobals->curtime + 0.05f );
  338. return;
  339. }
  340. else if ( pOther->IsPlayer() )
  341. {
  342. // Always keep players in this list in case the were trapped under another player in the previous launch
  343. if ( m_hAbortedLaunchees.Find( pOther ) == -1 )
  344. {
  345. m_hAbortedLaunchees.AddToTail( pOther );
  346. }
  347. SetThink( &CTriggerCatapult::LaunchThink );
  348. SetNextThink( gpGlobals->curtime + 0.05f );
  349. }
  350. #endif
  351. // Get the target
  352. CBaseEntity *pLaunchTarget = m_hLaunchTarget;
  353. // See if we're attempting to hit a target
  354. if ( pLaunchTarget )
  355. {
  356. // See if we are using the threshold check
  357. if ( m_bUseThresholdCheck )
  358. {
  359. // Get the velocity of the physics objects / players touching the catapult
  360. Vector vecVictim;
  361. if ( pOther->IsPlayer() )
  362. {
  363. vecVictim = pOther->GetAbsVelocity();
  364. }
  365. else if( pOther->VPhysicsGetObject() )
  366. {
  367. pOther->VPhysicsGetObject()->GetVelocity( &vecVictim, NULL );
  368. }
  369. else
  370. {
  371. // DevMsg("Catapult fail!! Object is not a player and has no physics object! BUG THIS\n");
  372. vecVictim = vec3_origin;
  373. }
  374. float flVictimSpeed = vecVictim.Length();
  375. // get the speed needed to hit the target
  376. Vector vecVelocity;
  377. if( m_bUseExactVelocity )
  378. {
  379. vecVelocity = CalculateLaunchVectorPreserve( vecVictim, pOther, pLaunchTarget );
  380. }
  381. else
  382. {
  383. vecVelocity = CalculateLaunchVector( pOther, pLaunchTarget );
  384. }
  385. float flLaunchSpeed = vecVelocity.Length();
  386. // is the victim facing the target?
  387. Vector vecDirection = ( pLaunchTarget->GetAbsOrigin() - pOther->GetAbsOrigin() );
  388. Vector necNormalizedVictim = vecVictim;
  389. Vector vecNormalizedDirection = vecDirection;
  390. necNormalizedVictim.NormalizeInPlace();
  391. vecNormalizedDirection.NormalizeInPlace();
  392. float flDot = DotProduct( necNormalizedVictim, vecNormalizedDirection );
  393. if ( flDot >= m_flEntryAngleTolerance )
  394. {
  395. // Is the victim speed within tolerance to launch them?
  396. if ( ( ( flLaunchSpeed - (flLaunchSpeed * m_flLowerThreshold ) ) < flVictimSpeed ) && ( ( flLaunchSpeed + (flLaunchSpeed * m_flUpperThreshold ) ) > flVictimSpeed ) )
  397. {
  398. if( m_bOnlyVelocityCheck )
  399. {
  400. OnLaunchedVictim( pOther );
  401. }
  402. else
  403. {
  404. // Launch!
  405. LaunchByTarget( pOther, pLaunchTarget );
  406. // DevMsg( 1, "Catapult \"%s\" is adjusting velocity of \"%s\" so it will hit the target. (Object Velocity: %.1f -- Object needed to be between %.1f and %.1f \n", STRING(GetEntityName()), pOther->GetClassname(), flVictimSpeed, flLaunchSpeed - (flLaunchSpeed * m_flLowerThreshold ), flLaunchSpeed + (flLaunchSpeed * m_flUpperThreshold ) );
  407. }
  408. }
  409. else
  410. {
  411. // DevMsg( 1, "Catapult \"%s\" ignoring object \"%s\" because its velocity is outside of the threshold. (Object Velocity: %.1f -- Object needed to be between %.1f and %.1f \n", STRING(GetEntityName()), pOther->GetClassname(), flVictimSpeed, flLaunchSpeed - (flLaunchSpeed * m_flLowerThreshold ), flLaunchSpeed + (flLaunchSpeed * m_flUpperThreshold ) );
  412. // since we attempted a fling set the refire delay
  413. #if defined( GAME_DLL )
  414. m_flRefireDelay[ nRefireIndex ] = gpGlobals->curtime + 0.5f; // HACK!
  415. #else
  416. m_flRefireDelay[ nRefireIndex ] = gpGlobals->curtime + 0.5f; // HACK!
  417. #endif
  418. }
  419. }
  420. else
  421. {
  422. // we're facing the wrong way. set the refire delay.
  423. #if defined( GAME_DLL )
  424. m_flRefireDelay[ nRefireIndex ] = gpGlobals->curtime + 0.5f; // HACK!
  425. #else
  426. m_flRefireDelay[ nRefireIndex ] = gpGlobals->curtime + 0.5f; // HACK!
  427. #endif
  428. }
  429. }
  430. else
  431. {
  432. LaunchByTarget( pOther, pLaunchTarget );
  433. }
  434. }
  435. else
  436. {
  437. #if defined( CLIENT_DLL )
  438. if( m_hLaunchTarget.IsValid() )
  439. {
  440. Warning( "Catapult launch target not networked to client! This will make prediction fail! Fix this in the map.\n"
  441. "Catapult launch target not networked to client! This will make prediction fail! Fix this in the map.\n"
  442. "Catapult launch target not networked to client! This will make prediction fail! Fix this in the map.\n"
  443. "Catapult launch target not networked to client! This will make prediction fail! Fix this in the map.\n"
  444. "Catapult launch target not networked to client! This will make prediction fail! Fix this in the map.\n" );
  445. }
  446. #endif
  447. bool bShouldLaunch = true;
  448. if( m_bUseThresholdCheck )
  449. {
  450. // Get the velocity of the physics objects / players touching the catapult
  451. Vector vecVictim;
  452. if ( pOther->IsPlayer() )
  453. {
  454. vecVictim = pOther->GetAbsVelocity();
  455. }
  456. else if( pOther->VPhysicsGetObject() )
  457. {
  458. pOther->VPhysicsGetObject()->GetVelocity( &vecVictim, NULL );
  459. }
  460. else
  461. {
  462. // DevMsg("Catapult fail!! Object is not a player and has no physics object! BUG THIS\n");
  463. vecVictim = vec3_origin;
  464. }
  465. Vector vecForward;
  466. AngleVectors( m_vecLaunchAngles, &vecForward, NULL, NULL );
  467. float flDot = DotProduct( vecForward, vecVictim );
  468. float flLower = m_flPlayerVelocity - (m_flPlayerVelocity * m_flLowerThreshold);
  469. float flUpper = m_flPlayerVelocity + (m_flPlayerVelocity * m_flUpperThreshold);
  470. if( flDot < flLower || flDot > flUpper )
  471. {
  472. bShouldLaunch = false;
  473. }
  474. }
  475. if( bShouldLaunch )
  476. {
  477. #if defined( CLIENT_DLL )
  478. CEG_PROTECT_VIRTUAL_FUNCTION ( CTriggerCatapult_StartTouch );
  479. #endif
  480. if( m_bOnlyVelocityCheck )
  481. {
  482. OnLaunchedVictim( pOther );
  483. }
  484. else
  485. {
  486. LaunchByDirection( pOther );
  487. }
  488. }
  489. }
  490. }