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.

412 lines
14 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================//
  7. #include "cbase.h"
  8. #include "trigger_catapult.h"
  9. #include "vcollide_parse.h"
  10. #include "props.h"
  11. #include "movevars_shared.h"
  12. #include "tf_player.h"
  13. #include "saverestore_utlvector.h"
  14. // memdbgon must be the last include file in a .cpp file!!!
  15. #include "tier0/memdbgon.h"
  16. const char *CTriggerCatapult::s_szPlayerPassesTriggerFiltersThinkContext = "CTriggerCatapult::PlayerPassesTriggerFiltersThink";
  17. extern ConVar catapult_physics_drag_boost;
  18. BEGIN_DATADESC( CTriggerCatapult )
  19. DEFINE_THINKFUNC( LaunchThink ),
  20. DEFINE_THINKFUNC( PlayerPassesTriggerFiltersThink ),
  21. DEFINE_KEYFIELD( m_flPlayerVelocity, FIELD_FLOAT, "playerSpeed" ),
  22. DEFINE_KEYFIELD( m_flPhysicsVelocity, FIELD_FLOAT, "physicsSpeed" ),
  23. DEFINE_KEYFIELD( m_vecLaunchAngles, FIELD_VECTOR, "launchDirection" ),
  24. DEFINE_KEYFIELD( m_strLaunchTarget, FIELD_STRING, "launchTarget" ),
  25. DEFINE_KEYFIELD( m_bUseThresholdCheck, FIELD_BOOLEAN, "useThresholdCheck" ),
  26. DEFINE_KEYFIELD( m_bUseExactVelocity, FIELD_BOOLEAN, "useExactVelocity" ),
  27. DEFINE_KEYFIELD( m_flLowerThreshold, FIELD_FLOAT, "lowerThreshold" ),
  28. DEFINE_KEYFIELD( m_flUpperThreshold, FIELD_FLOAT, "upperThreshold" ),
  29. DEFINE_KEYFIELD( m_ExactVelocityChoice, FIELD_INTEGER, "exactVelocityChoiceType" ),
  30. DEFINE_KEYFIELD( m_bOnlyVelocityCheck, FIELD_BOOLEAN, "onlyVelocityCheck" ),
  31. DEFINE_KEYFIELD( m_bApplyAngularImpulse, FIELD_BOOLEAN, "applyAngularImpulse" ),
  32. DEFINE_KEYFIELD( m_flEntryAngleTolerance, FIELD_FLOAT, "EntryAngleTolerance" ),
  33. DEFINE_KEYFIELD( m_flAirControlSupressionTime, FIELD_FLOAT, "AirCtrlSupressionTime" ),
  34. DEFINE_KEYFIELD( m_bDirectionSuppressAirControl, FIELD_BOOLEAN, "DirectionSuppressAirControl" ),
  35. DEFINE_FIELD( m_hLaunchTarget, FIELD_EHANDLE ),
  36. DEFINE_ARRAY( m_flRefireDelay, FIELD_TIME, MAX_PLAYERS + 1 ),
  37. DEFINE_UTLVECTOR( m_hAbortedLaunchees, FIELD_EHANDLE ),
  38. DEFINE_INPUTFUNC( FIELD_FLOAT, "SetPlayerSpeed", InputSetPlayerSpeed ),
  39. DEFINE_INPUTFUNC( FIELD_FLOAT, "SetPhysicsSpeed", InputSetPhysicsSpeed ),
  40. DEFINE_INPUTFUNC( FIELD_STRING,"SetLaunchTarget", InputSetLaunchTarget ),
  41. DEFINE_INPUTFUNC( FIELD_INTEGER, "SetExactVelocityChoiceType", InputSetExactVelocityChoiceType ),
  42. DEFINE_OUTPUT( m_OnCatapulted, "OnCatapulted" ),
  43. END_DATADESC()
  44. LINK_ENTITY_TO_CLASS( trigger_catapult, CTriggerCatapult );
  45. //IMPLEMENT_SERVERCLASS_ST( CTriggerCatapult, DT_TriggerCatapult )
  46. // SendPropArray3( SENDINFO_ARRAY3(m_flRefireDelay), SendPropFloat(SENDINFO_ARRAY(m_flRefireDelay)) ),
  47. // SendPropFloat( SENDINFO( m_flPlayerVelocity ) ),
  48. // SendPropFloat( SENDINFO( m_flPhysicsVelocity ) ),
  49. // SendPropQAngles( SENDINFO( m_vecLaunchAngles ) ),
  50. // //SendPropStringT( SENDINFO( m_strLaunchTarget ) ),
  51. // SendPropInt( SENDINFO( m_ExactVelocityChoice ) ),
  52. // SendPropBool( SENDINFO( m_bUseExactVelocity ) ),
  53. // SendPropBool( SENDINFO( m_bUseThresholdCheck ) ),
  54. // SendPropBool( SENDINFO( m_bOnlyVelocityCheck ) ),
  55. // SendPropFloat( SENDINFO( m_flLowerThreshold ) ),
  56. // SendPropFloat( SENDINFO( m_flUpperThreshold ) ),
  57. // SendPropFloat( SENDINFO( m_flAirControlSupressionTime ) ),
  58. // SendPropBool( SENDINFO( m_bApplyAngularImpulse ) ),
  59. // SendPropFloat( SENDINFO( m_flEntryAngleTolerance ) ),
  60. // SendPropEHandle( SENDINFO( m_hLaunchTarget ) ),
  61. // SendPropBool( SENDINFO( m_bPlayersPassTriggerFilters ) ),
  62. // SendPropBool( SENDINFO( m_bDirectionSuppressAirControl ) ),
  63. //END_SEND_TABLE()
  64. //-----------------------------------------------------------------------------
  65. // Purpose:
  66. //-----------------------------------------------------------------------------
  67. CTriggerCatapult::CTriggerCatapult( void )
  68. {
  69. //Defaulting to true;
  70. m_bApplyAngularImpulse = true;
  71. m_flAirControlSupressionTime = -1.0f;
  72. }
  73. //-----------------------------------------------------------------------------
  74. // Purpose:
  75. //-----------------------------------------------------------------------------
  76. void CTriggerCatapult::DrawDebugGeometryOverlays( void )
  77. {
  78. BaseClass::DrawDebugGeometryOverlays();
  79. CBaseEntity *pLaunchTarget = m_hLaunchTarget;
  80. if ( pLaunchTarget )
  81. {
  82. // Help us visualize the target
  83. Vector vecSourcePos = GetAbsOrigin();
  84. Vector vecTargetPos = pLaunchTarget->GetAbsOrigin();
  85. float flSpeed = m_flPlayerVelocity;
  86. float flGravity = sv_gravity.GetFloat();
  87. Vector vecVelocity = (vecTargetPos - vecSourcePos);
  88. // This is a hack to get around air resistance with weighted cubes -- this is not intended for all objects!
  89. // float flDragCoefficient = (pVictim->IsPlayer()) ? 1.0f : ( 1.6f );
  90. float flDragCoefficient = 0.0f;
  91. // throw at a constant time
  92. float time = vecVelocity.Length( ) / flSpeed;
  93. vecVelocity = vecVelocity * (1.0 / time) * flDragCoefficient;
  94. // adjust upward toss to compensate for gravity loss
  95. vecVelocity.z += flGravity * time * 0.5;
  96. Vector vecApex = vecSourcePos + (vecTargetPos - vecSourcePos) * 0.5;
  97. vecApex.z += 0.5 * flGravity * (time * 0.5) * (time * 0.5);
  98. // Visualize it!
  99. if( !m_bUseExactVelocity )
  100. {
  101. NDebugOverlay::Box( vecSourcePos, -Vector(2,2,2), Vector(2,2,2), 0, 255, 0, 8.0f, 0.05f );
  102. NDebugOverlay::Box( vecTargetPos, -Vector(2,2,2), Vector(2,2,2), 0, 255, 0, 8.0f, 0.05f );
  103. NDebugOverlay::Box( vecApex, -Vector(2,2,2), Vector(2,2,2), 0, 255, 0, 8.0f, 0.05f );
  104. NDebugOverlay::Line( vecSourcePos, vecApex, 0, 255, 0, false, 0.05f );
  105. NDebugOverlay::Line( vecApex, vecTargetPos, 0, 255, 0, false, 0.05f );
  106. }
  107. else
  108. {
  109. Vector lastPos = vecSourcePos;
  110. vecVelocity = (vecTargetPos - vecSourcePos);
  111. vecVelocity = CalculateLaunchVectorPreserve( vecVelocity, this, pLaunchTarget, true );
  112. for( int i = 0; i < 20; i++ )
  113. {
  114. float flTime = 0.2f*(i+1);
  115. vecApex = vecSourcePos + vecVelocity*flTime;
  116. vecApex.z -= 0.5 * flGravity * (flTime) * (flTime);
  117. NDebugOverlay::Box( vecApex, -Vector(2,2,2), Vector(2,2,2), 0, 255, 0, 8.0f, 0.05f );
  118. NDebugOverlay::Line( vecApex, lastPos, 0, 255, 0, false, 0.05f );
  119. lastPos = vecApex;
  120. }
  121. }
  122. // Physics!
  123. flSpeed = m_flPhysicsVelocity;
  124. vecVelocity = (vecTargetPos - vecSourcePos);
  125. // This is a hack to get around air resistance with weighted cubes -- this is not intended for all objects!
  126. flDragCoefficient = catapult_physics_drag_boost.GetFloat();
  127. // throw at a constant time
  128. time = vecVelocity.Length( ) / flSpeed;
  129. vecVelocity = vecVelocity * (1.0 / time) * flDragCoefficient;
  130. // adjust upward toss to compensate for gravity loss
  131. vecVelocity.z += flGravity * time * 0.5;
  132. vecApex = vecSourcePos + (vecTargetPos - vecSourcePos) * 0.5;
  133. vecApex.z += 0.5 * flGravity * (time * 0.5) * (time * 0.5);
  134. // Visualize it!
  135. if( !m_bUseExactVelocity )
  136. {
  137. NDebugOverlay::Box( vecApex, -Vector(2,2,2), Vector(2,2,2), 255, 255, 0, 8.0f, 0.05f );
  138. NDebugOverlay::Line( vecSourcePos, vecApex, 255, 255, 0, false, 0.05f );
  139. NDebugOverlay::Line( vecApex, vecTargetPos, 255, 255, 0, false, 0.05f );
  140. }
  141. else
  142. {
  143. Vector lastPos = vecSourcePos;
  144. vecVelocity = (vecTargetPos - vecSourcePos);
  145. vecVelocity = CalculateLaunchVectorPreserve( vecVelocity, this, pLaunchTarget );
  146. for( int i = 0; i < 20; i++ )
  147. {
  148. float flTime = 0.2f*(i+1);
  149. vecApex = vecSourcePos + vecVelocity*flTime;
  150. vecApex.z -= 0.5 * flGravity * (flTime) * (flTime);
  151. NDebugOverlay::Box( vecApex, -Vector(2,2,2), Vector(2,2,2), 255, 255, 0, 8.0f, 0.05f );
  152. NDebugOverlay::Line( vecApex, lastPos, 255, 255, 0, false, 0.05f );
  153. lastPos = vecApex;
  154. }
  155. }
  156. }
  157. else
  158. {
  159. // Meh
  160. }
  161. }
  162. //---------------------------------------------------------
  163. //---------------------------------------------------------
  164. int CTriggerCatapult::DrawDebugTextOverlays(void)
  165. {
  166. int text_offset = BaseClass::DrawDebugTextOverlays();
  167. if (m_debugOverlays & OVERLAY_TEXT_BIT)
  168. {
  169. char tempstr[512];
  170. Q_snprintf(tempstr,sizeof(tempstr), "Launch target: %s", m_strLaunchTarget.ToCStr() );
  171. EntityText(text_offset,tempstr,0);
  172. text_offset++;
  173. if ( m_bUseThresholdCheck )
  174. {
  175. Q_snprintf(tempstr,sizeof(tempstr), "Lower threshold velocity: %.2f", m_flLowerThreshold );
  176. EntityText(text_offset,tempstr,0);
  177. text_offset++;
  178. Q_snprintf(tempstr,sizeof(tempstr), "Upper threshold velocity: %.2f", m_flUpperThreshold );
  179. EntityText(text_offset,tempstr,0);
  180. text_offset++;
  181. }
  182. Q_snprintf(tempstr,sizeof(tempstr), "Player velocity: %.2f", m_flPlayerVelocity );
  183. EntityText(text_offset,tempstr,0);
  184. text_offset++;
  185. Q_snprintf(tempstr,sizeof(tempstr), "Physics velocity: %.2f", m_flPhysicsVelocity );
  186. EntityText(text_offset,tempstr,0);
  187. text_offset++;
  188. // Get the target
  189. CBaseEntity *pLaunchTarget = m_hLaunchTarget;
  190. // See if we're attempting to hit a target
  191. if ( pLaunchTarget )
  192. {
  193. Vector vecSourcePos = GetAbsOrigin();
  194. float flGravity = sv_gravity.GetFloat();
  195. {
  196. Vector vecTargetPos = pLaunchTarget->GetAbsOrigin();
  197. vecTargetPos.z -= 32.0f;
  198. Vector vecVelocity = (vecTargetPos - vecSourcePos);
  199. // throw at a constant time
  200. float time = vecVelocity.Length( ) / m_flPlayerVelocity;
  201. vecVelocity = vecVelocity * (1.0 / time);
  202. // adjust upward toss to compensate for gravity loss
  203. vecVelocity.z += flGravity * time * 0.5;
  204. Q_snprintf(tempstr,sizeof(tempstr), "Adjusted Player velocity: %.2f", vecVelocity.Length() );
  205. EntityText(text_offset,tempstr,0);
  206. text_offset++;
  207. }
  208. {
  209. Vector vecTargetPos = pLaunchTarget->GetAbsOrigin();
  210. Vector vecVelocity = (vecTargetPos - vecSourcePos );
  211. // throw at a constant time
  212. float time = vecVelocity.Length( ) / m_flPhysicsVelocity;
  213. vecVelocity = vecVelocity * (1.0 / time);
  214. // adjust upward toss to compensate for gravity loss
  215. vecVelocity.z += flGravity * time * 0.5;
  216. Q_snprintf(tempstr,sizeof(tempstr), "Adjusted Physics velocity: %.2f", vecVelocity.Length() );
  217. EntityText(text_offset,tempstr,0);
  218. text_offset++;
  219. }
  220. }
  221. }
  222. return text_offset;
  223. }
  224. //-----------------------------------------------------------------------------
  225. // Purpose:
  226. //-----------------------------------------------------------------------------
  227. void CTriggerCatapult::Spawn( void )
  228. {
  229. BaseClass::Spawn();
  230. // Don't let the camera shoot through us!
  231. InitTrigger();
  232. for ( int i = 0; i < MAX_PLAYERS + 1; ++i )
  233. {
  234. m_flRefireDelay[i] = 0.0f;
  235. }
  236. m_flLowerThreshold = clamp( m_flLowerThreshold, 0.0f, 1.0f );
  237. m_flUpperThreshold = clamp( m_flUpperThreshold, 0.0f, 1.0f );
  238. SetTransmitState( FL_EDICT_PVSCHECK );
  239. m_hLaunchTarget = gEntList.FindEntityByName( NULL, m_strLaunchTarget );
  240. SetContextThink( &CTriggerCatapult::PlayerPassesTriggerFiltersThink, gpGlobals->curtime + 1.0f, s_szPlayerPassesTriggerFiltersThinkContext );
  241. }
  242. //-----------------------------------------------------------------------------
  243. // Purpose:
  244. //-----------------------------------------------------------------------------
  245. void CTriggerCatapult::InputSetPlayerSpeed( inputdata_t &in )
  246. {
  247. m_flPlayerVelocity = in.value.Float();
  248. }
  249. //-----------------------------------------------------------------------------
  250. // Purpose:
  251. //-----------------------------------------------------------------------------
  252. void CTriggerCatapult::InputSetPhysicsSpeed( inputdata_t &in )
  253. {
  254. m_flPhysicsVelocity = in.value.Float();
  255. }
  256. //-----------------------------------------------------------------------------
  257. // Purpose:
  258. //-----------------------------------------------------------------------------
  259. void CTriggerCatapult::InputSetLaunchTarget( inputdata_t &in )
  260. {
  261. m_strLaunchTarget = in.value.StringID();
  262. m_hLaunchTarget = gEntList.FindEntityByName( NULL, m_strLaunchTarget );
  263. }
  264. //-----------------------------------------------------------------------------
  265. // Purpose:
  266. //-----------------------------------------------------------------------------
  267. void CTriggerCatapult::InputSetExactVelocityChoiceType( inputdata_t &in )
  268. {
  269. m_ExactVelocityChoice = in.value.Int();
  270. }
  271. //-----------------------------------------------------------------------------
  272. // Purpose:
  273. //-----------------------------------------------------------------------------
  274. void CTriggerCatapult::LaunchThink( void )
  275. {
  276. for ( int i = 0; i < m_hAbortedLaunchees.Count(); i++ )
  277. {
  278. CBaseEntity *pOther = m_hAbortedLaunchees[i].Get();
  279. bool bShouldRemove = true;
  280. if ( pOther )
  281. {
  282. if ( pOther->IsPlayer() )
  283. {
  284. // Time to get launched and stay in the list in case we're stuck under something again.
  285. bShouldRemove = false;
  286. StartTouch( pOther );
  287. }
  288. else if ( pOther->VPhysicsGetObject() )
  289. {
  290. if( ( pOther->VPhysicsGetObject()->GetGameFlags() & FVPHYSICS_PLAYER_HELD ) )
  291. {
  292. // the sphere should stay in the list.
  293. bShouldRemove = false;
  294. }
  295. else
  296. {
  297. // Time to get launched!
  298. StartTouch( pOther );
  299. }
  300. }
  301. }
  302. if ( bShouldRemove )
  303. {
  304. m_hAbortedLaunchees.Remove( i );
  305. i--;
  306. }
  307. }
  308. // see if we are still holding something in the catapult
  309. if( m_hAbortedLaunchees.Count() )
  310. {
  311. SetThink( &CTriggerCatapult::LaunchThink );
  312. SetNextThink( gpGlobals->curtime + 0.05f );
  313. }
  314. else
  315. {
  316. SetThink( NULL );
  317. }
  318. }
  319. //Think once a second, looking for a living player. Once one is found, evaluate if they pass our trigger filters and network that down to the client
  320. void CTriggerCatapult::PlayerPassesTriggerFiltersThink( void )
  321. {
  322. for( int i = 1; i != gpGlobals->maxClients; ++i )
  323. {
  324. CBasePlayer *pPlayer = UTIL_PlayerByIndex( i );
  325. if( pPlayer && pPlayer->IsAlive() )
  326. {
  327. m_bPlayersPassTriggerFilters = PassesTriggerFilters( pPlayer );
  328. SetContextThink( NULL, TICK_NEVER_THINK, s_szPlayerPassesTriggerFiltersThinkContext ); //never test again
  329. return;
  330. }
  331. }
  332. SetContextThink( &CTriggerCatapult::PlayerPassesTriggerFiltersThink, gpGlobals->curtime + 1.0f, s_szPlayerPassesTriggerFiltersThinkContext );
  333. }
  334. //-----------------------------------------------------------------------------
  335. // Purpose:
  336. //-----------------------------------------------------------------------------
  337. void CTriggerCatapult::EndTouch( CBaseEntity *pOther )
  338. {
  339. m_hAbortedLaunchees.FindAndFastRemove( pOther );
  340. }