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.

482 lines
14 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //
  7. //=============================================================================//
  8. #include "cbase.h"
  9. #include "basecombatcharacter.h"
  10. #include "entityoutput.h"
  11. #include "physics.h"
  12. #include "explode.h"
  13. #include "vphysics_interface.h"
  14. #include "collisionutils.h"
  15. #include "steamjet.h"
  16. #include "eventqueue.h"
  17. #include "soundflags.h"
  18. #include "engine/IEngineSound.h"
  19. #include "props.h"
  20. #include "physics_cannister.h"
  21. #include "globals.h"
  22. #include "physics_saverestore.h"
  23. #include "shareddefs.h"
  24. // memdbgon must be the last include file in a .cpp file!!!
  25. #include "tier0/memdbgon.h"
  26. #define SF_CANNISTER_ASLEEP 0x0001
  27. #define SF_CANNISTER_EXPLODE 0x0002
  28. BEGIN_SIMPLE_DATADESC( CThrustController )
  29. DEFINE_FIELD( m_thrustVector, FIELD_VECTOR ),
  30. DEFINE_FIELD( m_torqueVector, FIELD_VECTOR ),
  31. DEFINE_KEYFIELD( m_thrust, FIELD_FLOAT, "thrust" ),
  32. END_DATADESC()
  33. LINK_ENTITY_TO_CLASS( physics_cannister, CPhysicsCannister );
  34. BEGIN_DATADESC( CPhysicsCannister )
  35. DEFINE_OUTPUT( m_onActivate, "OnActivate" ),
  36. DEFINE_OUTPUT( m_OnAwakened, "OnAwakened" ),
  37. DEFINE_FIELD( m_thrustOrigin, FIELD_VECTOR ), // this is a position, but in local space
  38. DEFINE_EMBEDDED( m_thruster ),
  39. DEFINE_PHYSPTR( m_pController ),
  40. DEFINE_FIELD( m_pJet, FIELD_CLASSPTR ),
  41. DEFINE_FIELD( m_active, FIELD_BOOLEAN ),
  42. DEFINE_KEYFIELD( m_thrustTime, FIELD_FLOAT, "fuel" ),
  43. DEFINE_KEYFIELD( m_damage, FIELD_FLOAT, "expdamage" ),
  44. DEFINE_KEYFIELD( m_damageRadius, FIELD_FLOAT, "expradius" ),
  45. DEFINE_FIELD( m_activateTime, FIELD_TIME ),
  46. DEFINE_KEYFIELD( m_gasSound, FIELD_SOUNDNAME, "gassound" ),
  47. DEFINE_FIELD( m_bFired, FIELD_BOOLEAN ),
  48. // Physics Influence
  49. DEFINE_FIELD( m_hPhysicsAttacker, FIELD_EHANDLE ),
  50. DEFINE_FIELD( m_flLastPhysicsInfluenceTime, FIELD_TIME ),
  51. DEFINE_FIELD( m_hLauncher, FIELD_EHANDLE ),
  52. DEFINE_INPUTFUNC( FIELD_VOID, "Activate", InputActivate ),
  53. DEFINE_INPUTFUNC( FIELD_VOID, "Deactivate", InputDeactivate ),
  54. DEFINE_INPUTFUNC( FIELD_VOID, "Explode", InputExplode ),
  55. DEFINE_INPUTFUNC( FIELD_VOID, "Wake", InputWake ),
  56. DEFINE_THINKFUNC( BeginShutdownThink ),
  57. DEFINE_ENTITYFUNC( ExplodeTouch ),
  58. END_DATADESC()
  59. void CPhysicsCannister::Spawn( void )
  60. {
  61. Precache();
  62. SetModel( STRING(GetModelName()) );
  63. SetBloodColor( DONT_BLEED );
  64. AddSolidFlags( FSOLID_CUSTOMRAYTEST );
  65. m_takedamage = DAMAGE_YES;
  66. SetNextThink( TICK_NEVER_THINK );
  67. if ( m_iHealth <= 0 )
  68. m_iHealth = 25;
  69. m_flAnimTime = gpGlobals->curtime;
  70. m_flPlaybackRate = 0.0;
  71. SetCycle( 0 );
  72. m_bFired = false;
  73. // not thrusting
  74. m_active = false;
  75. CreateVPhysics();
  76. if ( !VPhysicsGetObject() )
  77. {
  78. // must have a physics object or code will crash later
  79. UTIL_Remove(this);
  80. }
  81. }
  82. void CPhysicsCannister::OnRestore()
  83. {
  84. BaseClass::OnRestore();
  85. if ( m_pController )
  86. {
  87. m_pController->SetEventHandler( &m_thruster );
  88. }
  89. }
  90. bool CPhysicsCannister::CreateVPhysics()
  91. {
  92. bool asleep = HasSpawnFlags(SF_CANNISTER_ASLEEP);
  93. VPhysicsInitNormal( SOLID_VPHYSICS, 0, asleep );
  94. return true;
  95. }
  96. bool CPhysicsCannister::TestCollision( const Ray_t &ray, unsigned int mask, trace_t& trace )
  97. {
  98. Vector vecAbsMins, vecAbsMaxs;
  99. CollisionProp()->WorldSpaceAABB( &vecAbsMins, &vecAbsMaxs );
  100. if ( !IsBoxIntersectingRay( vecAbsMins, vecAbsMaxs, ray.m_Start, ray.m_Delta ) )
  101. return false;
  102. return BaseClass::TestCollision( ray, mask, trace );
  103. }
  104. Vector CPhysicsCannister::CalcLocalThrust( const Vector &offset )
  105. {
  106. matrix3x4_t nozzleMatrix;
  107. Vector thrustDirection;
  108. GetAttachment( LookupAttachment("nozzle"), nozzleMatrix );
  109. MatrixGetColumn( nozzleMatrix, 2, thrustDirection );
  110. MatrixGetColumn( nozzleMatrix, 3, m_thrustOrigin );
  111. thrustDirection = -5*thrustDirection + offset;
  112. VectorNormalize( thrustDirection );
  113. return thrustDirection;
  114. }
  115. CPhysicsCannister::~CPhysicsCannister( void )
  116. {
  117. }
  118. void CPhysicsCannister::Precache( void )
  119. {
  120. PropBreakablePrecacheAll( GetModelName() );
  121. if ( m_gasSound != NULL_STRING )
  122. {
  123. PrecacheScriptSound( STRING(m_gasSound) );
  124. }
  125. BaseClass::Precache();
  126. }
  127. int CPhysicsCannister::OnTakeDamage( const CTakeDamageInfo &info )
  128. {
  129. // HACKHACK: Shouldn't g_vecAttackDir be a parameter to this function?
  130. if ( !m_takedamage )
  131. return 0;
  132. if ( !m_active )
  133. {
  134. m_iHealth -= info.GetDamage();
  135. if ( m_iHealth < 0 )
  136. {
  137. Explode( info.GetAttacker() );
  138. }
  139. else
  140. {
  141. // explosions that don't destroy will activate
  142. // 50% of the time blunt damage will activate as well
  143. if ( (info.GetDamageType() & DMG_BLAST) ||
  144. ( (info.GetDamageType() & (DMG_CLUB|DMG_SLASH|DMG_CRUSH) ) && random->RandomInt(1,100) < 50 ) )
  145. {
  146. CannisterActivate( info.GetAttacker(), g_vecAttackDir );
  147. }
  148. }
  149. return 1;
  150. }
  151. if ( (gpGlobals->curtime - m_activateTime) <= 0.1 )
  152. return 0;
  153. if ( info.GetDamageType() & (DMG_BULLET|DMG_BUCKSHOT|DMG_BURN|DMG_BLAST) )
  154. {
  155. Explode( info.GetAttacker() );
  156. }
  157. return 0;
  158. }
  159. void CPhysicsCannister::TraceAttack( const CTakeDamageInfo &info, const Vector &dir, trace_t *ptr, CDmgAccumulator *pAccumulator )
  160. {
  161. if ( !m_active && ptr->hitgroup != 0 )
  162. {
  163. Vector direction = -dir;
  164. direction.z -= 5;
  165. VectorNormalize( direction );
  166. CannisterActivate( info.GetAttacker(), direction );
  167. }
  168. BaseClass::TraceAttack( info, dir, ptr, pAccumulator );
  169. }
  170. //-----------------------------------------------------------------------------
  171. // Purpose:
  172. //-----------------------------------------------------------------------------
  173. void CPhysicsCannister::CannisterActivate( CBaseEntity *pActivator, const Vector &thrustOffset )
  174. {
  175. // already active or spent
  176. if ( m_active || !m_thrustTime )
  177. {
  178. return;
  179. }
  180. m_hLauncher = pActivator;
  181. Vector thrustDirection = CalcLocalThrust( thrustOffset );
  182. m_onActivate.FireOutput( pActivator, this, 0 );
  183. m_thruster.CalcThrust( m_thrustOrigin, thrustDirection, VPhysicsGetObject() );
  184. m_pController = physenv->CreateMotionController( &m_thruster );
  185. IPhysicsObject *pPhys = VPhysicsGetObject();
  186. m_pController->AttachObject( pPhys, true );
  187. // Make sure the object is simulated
  188. pPhys->Wake();
  189. m_active = true;
  190. m_activateTime = gpGlobals->curtime;
  191. SetNextThink( gpGlobals->curtime + m_thrustTime );
  192. SetThink( &CPhysicsCannister::BeginShutdownThink );
  193. QAngle angles;
  194. VectorAngles( -thrustDirection, angles );
  195. m_pJet = dynamic_cast<CSteamJet *>( CBaseEntity::Create( "env_steam", m_thrustOrigin, angles, this ) );
  196. m_pJet->SetParent( this );
  197. float extra = m_thruster.m_thrust * (1/5000.f);
  198. extra = clamp( extra, 0.f, 1.f );
  199. m_pJet->m_SpreadSpeed = 15 * m_thruster.m_thrust * 0.001;
  200. m_pJet->m_Speed = 128 + 100 * extra;
  201. m_pJet->m_StartSize = 10;
  202. m_pJet->m_EndSize = 25;
  203. m_pJet->m_Rate = 52 + (int)extra*20;
  204. m_pJet->m_JetLength = 64;
  205. m_pJet->m_clrRender = m_clrRender;
  206. m_pJet->Use( this, this, USE_ON, 1 );
  207. if ( m_gasSound != NULL_STRING )
  208. {
  209. CPASAttenuationFilter filter( this );
  210. EmitSound_t ep;
  211. ep.m_nChannel = CHAN_ITEM;
  212. ep.m_pSoundName = STRING(m_gasSound);
  213. ep.m_flVolume = 1.0f;
  214. ep.m_SoundLevel = SNDLVL_NORM;
  215. EmitSound( filter, entindex(), ep );
  216. }
  217. }
  218. //-----------------------------------------------------------------------------
  219. // Purpose: The cannister's been fired by a weapon, so it should stay pretty accurate
  220. //-----------------------------------------------------------------------------
  221. void CPhysicsCannister::CannisterFire( CBaseEntity *pActivator )
  222. {
  223. m_bFired = true;
  224. // Increase thrust
  225. m_thruster.m_thrust *= 4;
  226. // Only last a short time
  227. m_thrustTime = 10.0;
  228. // Explode on contact
  229. SetTouch( &CPhysicsCannister::ExplodeTouch );
  230. CannisterActivate( pActivator, vec3_origin );
  231. }
  232. //-----------------------------------------------------------------------------
  233. // Purpose: Input handler for activating the cannister.
  234. //-----------------------------------------------------------------------------
  235. void CPhysicsCannister::InputActivate( inputdata_t &data )
  236. {
  237. CannisterActivate( data.pActivator, Vector(0,0.1,-0.25) );
  238. }
  239. //-----------------------------------------------------------------------------
  240. // Purpose: Input handler for deactivating the cannister.
  241. //-----------------------------------------------------------------------------
  242. void CPhysicsCannister::InputDeactivate(inputdata_t &data)
  243. {
  244. Deactivate();
  245. }
  246. //-----------------------------------------------------------------------------
  247. // Purpose: Input handler for making the cannister go boom.
  248. //-----------------------------------------------------------------------------
  249. void CPhysicsCannister::InputExplode(inputdata_t &data)
  250. {
  251. Explode( data.pActivator );
  252. }
  253. //-----------------------------------------------------------------------------
  254. // Purpose: Input handler for waking up the cannister if it is sleeping.
  255. //-----------------------------------------------------------------------------
  256. void CPhysicsCannister::InputWake( inputdata_t &data )
  257. {
  258. IPhysicsObject *pPhys = VPhysicsGetObject();
  259. if ( pPhys != NULL )
  260. {
  261. pPhys->Wake();
  262. }
  263. }
  264. //-----------------------------------------------------------------------------
  265. // Purpose:
  266. //-----------------------------------------------------------------------------
  267. void CPhysicsCannister::Deactivate(void)
  268. {
  269. if ( !m_pController )
  270. return;
  271. m_pController->DetachObject( VPhysicsGetObject() );
  272. physenv->DestroyMotionController( m_pController );
  273. m_pController = NULL;
  274. SetNextThink( TICK_NEVER_THINK );
  275. m_thrustTime = 0;
  276. m_active = false;
  277. if ( m_pJet )
  278. {
  279. ShutdownJet();
  280. }
  281. if ( m_gasSound != NULL_STRING )
  282. {
  283. StopSound( entindex(), CHAN_ITEM, STRING(m_gasSound) );
  284. }
  285. }
  286. //-----------------------------------------------------------------------------
  287. // Purpose:
  288. //-----------------------------------------------------------------------------
  289. void CPhysicsCannister::Explode( CBaseEntity *pAttacker )
  290. {
  291. // don't recurse
  292. m_takedamage = 0;
  293. Deactivate();
  294. Vector velocity;
  295. AngularImpulse angVelocity;
  296. IPhysicsObject *pPhysics = VPhysicsGetObject();
  297. pPhysics->GetVelocity( &velocity, &angVelocity );
  298. PropBreakableCreateAll( GetModelIndex(), pPhysics, GetAbsOrigin(), GetAbsAngles(), velocity, angVelocity, 1.0, 20, COLLISION_GROUP_DEBRIS );
  299. ExplosionCreate( GetAbsOrigin(), GetAbsAngles(), pAttacker, m_damage, 0, true );
  300. UTIL_Remove( this );
  301. }
  302. //-----------------------------------------------------------------------------
  303. // Purpose: Explode when I next hit a damageable entity
  304. //-----------------------------------------------------------------------------
  305. void CPhysicsCannister::ExplodeTouch( CBaseEntity *pOther )
  306. {
  307. if ( !pOther->m_takedamage )
  308. return;
  309. Explode( m_hLauncher );
  310. }
  311. //-----------------------------------------------------------------------------
  312. // Purpose:
  313. //-----------------------------------------------------------------------------
  314. void CPhysicsCannister::VPhysicsCollision( int index, gamevcollisionevent_t *pEvent )
  315. {
  316. if ( m_bFired && m_active )
  317. {
  318. int otherIndex = !index;
  319. CBaseEntity *pHitEntity = pEvent->pEntities[otherIndex];
  320. if ( pEvent->deltaCollisionTime < 0.5 && (pHitEntity == this) )
  321. return;
  322. // If we hit hard enough. explode
  323. if ( pEvent->collisionSpeed > 1000 )
  324. {
  325. Explode( m_hLauncher );
  326. return;
  327. }
  328. }
  329. BaseClass::VPhysicsCollision( index, pEvent );
  330. }
  331. //-----------------------------------------------------------------------------
  332. // Purpose:
  333. //-----------------------------------------------------------------------------
  334. void CPhysicsCannister::ShutdownJet( void )
  335. {
  336. g_EventQueue.AddEvent( m_pJet, "kill", 5, NULL, NULL );
  337. m_pJet->m_bEmit = false;
  338. m_pJet->m_Rate = 0;
  339. m_pJet = NULL;
  340. SetNextThink( TICK_NEVER_THINK );
  341. }
  342. //-----------------------------------------------------------------------------
  343. // Purpose: The think just shuts the cannister down
  344. //-----------------------------------------------------------------------------
  345. void CPhysicsCannister::BeginShutdownThink( void )
  346. {
  347. Deactivate();
  348. }
  349. //-----------------------------------------------------------------------------
  350. // Physics Attacker
  351. //-----------------------------------------------------------------------------
  352. void CPhysicsCannister::SetPhysicsAttacker( CBasePlayer *pEntity, float flTime )
  353. {
  354. m_hPhysicsAttacker = pEntity;
  355. m_flLastPhysicsInfluenceTime = flTime;
  356. }
  357. //-----------------------------------------------------------------------------
  358. // Purpose: Keep track of physgun influence
  359. //-----------------------------------------------------------------------------
  360. void CPhysicsCannister::OnPhysGunPickup( CBasePlayer *pPhysGunUser, PhysGunPickup_t reason )
  361. {
  362. SetPhysicsAttacker( pPhysGunUser, gpGlobals->curtime );
  363. }
  364. //-----------------------------------------------------------------------------
  365. // Purpose:
  366. //-----------------------------------------------------------------------------
  367. void CPhysicsCannister::OnPhysGunDrop( CBasePlayer *pPhysGunUser, PhysGunDrop_t Reason )
  368. {
  369. SetPhysicsAttacker( pPhysGunUser, gpGlobals->curtime );
  370. if ( Reason == LAUNCHED_BY_CANNON )
  371. {
  372. CannisterActivate( pPhysGunUser, vec3_origin );
  373. }
  374. }
  375. //-----------------------------------------------------------------------------
  376. // Purpose:
  377. //-----------------------------------------------------------------------------
  378. CBasePlayer *CPhysicsCannister::HasPhysicsAttacker( float dt )
  379. {
  380. if (gpGlobals->curtime - dt <= m_flLastPhysicsInfluenceTime)
  381. {
  382. return m_hPhysicsAttacker;
  383. }
  384. return NULL;
  385. }
  386. //-----------------------------------------------------------------------------
  387. // Purpose: Update the visible representation of the physic system's representation of this object
  388. //-----------------------------------------------------------------------------
  389. void CPhysicsCannister::VPhysicsUpdate( IPhysicsObject *pPhysics )
  390. {
  391. BaseClass::VPhysicsUpdate( pPhysics );
  392. // if this is the first time we have moved, fire our target
  393. if ( HasSpawnFlags( SF_CANNISTER_ASLEEP ) )
  394. {
  395. if ( !pPhysics->IsAsleep() )
  396. {
  397. m_OnAwakened.FireOutput(this, this);
  398. RemoveSpawnFlags( SF_CANNISTER_ASLEEP );
  399. }
  400. }
  401. }