Counter Strike : Global Offensive Source Code
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.

1978 lines
58 KiB

  1. //========= Copyright � 1996-2005, Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================//
  7. #include "cbase.h"
  8. #include "player.h"
  9. #include "vphysics_interface.h"
  10. #include "physics.h"
  11. #include "vcollide_parse.h"
  12. #include "entitylist.h"
  13. #include "physobj.h"
  14. #include "hierarchy.h"
  15. #include "game.h"
  16. #include "ndebugoverlay.h"
  17. #include "engine/IEngineSound.h"
  18. #include "model_types.h"
  19. #include "props.h"
  20. #include "physics_saverestore.h"
  21. #include "saverestore_utlvector.h"
  22. #include "vphysics/constraints.h"
  23. #include "collisionutils.h"
  24. #include "decals.h"
  25. #include "bone_setup.h"
  26. #ifdef PORTAL
  27. #include "portal_base2d_shared.h"
  28. #include "particle_system.h"
  29. #endif // PORTAL
  30. // memdbgon must be the last include file in a .cpp file!!!
  31. #include "tier0/memdbgon.h"
  32. ConVar debug_physimpact("debug_physimpact", "0" );
  33. const char *GetMassEquivalent(float flMass);
  34. // This is a physically simulated spring, used to join objects together and create spring forces
  35. //
  36. // NOTE: Springs are not physical in the sense that they only create force, they do not collide with
  37. // anything or have any REAL constraints. They can be stretched infinitely (though this will create
  38. // and infinite force), they can penetrate any other object (or spring). They do not occupy any space.
  39. //
  40. #define SF_SPRING_ONLYSTRETCH 0x0001
  41. class CPhysicsSpring : public CBaseEntity
  42. {
  43. DECLARE_CLASS( CPhysicsSpring, CBaseEntity );
  44. public:
  45. CPhysicsSpring();
  46. ~CPhysicsSpring();
  47. void Spawn( void );
  48. void Activate( void );
  49. // Inputs
  50. void InputSetSpringConstant( inputdata_t &inputdata );
  51. void InputSetSpringDamping( inputdata_t &inputdata );
  52. void InputSetSpringLength( inputdata_t &inputdata );
  53. // Debug
  54. int DrawDebugTextOverlays(void);
  55. void DrawDebugGeometryOverlays(void);
  56. void GetSpringObjectConnections( string_t nameStart, string_t nameEnd, IPhysicsObject **pStart, IPhysicsObject **pEnd );
  57. void NotifySystemEvent( CBaseEntity *pNotify, notify_system_event_t eventType, const notify_system_event_params_t &params );
  58. IPhysicsObject *GetStartObject() { return m_pSpring ? m_pSpring->GetStartObject() : NULL; }
  59. IPhysicsObject *GetEndObject() { return m_pSpring ? m_pSpring->GetEndObject() : NULL; }
  60. DECLARE_DATADESC();
  61. private:
  62. IPhysicsSpring *m_pSpring;
  63. bool m_isLocal;
  64. // These are "template" values used to construct the spring. After creation, they are not needed
  65. float m_tempConstant;
  66. float m_tempLength; // This is the "ideal" length of the spring, not the length it is currently stretched to.
  67. float m_tempDamping;
  68. float m_tempRelativeDamping;
  69. string_t m_nameAttachStart;
  70. string_t m_nameAttachEnd;
  71. Vector m_start;
  72. Vector m_end;
  73. unsigned int m_teleportTick;
  74. };
  75. LINK_ENTITY_TO_CLASS( phys_spring, CPhysicsSpring );
  76. BEGIN_DATADESC( CPhysicsSpring )
  77. DEFINE_PHYSPTR( m_pSpring ),
  78. DEFINE_KEYFIELD( m_tempConstant, FIELD_FLOAT, "constant" ),
  79. DEFINE_KEYFIELD( m_tempLength, FIELD_FLOAT, "length" ),
  80. DEFINE_KEYFIELD( m_tempDamping, FIELD_FLOAT, "damping" ),
  81. DEFINE_KEYFIELD( m_tempRelativeDamping, FIELD_FLOAT, "relativedamping" ),
  82. DEFINE_KEYFIELD( m_nameAttachStart, FIELD_STRING, "attach1" ),
  83. DEFINE_KEYFIELD( m_nameAttachEnd, FIELD_STRING, "attach2" ),
  84. DEFINE_FIELD( m_start, FIELD_POSITION_VECTOR ),
  85. DEFINE_KEYFIELD( m_end, FIELD_POSITION_VECTOR, "springaxis" ),
  86. DEFINE_FIELD( m_isLocal, FIELD_BOOLEAN ),
  87. // Not necessary to save... it's only there to make sure
  88. // DEFINE_FIELD( m_teleportTick, FIELD_INTEGER ),
  89. // Inputs
  90. DEFINE_INPUTFUNC( FIELD_FLOAT, "SetSpringConstant", InputSetSpringConstant ),
  91. DEFINE_INPUTFUNC( FIELD_FLOAT, "SetSpringLength", InputSetSpringLength ),
  92. DEFINE_INPUTFUNC( FIELD_FLOAT, "SetSpringDamping", InputSetSpringDamping ),
  93. END_DATADESC()
  94. // debug function - slow, uses dynamic_cast<> - use this to query the attached objects
  95. // physics_debug_entity toggles the constraint system for an object using this
  96. bool GetSpringAttachments( CBaseEntity *pEntity, CBaseEntity *pAttachOut[2], IPhysicsObject *pAttachVPhysics[2] )
  97. {
  98. CPhysicsSpring *pSpringEntity = dynamic_cast<CPhysicsSpring *>(pEntity);
  99. if ( pSpringEntity )
  100. {
  101. IPhysicsObject *pRef = pSpringEntity->GetStartObject();
  102. pAttachOut[0] = pRef ? static_cast<CBaseEntity *>(pRef->GetGameData()) : NULL;
  103. pAttachVPhysics[0] = pRef;
  104. IPhysicsObject *pAttach = pSpringEntity->GetEndObject();
  105. pAttachOut[1] = pAttach ? static_cast<CBaseEntity *>(pAttach->GetGameData()) : NULL;
  106. pAttachVPhysics[1] = pAttach;
  107. return true;
  108. }
  109. return false;
  110. }
  111. CPhysicsSpring::CPhysicsSpring( void )
  112. {
  113. #ifdef _DEBUG
  114. m_start.Init();
  115. m_end.Init();
  116. #endif
  117. m_pSpring = NULL;
  118. m_tempConstant = 150;
  119. m_tempLength = 0;
  120. m_tempDamping = 2.0;
  121. m_tempRelativeDamping = 0.01;
  122. m_isLocal = false;
  123. m_teleportTick = 0xFFFFFFFF;
  124. }
  125. CPhysicsSpring::~CPhysicsSpring( void )
  126. {
  127. if ( m_pSpring )
  128. {
  129. physenv->DestroySpring( m_pSpring );
  130. }
  131. }
  132. //------------------------------------------------------------------------------
  133. // Purpose:
  134. //------------------------------------------------------------------------------
  135. void CPhysicsSpring::InputSetSpringConstant( inputdata_t &inputdata )
  136. {
  137. m_tempConstant = inputdata.value.Float();
  138. m_pSpring->SetSpringConstant(inputdata.value.Float());
  139. }
  140. //------------------------------------------------------------------------------
  141. // Purpose:
  142. //------------------------------------------------------------------------------
  143. void CPhysicsSpring::InputSetSpringDamping( inputdata_t &inputdata )
  144. {
  145. m_tempDamping = inputdata.value.Float();
  146. m_pSpring->SetSpringDamping(inputdata.value.Float());
  147. }
  148. //------------------------------------------------------------------------------
  149. // Purpose:
  150. //------------------------------------------------------------------------------
  151. void CPhysicsSpring::InputSetSpringLength( inputdata_t &inputdata )
  152. {
  153. m_tempLength = inputdata.value.Float();
  154. m_pSpring->SetSpringLength(inputdata.value.Float());
  155. }
  156. //-----------------------------------------------------------------------------
  157. // Purpose: Draw any debug text overlays
  158. // Output : Current text offset from the top
  159. //-----------------------------------------------------------------------------
  160. int CPhysicsSpring::DrawDebugTextOverlays(void)
  161. {
  162. int text_offset = BaseClass::DrawDebugTextOverlays();
  163. if (m_debugOverlays & OVERLAY_TEXT_BIT)
  164. {
  165. char tempstr[512];
  166. Q_snprintf(tempstr,sizeof(tempstr),"Constant: %3.2f",m_tempConstant);
  167. EntityText(text_offset,tempstr,0);
  168. text_offset++;
  169. Q_snprintf(tempstr,sizeof(tempstr),"Length: %3.2f",m_tempLength);
  170. EntityText(text_offset,tempstr,0);
  171. text_offset++;
  172. Q_snprintf(tempstr,sizeof(tempstr),"Damping: %3.2f",m_tempDamping);
  173. EntityText(text_offset,tempstr,0);
  174. text_offset++;
  175. }
  176. return text_offset;
  177. }
  178. //-----------------------------------------------------------------------------
  179. // Purpose: Override base class to add display of fly direction
  180. // Input :
  181. // Output :
  182. //-----------------------------------------------------------------------------
  183. void CPhysicsSpring::DrawDebugGeometryOverlays(void)
  184. {
  185. if ( !m_pSpring )
  186. return;
  187. // ------------------------------
  188. // Draw if BBOX is on
  189. // ------------------------------
  190. if (m_debugOverlays & OVERLAY_BBOX_BIT)
  191. {
  192. Vector vStartPos;
  193. Vector vEndPos;
  194. m_pSpring->GetEndpoints( &vStartPos, &vEndPos );
  195. Vector vSpringDir = vEndPos - vStartPos;
  196. VectorNormalize(vSpringDir);
  197. Vector vLength = vStartPos + (vSpringDir*m_tempLength);
  198. NDebugOverlay::Line(vStartPos, vLength, 0,0,255, false, 0);
  199. NDebugOverlay::Line(vLength, vEndPos, 255,0,0, false, 0);
  200. }
  201. BaseClass::DrawDebugGeometryOverlays();
  202. }
  203. bool PointIsNearer( IPhysicsObject *pObject1, const Vector &point1, const Vector &point2 )
  204. {
  205. Vector center;
  206. pObject1->GetPosition( &center, 0 );
  207. float dist1 = (center - point1).LengthSqr();
  208. float dist2 = (center - point2).LengthSqr();
  209. if ( dist1 < dist2 )
  210. return true;
  211. return false;
  212. }
  213. void CPhysicsSpring::GetSpringObjectConnections( string_t nameStart, string_t nameEnd, IPhysicsObject **pStart, IPhysicsObject **pEnd )
  214. {
  215. IPhysicsObject *pStartObject = FindPhysicsObjectByName( STRING(nameStart), this );
  216. IPhysicsObject *pEndObject = FindPhysicsObjectByName( STRING(nameEnd), this );
  217. // Assume the world for missing objects
  218. if ( !pStartObject )
  219. {
  220. pStartObject = g_PhysWorldObject;
  221. }
  222. else if ( !pEndObject )
  223. {
  224. // try to sort so that the world is always the start object
  225. pEndObject = pStartObject;
  226. pStartObject = g_PhysWorldObject;
  227. }
  228. else
  229. {
  230. CBaseEntity *pEntity0 = (CBaseEntity *) (pStartObject->GetGameData());
  231. if ( pEntity0 )
  232. {
  233. g_pNotify->AddEntity( this, pEntity0 );
  234. }
  235. CBaseEntity *pEntity1 = (CBaseEntity *) pEndObject->GetGameData();
  236. if ( pEntity1 )
  237. {
  238. g_pNotify->AddEntity( this, pEntity1 );
  239. }
  240. }
  241. *pStart = pStartObject;
  242. *pEnd = pEndObject;
  243. }
  244. void CPhysicsSpring::Activate( void )
  245. {
  246. BaseClass::Activate();
  247. // UNDONE: save/restore all data, and only create the spring here
  248. if ( !m_pSpring )
  249. {
  250. IPhysicsObject *pStart, *pEnd;
  251. GetSpringObjectConnections( m_nameAttachStart, m_nameAttachEnd, &pStart, &pEnd );
  252. // Needs to connect to real, different objects
  253. if ( (!pStart || !pEnd) || (pStart == pEnd) )
  254. {
  255. DevMsg("ERROR: Can't init spring %s from \"%s\" to \"%s\"\n", GetDebugName(), STRING(m_nameAttachStart), STRING(m_nameAttachEnd) );
  256. UTIL_Remove( this );
  257. return;
  258. }
  259. // if m_end is not closer to pEnd than m_start, swap
  260. if ( !PointIsNearer( pEnd, m_end, m_start ) )
  261. {
  262. Vector tmpVec = m_start;
  263. m_start = m_end;
  264. m_end = tmpVec;
  265. }
  266. // create the spring
  267. springparams_t spring;
  268. spring.constant = m_tempConstant;
  269. spring.damping = m_tempDamping;
  270. spring.naturalLength = m_tempLength;
  271. spring.relativeDamping = m_tempRelativeDamping;
  272. spring.startPosition = m_start;
  273. spring.endPosition = m_end;
  274. spring.useLocalPositions = false;
  275. spring.onlyStretch = HasSpawnFlags( SF_SPRING_ONLYSTRETCH );
  276. m_pSpring = physenv->CreateSpring( pStart, pEnd, &spring );
  277. }
  278. }
  279. void CPhysicsSpring::Spawn( void )
  280. {
  281. SetSolid( SOLID_NONE );
  282. m_start = GetAbsOrigin();
  283. if ( m_tempLength <= 0 )
  284. {
  285. m_tempLength = (m_end - m_start).Length();
  286. }
  287. }
  288. void CPhysicsSpring::NotifySystemEvent( CBaseEntity *pNotify, notify_system_event_t eventType, const notify_system_event_params_t &params )
  289. {
  290. // don't recurse
  291. if ( eventType != NOTIFY_EVENT_TELEPORT || (unsigned int)gpGlobals->tickcount == m_teleportTick )
  292. return;
  293. m_teleportTick = gpGlobals->tickcount;
  294. PhysTeleportConstrainedEntity( pNotify, m_pSpring->GetStartObject(), m_pSpring->GetEndObject(), params.pTeleport->prevOrigin, params.pTeleport->prevAngles, params.pTeleport->physicsRotate );
  295. }
  296. // ---------------------------------------------------------------------
  297. //
  298. // CPhysBox -- physically simulated brush
  299. //
  300. // ---------------------------------------------------------------------
  301. // SendTable stuff.
  302. IMPLEMENT_SERVERCLASS_ST(CPhysBox, DT_PhysBox)
  303. END_SEND_TABLE()
  304. LINK_ENTITY_TO_CLASS( func_physbox, CPhysBox );
  305. BEGIN_DATADESC( CPhysBox )
  306. DEFINE_FIELD( m_hCarryingPlayer, FIELD_EHANDLE ),
  307. DEFINE_KEYFIELD( m_massScale, FIELD_FLOAT, "massScale" ),
  308. DEFINE_KEYFIELD( m_damageType, FIELD_INTEGER, "Damagetype" ),
  309. DEFINE_KEYFIELD( m_iszOverrideScript, FIELD_STRING, "overridescript" ),
  310. DEFINE_KEYFIELD( m_damageToEnableMotion, FIELD_INTEGER, "damagetoenablemotion" ),
  311. DEFINE_KEYFIELD( m_flForceToEnableMotion, FIELD_FLOAT, "forcetoenablemotion" ),
  312. DEFINE_KEYFIELD( m_angPreferredCarryAngles, FIELD_VECTOR, "preferredcarryangles" ),
  313. DEFINE_KEYFIELD( m_bNotSolidToWorld, FIELD_BOOLEAN, "notsolid" ),
  314. DEFINE_KEYFIELD( m_iExploitableByPlayer, FIELD_INTEGER, "ExploitableByPlayer" ),
  315. DEFINE_INPUTFUNC( FIELD_VOID, "Wake", InputWake ),
  316. DEFINE_INPUTFUNC( FIELD_VOID, "Sleep", InputSleep ),
  317. DEFINE_INPUTFUNC( FIELD_VOID, "EnableMotion", InputEnableMotion ),
  318. DEFINE_INPUTFUNC( FIELD_VOID, "DisableMotion", InputDisableMotion ),
  319. DEFINE_INPUTFUNC( FIELD_VOID, "ForceDrop", InputForceDrop ),
  320. DEFINE_INPUTFUNC( FIELD_VOID, "DisableFloating", InputDisableFloating ),
  321. DEFINE_INPUTFUNC( FIELD_VOID, "BecomeDebris", InputBecomeDebris ),
  322. DEFINE_INPUTFUNC( FIELD_VOID, "Enable", InputEnable),
  323. DEFINE_INPUTFUNC( FIELD_VOID, "Disable", InputDisable),
  324. // Function pointers
  325. DEFINE_ENTITYFUNC( BreakTouch ),
  326. // Outputs
  327. DEFINE_OUTPUT( m_OnDamaged, "OnDamaged" ),
  328. DEFINE_OUTPUT( m_OnAwakened, "OnAwakened" ),
  329. DEFINE_OUTPUT( m_OnMotionEnabled, "OnMotionEnabled" ),
  330. DEFINE_OUTPUT( m_OnPhysGunPickup, "OnPhysGunPickup" ),
  331. DEFINE_OUTPUT( m_OnPhysGunPunt, "OnPhysGunPunt" ),
  332. DEFINE_OUTPUT( m_OnPhysGunOnlyPickup, "OnPhysGunOnlyPickup" ),
  333. DEFINE_OUTPUT( m_OnPhysGunDrop, "OnPhysGunDrop" ),
  334. DEFINE_OUTPUT( m_OnPlayerUse, "OnPlayerUse" ),
  335. END_DATADESC()
  336. // UNDONE: Save/Restore needs to take the physics object's properties into account
  337. // UNDONE: Acceleration, velocity, angular velocity, etc. must be preserved
  338. // UNDONE: Many of these quantities are relative to a coordinate frame
  339. // UNDONE: Translate when going across transitions?
  340. // UNDONE: Build transition transformation, and transform data in save/restore for IPhysicsObject
  341. // UNDONE: Angles are saved in the entity, but not propagated back to the IPhysicsObject on restore
  342. //-----------------------------------------------------------------------------
  343. // Purpose:
  344. //-----------------------------------------------------------------------------
  345. void CPhysBox::Spawn( void )
  346. {
  347. // Initialize damage modifiers. Must be done before baseclass spawn.
  348. m_flDmgModBullet = func_breakdmg_bullet.GetFloat();
  349. m_flDmgModClub = func_breakdmg_club.GetFloat();
  350. m_flDmgModExplosive = func_breakdmg_explosive.GetFloat();
  351. m_flDmgModFire = 1.0f;
  352. ParsePropData();
  353. Precache();
  354. m_iMaxHealth = ( m_iHealth > 0 ) ? m_iHealth : 1;
  355. if ( HasSpawnFlags( SF_BREAK_TRIGGER_ONLY ) )
  356. {
  357. m_takedamage = DAMAGE_EVENTS_ONLY;
  358. AddSpawnFlags( SF_BREAK_DONT_TAKE_PHYSICS_DAMAGE );
  359. }
  360. else if ( m_iHealth == 0 )
  361. {
  362. m_takedamage = DAMAGE_EVENTS_ONLY;
  363. AddSpawnFlags( SF_BREAK_DONT_TAKE_PHYSICS_DAMAGE );
  364. }
  365. else
  366. {
  367. m_takedamage = DAMAGE_YES;
  368. }
  369. SetMoveType( MOVETYPE_NONE );
  370. SetAbsVelocity( vec3_origin );
  371. SetModel( STRING( GetModelName() ) );
  372. SetSolid( SOLID_VPHYSICS );
  373. if ( HasSpawnFlags( SF_PHYSBOX_DEBRIS ) )
  374. {
  375. SetCollisionGroup( COLLISION_GROUP_DEBRIS );
  376. }
  377. if ( HasSpawnFlags( SF_PHYSBOX_NO_ROTORWASH_PUSH ) )
  378. {
  379. AddEFlags( EFL_NO_ROTORWASH_PUSH );
  380. }
  381. if ( m_bNotSolidToWorld )
  382. {
  383. AddSolidFlags( FSOLID_NOT_SOLID );
  384. }
  385. CreateVPhysics();
  386. m_hCarryingPlayer = NULL;
  387. SetTouch( &CPhysBox::BreakTouch );
  388. if ( HasSpawnFlags( SF_BREAK_TRIGGER_ONLY ) ) // Only break on trigger
  389. {
  390. SetTouch( NULL );
  391. }
  392. if ( m_impactEnergyScale == 0 )
  393. {
  394. m_impactEnergyScale = 1.0;
  395. }
  396. }
  397. // shared from studiomdl, checks for long, thin objects and adds some damping
  398. // to prevent endless rolling due to low inertia
  399. static bool ShouldDampRotation( const CPhysCollide *pCollide )
  400. {
  401. Vector mins, maxs;
  402. physcollision->CollideGetAABB( &mins, &maxs, pCollide, vec3_origin, vec3_angle );
  403. Vector size = maxs-mins;
  404. int largest = 0;
  405. float largeSize = size[0];
  406. for ( int i = 1; i < 3; i++ )
  407. {
  408. if ( size[i] > largeSize )
  409. {
  410. largeSize = size[i];
  411. largest = i;
  412. }
  413. }
  414. size[largest] = 0;
  415. float len = size.Length();
  416. if ( len > 0 )
  417. {
  418. float sizeRatio = largeSize / len;
  419. // HACKHACK: Hardcoded size ratio to induce damping
  420. // This prevents long skinny objects from rolling endlessly
  421. if ( sizeRatio > 9 )
  422. return true;
  423. }
  424. return false;
  425. }
  426. bool CPhysBox::CreateVPhysics()
  427. {
  428. solid_t tmpSolid;
  429. PhysModelParseSolid( tmpSolid, this, GetModelIndex() );
  430. if ( m_massScale > 0 )
  431. {
  432. tmpSolid.params.mass *= m_massScale;
  433. }
  434. vcollide_t *pVCollide = modelinfo->GetVCollide( GetModelIndex() );
  435. PhysGetMassCenterOverride( this, pVCollide, tmpSolid );
  436. PhysSolidOverride( tmpSolid, m_iszOverrideScript );
  437. if ( tmpSolid.params.rotdamping < 1.0f && ShouldDampRotation(pVCollide->solids[0]) )
  438. {
  439. tmpSolid.params.rotdamping = 1.0f;
  440. }
  441. IPhysicsObject *pPhysics = VPhysicsInitNormal( GetSolid(), GetSolidFlags(), true, &tmpSolid );
  442. if ( m_damageType == 1 )
  443. {
  444. PhysSetGameFlags( pPhysics, FVPHYSICS_DMG_SLICE );
  445. }
  446. // Wake it up if not asleep
  447. if ( !HasSpawnFlags(SF_PHYSBOX_ASLEEP) )
  448. {
  449. pPhysics->Wake();
  450. }
  451. if ( HasSpawnFlags(SF_PHYSBOX_MOTIONDISABLED) || m_damageToEnableMotion > 0 || m_flForceToEnableMotion > 0 )
  452. {
  453. pPhysics->EnableMotion( false );
  454. }
  455. return true;
  456. }
  457. //-----------------------------------------------------------------------------
  458. // Purpose:
  459. //-----------------------------------------------------------------------------
  460. int CPhysBox::ObjectCaps()
  461. {
  462. int caps = BaseClass::ObjectCaps() | FCAP_WCEDIT_POSITION;
  463. if ( HasSpawnFlags( SF_PHYSBOX_ENABLE_PICKUP_OUTPUT ) )
  464. {
  465. caps |= FCAP_IMPULSE_USE;
  466. }
  467. else if ( !HasSpawnFlags( SF_PHYSBOX_IGNOREUSE ) )
  468. {
  469. if ( CBasePlayer::CanPickupObject( this, 35, 128 ) )
  470. {
  471. caps |= FCAP_IMPULSE_USE;
  472. }
  473. }
  474. return caps;
  475. }
  476. //-----------------------------------------------------------------------------
  477. // Purpose:
  478. //-----------------------------------------------------------------------------
  479. void CPhysBox::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
  480. {
  481. CBasePlayer *pPlayer = ToBasePlayer( pActivator );
  482. if ( pPlayer )
  483. {
  484. if ( HasSpawnFlags( SF_PHYSBOX_ENABLE_PICKUP_OUTPUT ) )
  485. {
  486. m_OnPlayerUse.FireOutput( this, this );
  487. }
  488. if ( !HasSpawnFlags( SF_PHYSBOX_IGNOREUSE ) )
  489. {
  490. pPlayer->PickupObject( this );
  491. }
  492. }
  493. }
  494. //-----------------------------------------------------------------------------
  495. //-----------------------------------------------------------------------------
  496. bool CPhysBox::CanBePickedUpByPhyscannon()
  497. {
  498. if ( HasSpawnFlags( SF_PHYSBOX_NEVER_PICK_UP ) )
  499. return false;
  500. IPhysicsObject *pPhysicsObject = VPhysicsGetObject();
  501. if ( !pPhysicsObject )
  502. return false;
  503. if ( !pPhysicsObject->IsMotionEnabled() && !HasSpawnFlags( SF_PHYSBOX_ENABLE_ON_PHYSCANNON ) )
  504. return false;
  505. return true;
  506. }
  507. //-----------------------------------------------------------------------------
  508. // Purpose: Draw any debug text overlays
  509. // Output : Current text offset from the top
  510. //-----------------------------------------------------------------------------
  511. int CPhysBox::DrawDebugTextOverlays(void)
  512. {
  513. int text_offset = BaseClass::DrawDebugTextOverlays();
  514. if (m_debugOverlays & OVERLAY_TEXT_BIT)
  515. {
  516. if (VPhysicsGetObject())
  517. {
  518. char tempstr[512];
  519. Q_snprintf(tempstr, sizeof(tempstr),"Mass: %.2f kg / %.2f lb (%s)", VPhysicsGetObject()->GetMass(), kg2lbs(VPhysicsGetObject()->GetMass()), GetMassEquivalent(VPhysicsGetObject()->GetMass()));
  520. EntityText( text_offset, tempstr, 0);
  521. text_offset++;
  522. }
  523. }
  524. return text_offset;
  525. }
  526. //-----------------------------------------------------------------------------
  527. // Purpose: Input handler that breaks the physics object away from its parent
  528. // and starts it simulating.
  529. //-----------------------------------------------------------------------------
  530. void CPhysBox::InputWake( inputdata_t &inputdata )
  531. {
  532. VPhysicsGetObject()->Wake();
  533. }
  534. //-----------------------------------------------------------------------------
  535. // Purpose: Input handler that breaks the physics object away from its parent
  536. // and stops it simulating.
  537. //-----------------------------------------------------------------------------
  538. void CPhysBox::InputSleep( inputdata_t &inputdata )
  539. {
  540. VPhysicsGetObject()->Sleep();
  541. }
  542. //-----------------------------------------------------------------------------
  543. // Purpose: Show and make solid
  544. //-----------------------------------------------------------------------------
  545. void CPhysBox::InputEnable( inputdata_t &inputdata )
  546. {
  547. RemoveSolidFlags( FSOLID_NOT_SOLID );
  548. RemoveEffects( EF_NODRAW );
  549. }
  550. //-----------------------------------------------------------------------------
  551. // Purpose: Hide and make not solid
  552. //-----------------------------------------------------------------------------
  553. void CPhysBox::InputDisable( inputdata_t &inputdata )
  554. {
  555. AddSolidFlags( FSOLID_NOT_SOLID );
  556. AddEffects( EF_NODRAW );
  557. }
  558. //-----------------------------------------------------------------------------
  559. // Purpose: Enable physics motion and collision response (on by default)
  560. //-----------------------------------------------------------------------------
  561. void CPhysBox::InputEnableMotion( inputdata_t &inputdata )
  562. {
  563. EnableMotion();
  564. }
  565. //-----------------------------------------------------------------------------
  566. // Purpose:
  567. //-----------------------------------------------------------------------------
  568. void CPhysBox::EnableMotion( void )
  569. {
  570. IPhysicsObject *pPhysicsObject = VPhysicsGetObject();
  571. if ( pPhysicsObject != NULL )
  572. {
  573. pPhysicsObject->EnableMotion( true );
  574. pPhysicsObject->Wake();
  575. }
  576. m_damageToEnableMotion = 0;
  577. m_flForceToEnableMotion = 0;
  578. m_OnMotionEnabled.FireOutput( this, this, 0 );
  579. }
  580. //-----------------------------------------------------------------------------
  581. // Purpose: Disable any physics motion or collision response
  582. //-----------------------------------------------------------------------------
  583. void CPhysBox::InputDisableMotion( inputdata_t &inputdata )
  584. {
  585. IPhysicsObject *pPhysicsObject = VPhysicsGetObject();
  586. if ( pPhysicsObject != NULL )
  587. {
  588. pPhysicsObject->EnableMotion( false );
  589. }
  590. }
  591. // Turn off floating simulation (and cost)
  592. void CPhysBox::InputDisableFloating( inputdata_t &inputdata )
  593. {
  594. PhysEnableFloating( VPhysicsGetObject(), false );
  595. }
  596. // Switch our collision to debris
  597. void CPhysBox::InputBecomeDebris( inputdata_t &inputdata )
  598. {
  599. SetCollisionGroup( COLLISION_GROUP_DEBRIS );
  600. }
  601. //-----------------------------------------------------------------------------
  602. // Purpose: If we're being held by the player's hand/physgun, force it to drop us
  603. //-----------------------------------------------------------------------------
  604. void CPhysBox::InputForceDrop( inputdata_t &inputdata )
  605. {
  606. if ( m_hCarryingPlayer )
  607. {
  608. m_hCarryingPlayer->ForceDropOfCarriedPhysObjects();
  609. }
  610. }
  611. //-----------------------------------------------------------------------------
  612. // Purpose:
  613. //-----------------------------------------------------------------------------
  614. void CPhysBox::Move( const Vector &direction )
  615. {
  616. VPhysicsGetObject()->ApplyForceCenter( direction );
  617. }
  618. // Update the visible representation of the physic system's representation of this object
  619. void CPhysBox::VPhysicsUpdate( IPhysicsObject *pPhysics )
  620. {
  621. BaseClass::VPhysicsUpdate( pPhysics );
  622. // if this is the first time we have moved, fire our target
  623. if ( HasSpawnFlags( SF_PHYSBOX_ASLEEP ) )
  624. {
  625. if ( !pPhysics->IsAsleep() )
  626. {
  627. m_OnAwakened.FireOutput(this, this);
  628. FireTargets( STRING(m_target), this, this, USE_TOGGLE, 0 );
  629. RemoveSpawnFlags( SF_PHYSBOX_ASLEEP );
  630. }
  631. }
  632. }
  633. //-----------------------------------------------------------------------------
  634. // Purpose:
  635. //-----------------------------------------------------------------------------
  636. void CPhysBox::OnPhysGunPickup( CBasePlayer *pPhysGunUser, PhysGunPickup_t reason )
  637. {
  638. if ( reason == PUNTED_BY_CANNON )
  639. {
  640. m_OnPhysGunPunt.FireOutput( pPhysGunUser, this );
  641. }
  642. IPhysicsObject *pPhysicsObject = VPhysicsGetObject();
  643. if ( pPhysicsObject && !pPhysicsObject->IsMoveable() )
  644. {
  645. if ( !HasSpawnFlags( SF_PHYSBOX_ENABLE_ON_PHYSCANNON ) )
  646. return;
  647. EnableMotion();
  648. }
  649. m_OnPhysGunPickup.FireOutput( pPhysGunUser, this );
  650. // Are we just being punted?
  651. if ( reason == PUNTED_BY_CANNON )
  652. return;
  653. if( reason == PICKED_UP_BY_CANNON )
  654. {
  655. m_OnPhysGunOnlyPickup.FireOutput( pPhysGunUser, this );
  656. }
  657. m_hCarryingPlayer = pPhysGunUser;
  658. }
  659. //-----------------------------------------------------------------------------
  660. // Purpose:
  661. //-----------------------------------------------------------------------------
  662. void CPhysBox::OnPhysGunDrop( CBasePlayer *pPhysGunUser, PhysGunDrop_t Reason )
  663. {
  664. BaseClass::OnPhysGunDrop( pPhysGunUser, Reason );
  665. m_hCarryingPlayer = NULL;
  666. m_OnPhysGunDrop.FireOutput( pPhysGunUser, this );
  667. }
  668. //-----------------------------------------------------------------------------
  669. // Purpose:
  670. //-----------------------------------------------------------------------------
  671. void CPhysBox::VPhysicsCollision( int index, gamevcollisionevent_t *pEvent )
  672. {
  673. BaseClass::VPhysicsCollision( index, pEvent );
  674. IPhysicsObject *pPhysObj = pEvent->pObjects[!index];
  675. // If we have a force to enable motion, and we're still disabled, check to see if this should enable us
  676. if ( m_flForceToEnableMotion )
  677. {
  678. CBaseEntity *pOther = static_cast<CBaseEntity *>(pPhysObj->GetGameData());
  679. // Don't allow the player to bump an object active if we've requested not to
  680. if ( ( pOther && pOther->IsPlayer() && HasSpawnFlags( SF_PHYSBOX_PREVENT_PLAYER_TOUCH_ENABLE ) ) == false )
  681. {
  682. // Large enough to enable motion?
  683. float flForce = pEvent->collisionSpeed * pEvent->pObjects[!index]->GetMass();
  684. if ( flForce >= m_flForceToEnableMotion )
  685. {
  686. EnableMotion();
  687. }
  688. }
  689. }
  690. }
  691. //-----------------------------------------------------------------------------
  692. // Purpose:
  693. //-----------------------------------------------------------------------------
  694. int CPhysBox::OnTakeDamage( const CTakeDamageInfo &info )
  695. {
  696. if ( IsMarkedForDeletion() )
  697. return 0;
  698. // note: if motion is disabled, OnTakeDamage can't apply physics force
  699. int ret = BaseClass::OnTakeDamage( info );
  700. if ( info.GetInflictor() )
  701. {
  702. m_OnDamaged.FireOutput( info.GetAttacker(), this );
  703. }
  704. // Have we been broken? If so, abort
  705. if ( GetHealth() <= 0 )
  706. return ret;
  707. // If we have a force to enable motion, and we're still disabled, check to see if this should enable us
  708. if ( m_flForceToEnableMotion )
  709. {
  710. // Large enough to enable motion?
  711. float flForce = info.GetDamageForce().Length();
  712. if ( flForce >= m_flForceToEnableMotion )
  713. {
  714. IPhysicsObject *pPhysicsObject = VPhysicsGetObject();
  715. if ( pPhysicsObject )
  716. {
  717. EnableMotion();
  718. }
  719. }
  720. }
  721. // Check our health against the threshold:
  722. if( m_damageToEnableMotion > 0 && GetHealth() < m_damageToEnableMotion )
  723. {
  724. EnableMotion();
  725. VPhysicsTakeDamage( info );
  726. }
  727. return ret;
  728. }
  729. //-----------------------------------------------------------------------------
  730. // Purpose: Return true if this physbox has preferred carry angles
  731. //-----------------------------------------------------------------------------
  732. bool CPhysBox::HasPreferredCarryAnglesForPlayer( CBasePlayer *pPlayer )
  733. {
  734. return HasSpawnFlags( SF_PHYSBOX_USEPREFERRED );
  735. }
  736. // ---------------------------------------------------------------------
  737. //
  738. // CPhysExplosion -- physically simulated explosion
  739. //
  740. // ---------------------------------------------------------------------
  741. LINK_ENTITY_TO_CLASS( env_physexplosion, CPhysExplosion );
  742. BEGIN_DATADESC( CPhysExplosion )
  743. DEFINE_KEYFIELD( m_damage, FIELD_FLOAT, "magnitude" ),
  744. DEFINE_KEYFIELD( m_radius, FIELD_FLOAT, "radius" ),
  745. DEFINE_KEYFIELD( m_targetEntityName, FIELD_STRING, "targetentityname" ),
  746. DEFINE_KEYFIELD( m_flInnerRadius, FIELD_FLOAT, "inner_radius" ),
  747. // Inputs
  748. DEFINE_INPUTFUNC( FIELD_VOID, "Explode", InputExplode ),
  749. DEFINE_INPUTFUNC( FIELD_VOID, "ExplodeAndRemove", InputExplodeAndRemove ),
  750. // Outputs
  751. DEFINE_OUTPUT( m_OnPushedPlayer, "OnPushedPlayer" ),
  752. END_DATADESC()
  753. void CPhysExplosion::Spawn( void )
  754. {
  755. SetMoveType( MOVETYPE_NONE );
  756. SetSolid( SOLID_NONE );
  757. SetModelName( NULL_STRING );
  758. }
  759. float CPhysExplosion::GetRadius( void )
  760. {
  761. float radius = m_radius;
  762. if ( radius <= 0 )
  763. {
  764. // Use the same radius as combat
  765. radius = m_damage * 2.5;
  766. }
  767. return radius;
  768. }
  769. CBaseEntity *CPhysExplosion::FindEntity( CBaseEntity *pEntity, CBaseEntity *pActivator, CBaseEntity *pCaller )
  770. {
  771. // Filter by name or classname
  772. if ( m_targetEntityName != NULL_STRING )
  773. {
  774. // Try an explicit name first
  775. CBaseEntity *pTarget = gEntList.FindEntityByName( pEntity, m_targetEntityName, NULL, pActivator, pCaller );
  776. if ( pTarget != NULL )
  777. return pTarget;
  778. // Failing that, try a classname
  779. return gEntList.FindEntityByClassnameWithin( pEntity, STRING(m_targetEntityName), GetAbsOrigin(), GetRadius() );
  780. }
  781. // Just find anything in the radius
  782. return gEntList.FindEntityInSphere( pEntity, GetAbsOrigin(), GetRadius() );
  783. }
  784. //-----------------------------------------------------------------------------
  785. // Purpose:
  786. //-----------------------------------------------------------------------------
  787. void CPhysExplosion::InputExplode( inputdata_t &inputdata )
  788. {
  789. Explode( inputdata.pActivator, inputdata.pCaller );
  790. }
  791. void CPhysExplosion::InputExplodeAndRemove( inputdata_t &inputdata )
  792. {
  793. ExplodeAndRemove( inputdata.pActivator, inputdata.pCaller );
  794. }
  795. //-----------------------------------------------------------------------------
  796. // Purpose:
  797. //-----------------------------------------------------------------------------
  798. void CPhysExplosion::Explode( CBaseEntity *pActivator, CBaseEntity *pCaller )
  799. {
  800. CBaseEntity *pEntity = NULL;
  801. float adjustedDamage, falloff, flDist;
  802. Vector vecSpot, vecOrigin;
  803. falloff = 1.0 / 2.5;
  804. // iterate on all entities in the vicinity.
  805. // I've removed the traceline heuristic from phys explosions. SO right now they will
  806. // affect entities through walls. (sjb)
  807. // UNDONE: Try tracing world-only?
  808. while ((pEntity = FindEntity( pEntity, pActivator, pCaller )) != NULL)
  809. {
  810. // UNDONE: Ask the object if it should get force if it's not MOVETYPE_VPHYSICS?
  811. if ( pEntity->m_takedamage != DAMAGE_NO && (pEntity->GetMoveType() == MOVETYPE_VPHYSICS || (pEntity->VPhysicsGetObject() /*&& !pEntity->IsPlayer()*/)) )
  812. {
  813. vecOrigin = GetAbsOrigin();
  814. vecSpot = pEntity->BodyTarget( vecOrigin );
  815. // Squash this down to a circle
  816. if ( HasSpawnFlags( SF_PHYSEXPLOSION_RADIAL ) )
  817. {
  818. vecOrigin[2] = vecSpot[2];
  819. }
  820. // decrease damage for an ent that's farther from the bomb.
  821. flDist = ( vecOrigin - vecSpot ).Length();
  822. if( m_radius == 0 || flDist <= m_radius )
  823. {
  824. if ( HasSpawnFlags( SF_PHYSEXPLOSION_TEST_LOS ) )
  825. {
  826. Vector vecStartPos = GetAbsOrigin();
  827. Vector vecEndPos = pEntity->BodyTarget( vecStartPos, false );
  828. if ( m_flInnerRadius != 0.0f )
  829. {
  830. // Find a point on our inner radius sphere to begin from
  831. Vector vecDirToTarget = ( vecEndPos - vecStartPos );
  832. VectorNormalize( vecDirToTarget );
  833. vecStartPos = GetAbsOrigin() + ( vecDirToTarget * m_flInnerRadius );
  834. }
  835. trace_t tr;
  836. UTIL_TraceLine( vecStartPos,
  837. pEntity->BodyTarget( vecStartPos, false ),
  838. MASK_SOLID_BRUSHONLY,
  839. this,
  840. COLLISION_GROUP_NONE,
  841. &tr );
  842. // Shielded
  843. if ( tr.fraction < 1.0f && tr.m_pEnt != pEntity )
  844. continue;
  845. }
  846. adjustedDamage = flDist * falloff;
  847. adjustedDamage = m_damage - adjustedDamage;
  848. if ( adjustedDamage < 1 )
  849. {
  850. adjustedDamage = 1;
  851. }
  852. CTakeDamageInfo info( this, this, adjustedDamage, DMG_BLAST );
  853. CalculateExplosiveDamageForce( &info, (vecSpot - vecOrigin), vecOrigin );
  854. if ( HasSpawnFlags( SF_PHYSEXPLOSION_PUSH_PLAYER ) )
  855. {
  856. if ( pEntity->IsPlayer() )
  857. {
  858. Vector vecPushDir = ( pEntity->BodyTarget( GetAbsOrigin(), false ) - GetAbsOrigin() );
  859. float dist = VectorNormalize( vecPushDir );
  860. float flFalloff = RemapValClamped( dist, m_radius, m_radius*0.75f, 0.0f, 1.0f );
  861. if ( HasSpawnFlags( SF_PHYSEXPLOSION_DISORIENT_PLAYER ) )
  862. {
  863. //Disorient the player
  864. QAngle vecDeltaAngles;
  865. vecDeltaAngles.x = random->RandomInt( -30, 30 );
  866. vecDeltaAngles.y = random->RandomInt( -30, 30 );
  867. vecDeltaAngles.z = 0.0f;
  868. CBasePlayer *pPlayer = ToBasePlayer( pEntity );
  869. pPlayer->SnapEyeAngles( GetLocalAngles() + vecDeltaAngles );
  870. pEntity->ViewPunch( vecDeltaAngles );
  871. }
  872. Vector vecPush = (vecPushDir*m_damage*flFalloff*2.0f);
  873. if ( pEntity->GetFlags() & FL_BASEVELOCITY )
  874. {
  875. vecPush = vecPush + pEntity->GetBaseVelocity();
  876. }
  877. if ( vecPush.z > 0 && (pEntity->GetFlags() & FL_ONGROUND) )
  878. {
  879. pEntity->SetGroundEntity( NULL );
  880. Vector origin = pEntity->GetAbsOrigin();
  881. origin.z += 1.0f;
  882. pEntity->SetAbsOrigin( origin );
  883. }
  884. pEntity->SetBaseVelocity( vecPush );
  885. pEntity->AddFlag( FL_BASEVELOCITY );
  886. // Fire an output that the player has been pushed
  887. m_OnPushedPlayer.FireOutput( this, this );
  888. continue;
  889. }
  890. }
  891. if ( HasSpawnFlags( SF_PHYSEXPLOSION_NODAMAGE ) )
  892. {
  893. pEntity->VPhysicsTakeDamage( info );
  894. }
  895. else
  896. {
  897. pEntity->TakeDamage( info );
  898. }
  899. }
  900. }
  901. }
  902. }
  903. void CPhysExplosion::ExplodeAndRemove( CBaseEntity *pActivator, CBaseEntity *pCaller )
  904. {
  905. Explode( pActivator, pCaller );
  906. UTIL_Remove( this );
  907. }
  908. //-----------------------------------------------------------------------------
  909. // Purpose: Draw any debug text overlays
  910. // Output : Current text offset from the top
  911. //-----------------------------------------------------------------------------
  912. int CPhysExplosion::DrawDebugTextOverlays( void )
  913. {
  914. int text_offset = BaseClass::DrawDebugTextOverlays();
  915. if (m_debugOverlays & OVERLAY_TEXT_BIT)
  916. {
  917. char tempstr[512];
  918. // print magnitude
  919. Q_snprintf(tempstr,sizeof(tempstr)," magnitude: %f", m_damage);
  920. EntityText(text_offset,tempstr,0);
  921. text_offset++;
  922. // print target entity
  923. Q_snprintf(tempstr,sizeof(tempstr)," limit to: %s", STRING( m_targetEntityName ) );
  924. EntityText(text_offset,tempstr,0);
  925. text_offset++;
  926. }
  927. return text_offset;
  928. }
  929. //-----------------------------------------------------------------------------
  930. // Purpose: Create a CPhysExplosion entity on the fly and call the explode method.
  931. //-----------------------------------------------------------------------------
  932. void CreatePhysExplosion( Vector origin, float magnitude, float radius, string_t target, float innerRadius, int flags )
  933. {
  934. CPhysExplosion *pExplosion = (CPhysExplosion*)CBaseEntity::Create( "env_physexplosion", origin, vec3_angle, NULL );
  935. pExplosion->m_damage = magnitude;
  936. pExplosion->m_radius = radius;
  937. pExplosion->m_flInnerRadius = innerRadius;
  938. pExplosion->m_targetEntityName = target;
  939. pExplosion->AddSpawnFlags( flags );
  940. variant_t emptyVariant;
  941. pExplosion->Spawn();
  942. pExplosion->AcceptInput( "ExplodeAndRemove", NULL, NULL, emptyVariant, 0 );
  943. }
  944. //==================================================
  945. // CPhysImpact
  946. //==================================================
  947. #define bitsPHYSIMPACT_NOFALLOFF 0x00000001
  948. #define bitsPHYSIMPACT_INFINITE_LENGTH 0x00000002
  949. #define bitsPHYSIMPACT_IGNORE_MASS 0x00000004
  950. #define bitsPHYSIMPACT_IGNORE_NORMAL 0x00000008
  951. #define DEFAULT_EXPLODE_DISTANCE 256
  952. LINK_ENTITY_TO_CLASS( env_physimpact, CPhysImpact );
  953. BEGIN_DATADESC( CPhysImpact )
  954. DEFINE_KEYFIELD( m_damage, FIELD_FLOAT, "magnitude" ),
  955. DEFINE_KEYFIELD( m_distance, FIELD_FLOAT, "distance" ),
  956. DEFINE_KEYFIELD( m_directionEntityName,FIELD_STRING, "directionentityname" ),
  957. // Function pointers
  958. DEFINE_FUNCTION( PointAtEntity ),
  959. DEFINE_INPUTFUNC( FIELD_VOID, "Impact", InputImpact ),
  960. END_DATADESC()
  961. //-----------------------------------------------------------------------------
  962. // Purpose:
  963. //-----------------------------------------------------------------------------
  964. void CPhysImpact::Activate( void )
  965. {
  966. BaseClass::Activate();
  967. }
  968. //-----------------------------------------------------------------------------
  969. // Purpose:
  970. //-----------------------------------------------------------------------------
  971. void CPhysImpact::Spawn( void )
  972. {
  973. SetMoveType( MOVETYPE_NONE );
  974. SetSolid( SOLID_NONE );
  975. SetModelName( NULL_STRING );
  976. //If not targetted, and no distance is set, give it a default value
  977. if ( m_distance == 0 )
  978. {
  979. m_distance = DEFAULT_EXPLODE_DISTANCE;
  980. }
  981. }
  982. //-----------------------------------------------------------------------------
  983. // Purpose:
  984. //-----------------------------------------------------------------------------
  985. void CPhysImpact::PointAtEntity( void )
  986. {
  987. //If we're not targetted at anything, don't bother
  988. if ( m_directionEntityName == NULL_STRING )
  989. return;
  990. UTIL_PointAtNamedEntity( this, m_directionEntityName );
  991. }
  992. //-----------------------------------------------------------------------------
  993. // Purpose:
  994. // Input : *pActivator -
  995. // *pCaller -
  996. // useType -
  997. // value -
  998. //-----------------------------------------------------------------------------
  999. void CPhysImpact::InputImpact( inputdata_t &inputdata )
  1000. {
  1001. Vector dir;
  1002. trace_t trace;
  1003. //If we have a direction target, setup to point at it
  1004. if ( m_directionEntityName != NULL_STRING )
  1005. {
  1006. PointAtEntity();
  1007. }
  1008. AngleVectors( GetAbsAngles(), &dir );
  1009. //Setup our trace information
  1010. float dist = HasSpawnFlags( bitsPHYSIMPACT_INFINITE_LENGTH ) ? MAX_TRACE_LENGTH : m_distance;
  1011. Vector start = GetAbsOrigin();
  1012. Vector end = start + ( dir * dist );
  1013. //Trace out
  1014. UTIL_TraceLine( start, end, MASK_SHOT, this, 0, &trace );
  1015. if( debug_physimpact.GetBool() )
  1016. {
  1017. NDebugOverlay::Cross3D( start, 24, 255, 255, 255, false, 30 );
  1018. NDebugOverlay::Line( trace.startpos, trace.endpos, 0, 255, 0, false, 30 );
  1019. }
  1020. if ( trace.fraction != 1.0 )
  1021. {
  1022. // if inside the object, just go opposite the direction
  1023. if ( trace.startsolid )
  1024. {
  1025. trace.plane.normal = -dir;
  1026. }
  1027. CBaseEntity *pEnt = trace.m_pEnt;
  1028. IPhysicsObject *pPhysics = pEnt->VPhysicsGetObject();
  1029. //If the entity is valid, hit it
  1030. if ( ( pEnt != NULL ) && ( pPhysics != NULL ) )
  1031. {
  1032. CTakeDamageInfo info;
  1033. info.SetAttacker( this);
  1034. info.SetInflictor( this );
  1035. info.SetDamage( 0 );
  1036. info.SetDamageForce( vec3_origin );
  1037. info.SetDamageType( DMG_GENERIC );
  1038. pEnt->DispatchTraceAttack( info, dir, &trace );
  1039. ApplyMultiDamage();
  1040. //Damage falls off unless specified or the ray's length is infinite
  1041. float damage = HasSpawnFlags( bitsPHYSIMPACT_NOFALLOFF | bitsPHYSIMPACT_INFINITE_LENGTH ) ?
  1042. m_damage : (m_damage * (1.0f-trace.fraction));
  1043. if ( HasSpawnFlags( bitsPHYSIMPACT_IGNORE_MASS ) )
  1044. {
  1045. damage *= pPhysics->GetMass();
  1046. }
  1047. if( debug_physimpact.GetBool() )
  1048. {
  1049. NDebugOverlay::Line( trace.endpos, trace.endpos + trace.plane.normal * -128, 255, 0, 0, false, 30 );
  1050. }
  1051. // Legacy entities applied the force along the impact normal, which yielded unpredictable results.
  1052. if ( !HasSpawnFlags( bitsPHYSIMPACT_IGNORE_NORMAL ) )
  1053. {
  1054. dir = -trace.plane.normal;
  1055. }
  1056. pPhysics->ApplyForceOffset( damage * dir * phys_pushscale.GetFloat(), trace.endpos );
  1057. }
  1058. }
  1059. }
  1060. class CSimplePhysicsBrush : public CBaseEntity
  1061. {
  1062. DECLARE_CLASS( CSimplePhysicsBrush, CBaseEntity );
  1063. public:
  1064. void Spawn()
  1065. {
  1066. SetModel( STRING( GetModelName() ) );
  1067. SetMoveType( MOVETYPE_VPHYSICS );
  1068. SetSolid( SOLID_VPHYSICS );
  1069. m_takedamage = DAMAGE_EVENTS_ONLY;
  1070. }
  1071. };
  1072. LINK_ENTITY_TO_CLASS( simple_physics_brush, CSimplePhysicsBrush );
  1073. class CSimplePhysicsProp : public CBaseProp
  1074. {
  1075. DECLARE_CLASS( CSimplePhysicsProp, CBaseProp );
  1076. public:
  1077. void Spawn()
  1078. {
  1079. BaseClass::Spawn();
  1080. SetMoveType( MOVETYPE_VPHYSICS );
  1081. SetSolid( SOLID_VPHYSICS );
  1082. m_takedamage = DAMAGE_EVENTS_ONLY;
  1083. }
  1084. int ObjectCaps()
  1085. {
  1086. int caps = BaseClass::ObjectCaps() | FCAP_WCEDIT_POSITION;
  1087. if ( CBasePlayer::CanPickupObject( this, 35, 128 ) )
  1088. {
  1089. caps |= FCAP_IMPULSE_USE;
  1090. }
  1091. return caps;
  1092. }
  1093. void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
  1094. {
  1095. CBasePlayer *pPlayer = ToBasePlayer( pActivator );
  1096. if ( pPlayer )
  1097. {
  1098. pPlayer->PickupObject( this );
  1099. }
  1100. }
  1101. };
  1102. LINK_ENTITY_TO_CLASS( simple_physics_prop, CSimplePhysicsProp );
  1103. // UNDONE: Is this worth it?, just recreate the object instead? (that happens when this returns false anyway)
  1104. // recreating works, but is more expensive and won't inherit properties (velocity, constraints, etc)
  1105. bool TransferPhysicsObject( CBaseEntity *pFrom, CBaseEntity *pTo, bool wakeUp )
  1106. {
  1107. IPhysicsObject *pVPhysics = pFrom->VPhysicsGetObject();
  1108. if ( !pVPhysics || pVPhysics->IsStatic() )
  1109. return false;
  1110. // clear out the pointer so it won't get deleted
  1111. pFrom->VPhysicsSwapObject( NULL );
  1112. // remove any AI behavior bound to it
  1113. pVPhysics->RemoveShadowController();
  1114. // transfer to the new owner
  1115. pTo->VPhysicsSetObject( pVPhysics );
  1116. pVPhysics->SetGameData( (void *)pTo );
  1117. pTo->VPhysicsUpdate( pVPhysics );
  1118. // may have been temporarily disabled by the old object
  1119. pVPhysics->EnableMotion( true );
  1120. pVPhysics->EnableGravity( true );
  1121. // Update for the new entity solid type
  1122. pVPhysics->RecheckCollisionFilter();
  1123. if ( wakeUp )
  1124. {
  1125. pVPhysics->Wake();
  1126. }
  1127. return true;
  1128. }
  1129. // UNDONE: Move/rename this function
  1130. static CBaseEntity *CreateSimplePhysicsObject( CBaseEntity *pEntity, bool createAsleep, bool createAsDebris )
  1131. {
  1132. CBaseEntity *pPhysEntity = NULL;
  1133. int modelindex = pEntity->GetModelIndex();
  1134. const model_t *model = modelinfo->GetModel( modelindex );
  1135. if ( model && modelinfo->GetModelType(model) == mod_brush )
  1136. {
  1137. pPhysEntity = CreateEntityByName( "simple_physics_brush" );
  1138. }
  1139. else
  1140. {
  1141. pPhysEntity = CreateEntityByName( "simple_physics_prop" );
  1142. }
  1143. pPhysEntity->KeyValue( "model", STRING(pEntity->GetModelName()) );
  1144. pPhysEntity->SetAbsOrigin( pEntity->GetAbsOrigin() );
  1145. pPhysEntity->SetAbsAngles( pEntity->GetAbsAngles() );
  1146. pPhysEntity->Spawn();
  1147. if ( !TransferPhysicsObject( pEntity, pPhysEntity, !createAsleep ) )
  1148. {
  1149. pPhysEntity->VPhysicsInitNormal( SOLID_VPHYSICS, 0, createAsleep );
  1150. if ( createAsDebris )
  1151. pPhysEntity->SetCollisionGroup( COLLISION_GROUP_DEBRIS );
  1152. }
  1153. return pPhysEntity;
  1154. }
  1155. #define SF_CONVERT_ASLEEP 0x0001
  1156. #define SF_CONVERT_AS_DEBRIS 0x0002
  1157. class CPhysConvert : public CLogicalEntity
  1158. {
  1159. DECLARE_CLASS( CPhysConvert, CLogicalEntity );
  1160. public:
  1161. CPhysConvert( void ) : m_flMassOverride( 0.0f ) {};
  1162. COutputEvent m_OnConvert;
  1163. // Input handlers
  1164. void InputConvertTarget( inputdata_t &inputdata );
  1165. DECLARE_DATADESC();
  1166. private:
  1167. string_t m_swapModel;
  1168. float m_flMassOverride;
  1169. };
  1170. LINK_ENTITY_TO_CLASS( phys_convert, CPhysConvert );
  1171. BEGIN_DATADESC( CPhysConvert )
  1172. DEFINE_KEYFIELD( m_swapModel, FIELD_STRING, "swapmodel" ),
  1173. DEFINE_KEYFIELD( m_flMassOverride, FIELD_FLOAT, "massoverride" ),
  1174. // Inputs
  1175. DEFINE_INPUTFUNC( FIELD_VOID, "ConvertTarget", InputConvertTarget ),
  1176. // Outputs
  1177. DEFINE_OUTPUT( m_OnConvert, "OnConvert"),
  1178. END_DATADESC()
  1179. //-----------------------------------------------------------------------------
  1180. // Purpose: Input handler that converts our target to a physics object.
  1181. //-----------------------------------------------------------------------------
  1182. void CPhysConvert::InputConvertTarget( inputdata_t &inputdata )
  1183. {
  1184. bool createAsleep = HasSpawnFlags(SF_CONVERT_ASLEEP);
  1185. bool createAsDebris = HasSpawnFlags(SF_CONVERT_AS_DEBRIS);
  1186. // Fire output
  1187. m_OnConvert.FireOutput( inputdata.pActivator, this );
  1188. CBaseEntity *entlist[512];
  1189. CBaseEntity *pSwap = gEntList.FindEntityByName( NULL, m_swapModel, NULL, inputdata.pActivator, inputdata.pCaller );
  1190. CBaseEntity *pEntity = NULL;
  1191. int count = 0;
  1192. while ( (pEntity = gEntList.FindEntityByName( pEntity, m_target, NULL, inputdata.pActivator, inputdata.pCaller )) != NULL )
  1193. {
  1194. entlist[count++] = pEntity;
  1195. if ( count >= ARRAYSIZE(entlist) )
  1196. break;
  1197. }
  1198. // if we're swapping to model out, don't loop over more than one object
  1199. // multiple objects with the same brush model will render, but the dynamic lights
  1200. // and decals will be shared between the two instances...
  1201. if ( pSwap && count > 0 )
  1202. {
  1203. count = 1;
  1204. }
  1205. for ( int i = 0; i < count; i++ )
  1206. {
  1207. pEntity = entlist[i];
  1208. // don't convert something that is already physics based
  1209. if ( pEntity->GetMoveType() == MOVETYPE_VPHYSICS )
  1210. {
  1211. Msg( "ERROR phys_convert %s ! Already MOVETYPE_VPHYSICS\n", STRING(pEntity->m_iClassname) );
  1212. continue;
  1213. }
  1214. UnlinkFromParent( pEntity );
  1215. if ( pSwap )
  1216. {
  1217. // we can't reuse this physics object, so kill it
  1218. pEntity->VPhysicsDestroyObject();
  1219. pEntity->SetModel( STRING(pSwap->GetModelName()) );
  1220. }
  1221. // created phys object, now move hierarchy over
  1222. CBaseEntity *pPhys = CreateSimplePhysicsObject( pEntity, createAsleep, createAsDebris );
  1223. if ( pPhys )
  1224. {
  1225. // Override the mass if specified
  1226. if ( m_flMassOverride > 0 )
  1227. {
  1228. IPhysicsObject *pPhysObj = pPhys->VPhysicsGetObject();
  1229. if ( pPhysObj )
  1230. {
  1231. pPhysObj->SetMass( m_flMassOverride );
  1232. }
  1233. }
  1234. pPhys->SetName( pEntity->GetEntityName() );
  1235. UTIL_TransferPoseParameters( pEntity, pPhys );
  1236. TransferChildren( pEntity, pPhys );
  1237. pEntity->AddSolidFlags( FSOLID_NOT_SOLID );
  1238. pEntity->AddEffects( EF_NODRAW );
  1239. UTIL_Remove( pEntity );
  1240. }
  1241. }
  1242. }
  1243. //============================================================================================================
  1244. // PHYS MAGNET
  1245. //============================================================================================================
  1246. #define SF_MAGNET_ASLEEP 0x0001
  1247. #define SF_MAGNET_MOTIONDISABLED 0x0002
  1248. #define SF_MAGNET_SUCK 0x0004
  1249. #define SF_MAGNET_ALLOWROTATION 0x0008
  1250. #define SF_MAGNET_COAST_HACK 0x0010
  1251. LINK_ENTITY_TO_CLASS( phys_magnet, CPhysMagnet );
  1252. // BUGBUG: This won't work! Right now you can't save physics pointers inside an embedded type!
  1253. BEGIN_SIMPLE_DATADESC( magnetted_objects_t )
  1254. DEFINE_PHYSPTR( pConstraint ),
  1255. DEFINE_FIELD( hEntity, FIELD_EHANDLE ),
  1256. END_DATADESC()
  1257. BEGIN_DATADESC( CPhysMagnet )
  1258. // Outputs
  1259. DEFINE_OUTPUT( m_OnMagnetAttach, "OnAttach" ),
  1260. DEFINE_OUTPUT( m_OnMagnetDetach, "OnDetach" ),
  1261. // Keys
  1262. DEFINE_KEYFIELD( m_massScale, FIELD_FLOAT, "massScale" ),
  1263. DEFINE_KEYFIELD( m_iszOverrideScript, FIELD_STRING, "overridescript" ),
  1264. DEFINE_KEYFIELD( m_iMaxObjectsAttached, FIELD_INTEGER, "maxobjects" ),
  1265. DEFINE_KEYFIELD( m_forceLimit, FIELD_FLOAT, "forcelimit" ),
  1266. DEFINE_KEYFIELD( m_torqueLimit, FIELD_FLOAT, "torquelimit" ),
  1267. DEFINE_UTLVECTOR( m_MagnettedEntities, FIELD_EMBEDDED ),
  1268. DEFINE_PHYSPTR( m_pConstraintGroup ),
  1269. DEFINE_FIELD( m_bActive, FIELD_BOOLEAN ),
  1270. DEFINE_FIELD( m_bHasHitSomething, FIELD_BOOLEAN ),
  1271. DEFINE_FIELD( m_flTotalMass, FIELD_FLOAT ),
  1272. DEFINE_FIELD( m_flRadius, FIELD_FLOAT ),
  1273. DEFINE_FIELD( m_flNextSuckTime, FIELD_FLOAT ),
  1274. // Inputs
  1275. DEFINE_INPUTFUNC( FIELD_VOID, "Toggle", InputToggle ),
  1276. DEFINE_INPUTFUNC( FIELD_VOID, "TurnOn", InputTurnOn ),
  1277. DEFINE_INPUTFUNC( FIELD_VOID, "TurnOff", InputTurnOff ),
  1278. END_DATADESC()
  1279. //-----------------------------------------------------------------------------
  1280. // Purpose: SendProxy that converts the magnet's attached object UtlVector to entindexes
  1281. //-----------------------------------------------------------------------------
  1282. void SendProxy_MagnetAttachedObjectList( const void *pStruct, const void *pData, DVariant *pOut, int iElement, int objectID )
  1283. {
  1284. CPhysMagnet *pMagnet = (CPhysMagnet*)pData;
  1285. // If this assertion fails, then SendProxyArrayLength_MagnetAttachedArray must have failed.
  1286. Assert( iElement < pMagnet->GetNumAttachedObjects() );
  1287. pOut->m_Int = pMagnet->GetAttachedObject(iElement)->entindex();
  1288. }
  1289. int SendProxyArrayLength_MagnetAttachedArray( const void *pStruct, int objectID )
  1290. {
  1291. CPhysMagnet *pMagnet = (CPhysMagnet*)pStruct;
  1292. return pMagnet->GetNumAttachedObjects();
  1293. }
  1294. IMPLEMENT_SERVERCLASS_ST( CPhysMagnet, DT_PhysMagnet )
  1295. // ROBIN: Disabled because we don't need it anymore
  1296. /*
  1297. SendPropArray2(
  1298. SendProxyArrayLength_MagnetAttachedArray,
  1299. SendPropInt("magnetattached_array_element", 0, 4, 10, SPROP_UNSIGNED, SendProxy_MagnetAttachedObjectList),
  1300. 128,
  1301. 0,
  1302. "magnetattached_array"
  1303. )
  1304. */
  1305. END_SEND_TABLE()
  1306. //-----------------------------------------------------------------------------
  1307. // Purpose:
  1308. //-----------------------------------------------------------------------------
  1309. CPhysMagnet::CPhysMagnet( void )
  1310. {
  1311. m_forceLimit = 0;
  1312. m_torqueLimit = 0;
  1313. }
  1314. //-----------------------------------------------------------------------------
  1315. // Purpose:
  1316. //-----------------------------------------------------------------------------
  1317. CPhysMagnet::~CPhysMagnet( void )
  1318. {
  1319. DetachAll();
  1320. }
  1321. //-----------------------------------------------------------------------------
  1322. // Purpose:
  1323. //-----------------------------------------------------------------------------
  1324. void CPhysMagnet::Spawn( void )
  1325. {
  1326. Precache();
  1327. SetMoveType( MOVETYPE_NONE );
  1328. SetSolid( SOLID_VPHYSICS );
  1329. SetModel( STRING( GetModelName() ) );
  1330. m_takedamage = DAMAGE_EVENTS_ONLY;
  1331. solid_t tmpSolid;
  1332. PhysModelParseSolid( tmpSolid, this, GetModelIndex() );
  1333. if ( m_massScale > 0 )
  1334. {
  1335. tmpSolid.params.mass *= m_massScale;
  1336. }
  1337. PhysSolidOverride( tmpSolid, m_iszOverrideScript );
  1338. VPhysicsInitNormal( GetSolid(), GetSolidFlags(), true, &tmpSolid );
  1339. // Wake it up if not asleep
  1340. if ( !HasSpawnFlags(SF_MAGNET_ASLEEP) )
  1341. {
  1342. VPhysicsGetObject()->Wake();
  1343. }
  1344. if ( HasSpawnFlags(SF_MAGNET_MOTIONDISABLED) )
  1345. {
  1346. VPhysicsGetObject()->EnableMotion( false );
  1347. }
  1348. m_bActive = true;
  1349. m_pConstraintGroup = NULL;
  1350. m_flTotalMass = 0;
  1351. m_flNextSuckTime = 0;
  1352. BaseClass::Spawn();
  1353. }
  1354. //-----------------------------------------------------------------------------
  1355. // Purpose:
  1356. //-----------------------------------------------------------------------------
  1357. void CPhysMagnet::Precache( void )
  1358. {
  1359. PrecacheModel( STRING( GetModelName() ) );
  1360. BaseClass::Precache();
  1361. }
  1362. //-----------------------------------------------------------------------------
  1363. // Purpose:
  1364. //-----------------------------------------------------------------------------
  1365. void CPhysMagnet::Touch( CBaseEntity *pOther )
  1366. {
  1367. }
  1368. //-----------------------------------------------------------------------------
  1369. // Purpose:
  1370. //-----------------------------------------------------------------------------
  1371. void CPhysMagnet::VPhysicsCollision( int index, gamevcollisionevent_t *pEvent )
  1372. {
  1373. int otherIndex = !index;
  1374. CBaseEntity *pOther = pEvent->pEntities[otherIndex];
  1375. // Ignore triggers
  1376. if ( pOther->IsSolidFlagSet( FSOLID_NOT_SOLID ) )
  1377. return;
  1378. m_bHasHitSomething = true;
  1379. DoMagnetSuck( pEvent->pEntities[!index] );
  1380. // Don't pickup if we're not active
  1381. if ( !m_bActive )
  1382. return;
  1383. // Hit our maximum?
  1384. if ( m_iMaxObjectsAttached && m_iMaxObjectsAttached <= GetNumAttachedObjects() )
  1385. return;
  1386. // This is a hack to solve players (Erik) stacking stuff on their jeeps in coast_01
  1387. // and being screwed when the crane can't pick them up. We need to get rid of the object.
  1388. if ( HasSpawnFlags( SF_MAGNET_COAST_HACK ) )
  1389. {
  1390. // If the other isn't the jeep, we need to get rid of it
  1391. if ( !FClassnameIs( pOther, "prop_vehicle_jeep" ) )
  1392. {
  1393. // If it takes damage, destroy it
  1394. if ( pOther->m_takedamage != DAMAGE_NO && pOther->m_takedamage != DAMAGE_EVENTS_ONLY )
  1395. {
  1396. CTakeDamageInfo info( this, this, pOther->GetHealth(), DMG_GENERIC | DMG_PREVENT_PHYSICS_FORCE );
  1397. pOther->TakeDamage( info );
  1398. }
  1399. else if ( pEvent->pObjects[ otherIndex ]->IsMoveable() )
  1400. {
  1401. // Otherwise, we're screwed, so just remove it
  1402. UTIL_Remove( pOther );
  1403. }
  1404. else
  1405. {
  1406. Warning( "CPhysMagnet %s:%d blocking magnet\n",
  1407. pOther->GetClassname(), pOther->entindex() );
  1408. }
  1409. return;
  1410. }
  1411. }
  1412. // Make sure it's made of metal
  1413. const surfacedata_t *phit = physprops->GetSurfaceData( pEvent->surfaceProps[otherIndex] );
  1414. char cTexType = phit->game.material;
  1415. if ( cTexType != CHAR_TEX_METAL && cTexType != CHAR_TEX_COMPUTER )
  1416. {
  1417. // If we don't have a model, we're done. The texture we hit wasn't metal.
  1418. if ( !pOther->GetBaseAnimating() )
  1419. return;
  1420. // If we have a model that wants to be metal, even though we hit a non-metal texture, we'll stick to it
  1421. if ( !StringHasPrefixCaseSensitive( Studio_GetDefaultSurfaceProps( pOther->GetBaseAnimating()->GetModelPtr() ), "metal" ) )
  1422. return;
  1423. }
  1424. IPhysicsObject *pPhysics = pOther->VPhysicsGetObject();
  1425. if ( pPhysics && pOther->GetMoveType() == MOVETYPE_VPHYSICS && pPhysics->IsMoveable() )
  1426. {
  1427. // Make sure we haven't already got this sucker on the magnet
  1428. int iCount = m_MagnettedEntities.Count();
  1429. for ( int i = 0; i < iCount; i++ )
  1430. {
  1431. if ( m_MagnettedEntities[i].hEntity == pOther )
  1432. return;
  1433. }
  1434. // We want to cast a long way to ensure our shadow shows up
  1435. pOther->SetShadowCastDistance( 2048 );
  1436. // Create a constraint between the magnet and this sucker
  1437. IPhysicsObject *pMagnetPhysObject = VPhysicsGetObject();
  1438. Assert( pMagnetPhysObject );
  1439. magnetted_objects_t newEntityOnMagnet;
  1440. newEntityOnMagnet.hEntity = pOther;
  1441. // Use the right constraint
  1442. if ( HasSpawnFlags( SF_MAGNET_ALLOWROTATION ) )
  1443. {
  1444. constraint_ballsocketparams_t ballsocket;
  1445. ballsocket.Defaults();
  1446. ballsocket.constraint.Defaults();
  1447. ballsocket.constraint.forceLimit = lbs2kg(m_forceLimit);
  1448. ballsocket.constraint.torqueLimit = lbs2kg(m_torqueLimit);
  1449. Vector vecCollisionPoint;
  1450. pEvent->pInternalData->GetContactPoint( vecCollisionPoint );
  1451. pMagnetPhysObject->WorldToLocal( &ballsocket.constraintPosition[0], vecCollisionPoint );
  1452. pPhysics->WorldToLocal( &ballsocket.constraintPosition[1], vecCollisionPoint );
  1453. //newEntityOnMagnet.pConstraint = physenv->CreateBallsocketConstraint( pMagnetPhysObject, pPhysics, m_pConstraintGroup, ballsocket );
  1454. newEntityOnMagnet.pConstraint = physenv->CreateBallsocketConstraint( pMagnetPhysObject, pPhysics, NULL, ballsocket );
  1455. }
  1456. else
  1457. {
  1458. constraint_fixedparams_t fixed;
  1459. fixed.Defaults();
  1460. fixed.InitWithCurrentObjectState( pMagnetPhysObject, pPhysics );
  1461. fixed.constraint.Defaults();
  1462. fixed.constraint.forceLimit = lbs2kg(m_forceLimit);
  1463. fixed.constraint.torqueLimit = lbs2kg(m_torqueLimit);
  1464. // FIXME: Use the magnet's constraint group.
  1465. //newEntityOnMagnet.pConstraint = physenv->CreateFixedConstraint( pMagnetPhysObject, pPhysics, m_pConstraintGroup, fixed );
  1466. newEntityOnMagnet.pConstraint = physenv->CreateFixedConstraint( pMagnetPhysObject, pPhysics, NULL, fixed );
  1467. }
  1468. newEntityOnMagnet.pConstraint->SetGameData( (void *) this );
  1469. m_MagnettedEntities.AddToTail( newEntityOnMagnet );
  1470. m_flTotalMass += pPhysics->GetMass();
  1471. }
  1472. DoMagnetSuck( pOther );
  1473. m_OnMagnetAttach.FireOutput( this, this );
  1474. BaseClass::VPhysicsCollision( index, pEvent );
  1475. }
  1476. //-----------------------------------------------------------------------------
  1477. // Purpose:
  1478. //-----------------------------------------------------------------------------
  1479. void CPhysMagnet::DoMagnetSuck( CBaseEntity *pOther )
  1480. {
  1481. if ( !HasSpawnFlags( SF_MAGNET_SUCK ) )
  1482. return;
  1483. if ( !m_bActive )
  1484. return;
  1485. // Don't repeatedly suck
  1486. if ( m_flNextSuckTime > gpGlobals->curtime )
  1487. return;
  1488. // Look for physics objects underneath the magnet and suck them onto it
  1489. Vector vecCheckPos, vecSuckPoint;
  1490. VectorTransform( Vector(0,0,-96), EntityToWorldTransform(), vecCheckPos );
  1491. VectorTransform( Vector(0,0,-64), EntityToWorldTransform(), vecSuckPoint );
  1492. CBaseEntity *pEntities[20];
  1493. int iNumEntities = UTIL_EntitiesInSphere( pEntities, 20, vecCheckPos, 80.0, 0 );
  1494. for ( int i = 0; i < iNumEntities; i++ )
  1495. {
  1496. CBaseEntity *pEntity = pEntities[i];
  1497. if ( !pEntity || pEntity == pOther )
  1498. continue;
  1499. IPhysicsObject *pPhys = pEntity->VPhysicsGetObject();
  1500. if ( pPhys && pEntity->GetMoveType() == MOVETYPE_VPHYSICS && pPhys->GetMass() < 5000 )
  1501. {
  1502. // Do we have line of sight to it?
  1503. trace_t tr;
  1504. UTIL_TraceLine( GetAbsOrigin(), pEntity->GetAbsOrigin(), MASK_SHOT, this, 0, &tr );
  1505. if ( tr.fraction == 1.0 || tr.m_pEnt == pEntity )
  1506. {
  1507. // Pull it towards the magnet
  1508. Vector vecVelocity = (vecSuckPoint - pEntity->GetAbsOrigin());
  1509. VectorNormalize(vecVelocity);
  1510. vecVelocity *= 5 * pPhys->GetMass();
  1511. pPhys->AddVelocity( &vecVelocity, NULL );
  1512. }
  1513. }
  1514. }
  1515. m_flNextSuckTime = gpGlobals->curtime + 2.0;
  1516. }
  1517. //-----------------------------------------------------------------------------
  1518. // Purpose:
  1519. //-----------------------------------------------------------------------------
  1520. void CPhysMagnet::SetConstraintGroup( IPhysicsConstraintGroup *pGroup )
  1521. {
  1522. m_pConstraintGroup = pGroup;
  1523. }
  1524. //-----------------------------------------------------------------------------
  1525. // Purpose: Make the magnet active
  1526. //-----------------------------------------------------------------------------
  1527. void CPhysMagnet::InputTurnOn( inputdata_t &inputdata )
  1528. {
  1529. m_bActive = true;
  1530. }
  1531. //-----------------------------------------------------------------------------
  1532. // Purpose: Make the magnet inactive. Drop everything it's got hooked on.
  1533. //-----------------------------------------------------------------------------
  1534. void CPhysMagnet::InputTurnOff( inputdata_t &inputdata )
  1535. {
  1536. m_bActive = false;
  1537. DetachAll();
  1538. }
  1539. //-----------------------------------------------------------------------------
  1540. // Purpose: Toggle the magnet's active state
  1541. //-----------------------------------------------------------------------------
  1542. void CPhysMagnet::InputToggle( inputdata_t &inputdata )
  1543. {
  1544. if ( m_bActive )
  1545. {
  1546. InputTurnOff( inputdata );
  1547. }
  1548. else
  1549. {
  1550. InputTurnOn( inputdata );
  1551. }
  1552. }
  1553. //-----------------------------------------------------------------------------
  1554. // Purpose: One of our magnet constraints broke
  1555. //-----------------------------------------------------------------------------
  1556. void CPhysMagnet::ConstraintBroken( IPhysicsConstraint *pConstraint )
  1557. {
  1558. // Find the entity that was constrained and release it
  1559. int iCount = m_MagnettedEntities.Count();
  1560. for ( int i = 0; i < iCount; i++ )
  1561. {
  1562. if ( m_MagnettedEntities[i].hEntity.Get() != NULL && m_MagnettedEntities[i].pConstraint == pConstraint )
  1563. {
  1564. IPhysicsObject *pPhysObject = m_MagnettedEntities[i].hEntity->VPhysicsGetObject();
  1565. if( pPhysObject != NULL )
  1566. {
  1567. m_flTotalMass -= pPhysObject->GetMass();
  1568. }
  1569. m_MagnettedEntities.Remove(i);
  1570. break;
  1571. }
  1572. }
  1573. m_OnMagnetDetach.FireOutput( this, this );
  1574. physenv->DestroyConstraint( pConstraint );
  1575. }
  1576. //-----------------------------------------------------------------------------
  1577. // Purpose:
  1578. //-----------------------------------------------------------------------------
  1579. void CPhysMagnet::DetachAll( void )
  1580. {
  1581. // Make sure we haven't already got this sucker on the magnet
  1582. int iCount = m_MagnettedEntities.Count();
  1583. for ( int i = 0; i < iCount; i++ )
  1584. {
  1585. // Delay a couple seconds to reset to the default shadow cast behavior
  1586. if ( m_MagnettedEntities[i].hEntity )
  1587. {
  1588. m_MagnettedEntities[i].hEntity->SetShadowCastDistance( 0, 2.0f );
  1589. }
  1590. physenv->DestroyConstraint( m_MagnettedEntities[i].pConstraint );
  1591. }
  1592. m_MagnettedEntities.Purge();
  1593. m_flTotalMass = 0;
  1594. }
  1595. //-----------------------------------------------------------------------------
  1596. // Purpose:
  1597. //-----------------------------------------------------------------------------
  1598. int CPhysMagnet::GetNumAttachedObjects( void )
  1599. {
  1600. return m_MagnettedEntities.Count();
  1601. }
  1602. //-----------------------------------------------------------------------------
  1603. // Purpose:
  1604. //-----------------------------------------------------------------------------
  1605. float CPhysMagnet::GetTotalMassAttachedObjects( void )
  1606. {
  1607. return m_flTotalMass;
  1608. }
  1609. //-----------------------------------------------------------------------------
  1610. // Purpose:
  1611. //-----------------------------------------------------------------------------
  1612. CBaseEntity *CPhysMagnet::GetAttachedObject( int iIndex )
  1613. {
  1614. Assert( iIndex < GetNumAttachedObjects() );
  1615. return m_MagnettedEntities[iIndex].hEntity;
  1616. }
  1617. class CInfoMassCenter : public CPointEntity
  1618. {
  1619. DECLARE_CLASS( CInfoMassCenter, CPointEntity );
  1620. public:
  1621. void Spawn( void )
  1622. {
  1623. if ( m_target != NULL_STRING )
  1624. {
  1625. masscenteroverride_t params;
  1626. params.SnapToPoint( m_target, GetAbsOrigin() );
  1627. PhysSetMassCenterOverride( params );
  1628. UTIL_Remove( this );
  1629. }
  1630. }
  1631. };
  1632. LINK_ENTITY_TO_CLASS( info_mass_center, CInfoMassCenter );