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.

4934 lines
135 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: Physics cannon
  4. //
  5. //=============================================================================//
  6. #include "cbase.h"
  7. #include "player.h"
  8. #include "weapon_portalgun.h"
  9. #include "prop_portal_shared.h"
  10. #include "portal_util_shared.h"
  11. #include "gamerules.h"
  12. #include "soundenvelope.h"
  13. #include "engine/IEngineSound.h"
  14. #include "physics.h"
  15. #include "in_buttons.h"
  16. #include "soundent.h"
  17. #include "IEffects.h"
  18. #include "ndebugoverlay.h"
  19. #include "shake.h"
  20. #include "portal_player.h"
  21. #include "hl2_player.h"
  22. #include "beam_shared.h"
  23. #include "Sprite.h"
  24. #include "util.h"
  25. #include "portal/weapon_physcannon.h"
  26. #include "physics_saverestore.h"
  27. #include "ai_basenpc.h"
  28. #include "player_pickup.h"
  29. #include "physics_prop_ragdoll.h"
  30. #include "globalstate.h"
  31. #include "props.h"
  32. #include "movevars_shared.h"
  33. #include "weapon_portalbasecombatweapon.h"
  34. #include "te_effect_dispatch.h"
  35. #include "vphysics/friction.h"
  36. #include "saverestore_utlvector.h"
  37. #include "prop_combine_ball.h"
  38. #include "physobj.h"
  39. #include "hl2_gamerules.h"
  40. #include "citadel_effects_shared.h"
  41. #include "eventqueue.h"
  42. #include "model_types.h"
  43. #include "ai_interactions.h"
  44. #include "rumble_shared.h"
  45. #include "gamestats.h"
  46. // NVNT haptic utils
  47. #include "haptics/haptic_utils.h"
  48. // memdbgon must be the last include file in a .cpp file!!!
  49. #include "tier0/memdbgon.h"
  50. static const char *s_pWaitForUpgradeContext = "WaitForUpgrade";
  51. ConVar g_debug_physcannon( "g_debug_physcannon", "0" );
  52. ConVar physcannon_minforce( "physcannon_minforce", "700" );
  53. ConVar physcannon_maxforce( "physcannon_maxforce", "1500" );
  54. ConVar physcannon_maxmass( "physcannon_maxmass", "250" );
  55. ConVar physcannon_tracelength( "physcannon_tracelength", "250" );
  56. ConVar physcannon_mega_tracelength( "physcannon_mega_tracelength", "850" );
  57. ConVar physcannon_chargetime("physcannon_chargetime", "2" );
  58. ConVar physcannon_pullforce( "physcannon_pullforce", "4000" );
  59. ConVar physcannon_mega_pullforce( "physcannon_mega_pullforce", "8000" );
  60. ConVar physcannon_cone( "physcannon_cone", "0.97" );
  61. ConVar physcannon_ball_cone( "physcannon_ball_cone", "0.997" );
  62. ConVar physcannon_punt_cone( "physcannon_punt_cone", "0.997" );
  63. ConVar player_throwforce( "player_throwforce", "1000" );
  64. ConVar physcannon_dmg_glass( "physcannon_dmg_glass", "15" );
  65. extern ConVar hl2_normspeed;
  66. extern ConVar hl2_walkspeed;
  67. #define PHYSCANNON_BEAM_SPRITE "sprites/orangelight1.vmt"
  68. #define PHYSCANNON_GLOW_SPRITE "sprites/glow04_noz.vmt"
  69. #define PHYSCANNON_ENDCAP_SPRITE "sprites/orangeflare1.vmt"
  70. #define PHYSCANNON_CENTER_GLOW "sprites/orangecore1.vmt"
  71. #define PHYSCANNON_BLAST_SPRITE "sprites/orangecore2.vmt"
  72. #define MEGACANNON_BEAM_SPRITE "sprites/lgtning_noz.vmt"
  73. #define MEGACANNON_GLOW_SPRITE "sprites/blueflare1_noz.vmt"
  74. #define MEGACANNON_ENDCAP_SPRITE "sprites/blueflare1_noz.vmt"
  75. #define MEGACANNON_CENTER_GLOW "effects/fluttercore.vmt"
  76. #define MEGACANNON_BLAST_SPRITE "effects/fluttercore.vmt"
  77. #define MEGACANNON_RAGDOLL_BOOGIE_SPRITE "sprites/lgtning_noz.vmt"
  78. #define MEGACANNON_MODEL "models/weapons/v_superphyscannon.mdl"
  79. #define MEGACANNON_SKIN 1
  80. // -------------------------------------------------------------------------
  81. // Physcannon trace filter to handle special cases
  82. // -------------------------------------------------------------------------
  83. class CTraceFilterPhyscannon : public CTraceFilterSimple
  84. {
  85. public:
  86. DECLARE_CLASS( CTraceFilterPhyscannon, CTraceFilterSimple );
  87. CTraceFilterPhyscannon( const IHandleEntity *passentity, int collisionGroup )
  88. : CTraceFilterSimple( NULL, collisionGroup ), m_pTraceOwner( passentity ) { }
  89. // For this test, we only test against entities (then world brushes afterwards)
  90. virtual TraceType_t GetTraceType() const { return TRACE_ENTITIES_ONLY; }
  91. bool HasContentsGrate( CBaseEntity *pEntity )
  92. {
  93. // FIXME: Move this into the GetModelContents() function in base entity
  94. // Find the contents based on the model type
  95. int nModelType = modelinfo->GetModelType( pEntity->GetModel() );
  96. if ( nModelType == mod_studio )
  97. {
  98. CBaseAnimating *pAnim = dynamic_cast<CBaseAnimating *>(pEntity);
  99. if ( pAnim != NULL )
  100. {
  101. CStudioHdr *pStudioHdr = pAnim->GetModelPtr();
  102. if ( pStudioHdr != NULL && (pStudioHdr->contents() & CONTENTS_GRATE) )
  103. return true;
  104. }
  105. }
  106. else if ( nModelType == mod_brush )
  107. {
  108. // Brushes poll their contents differently
  109. int contents = modelinfo->GetModelContents( pEntity->GetModelIndex() );
  110. if ( contents & CONTENTS_GRATE )
  111. return true;
  112. }
  113. return false;
  114. }
  115. virtual bool ShouldHitEntity( IHandleEntity *pHandleEntity, int contentsMask )
  116. {
  117. // Only skip ourselves (not things we own)
  118. if ( pHandleEntity == m_pTraceOwner )
  119. return false;
  120. // Get the entity referenced by this handle
  121. CBaseEntity *pEntity = EntityFromEntityHandle( pHandleEntity );
  122. if ( pEntity == NULL )
  123. return false;
  124. // Handle grate entities differently
  125. if ( HasContentsGrate( pEntity ) )
  126. {
  127. // See if it's a grabbable physics prop
  128. CPhysicsProp *pPhysProp = dynamic_cast<CPhysicsProp *>(pEntity);
  129. if ( pPhysProp != NULL )
  130. return pPhysProp->CanBePickedUpByPhyscannon();
  131. // See if it's a grabbable physics prop
  132. if ( FClassnameIs( pEntity, "prop_physics" ) )
  133. {
  134. CPhysicsProp *pPhysProp = dynamic_cast<CPhysicsProp *>(pEntity);
  135. if ( pPhysProp != NULL )
  136. return pPhysProp->CanBePickedUpByPhyscannon();
  137. // Somehow had a classname that didn't match the class!
  138. Assert(0);
  139. }
  140. else if ( FClassnameIs( pEntity, "func_physbox" ) )
  141. {
  142. // Must be a moveable physbox
  143. CPhysBox *pPhysBox = dynamic_cast<CPhysBox *>(pEntity);
  144. if ( pPhysBox )
  145. return pPhysBox->CanBePickedUpByPhyscannon();
  146. // Somehow had a classname that didn't match the class!
  147. Assert(0);
  148. }
  149. // Don't bother with any other sort of grated entity
  150. return false;
  151. }
  152. // Use the default rules
  153. return BaseClass::ShouldHitEntity( pHandleEntity, contentsMask );
  154. }
  155. protected:
  156. const IHandleEntity *m_pTraceOwner;
  157. };
  158. // We want to test against brushes alone
  159. class CTraceFilterOnlyBrushes : public CTraceFilterSimple
  160. {
  161. public:
  162. DECLARE_CLASS( CTraceFilterOnlyBrushes, CTraceFilterSimple );
  163. CTraceFilterOnlyBrushes( int collisionGroup ) : CTraceFilterSimple( NULL, collisionGroup ) {}
  164. virtual TraceType_t GetTraceType() const { return TRACE_WORLD_ONLY; }
  165. };
  166. //-----------------------------------------------------------------------------
  167. // this will hit skip the pass entity, but not anything it owns
  168. // (lets player grab own grenades)
  169. class CTraceFilterNoOwnerTest : public CTraceFilterSimple
  170. {
  171. public:
  172. DECLARE_CLASS( CTraceFilterNoOwnerTest, CTraceFilterSimple );
  173. CTraceFilterNoOwnerTest( const IHandleEntity *passentity, int collisionGroup )
  174. : CTraceFilterSimple( NULL, collisionGroup ), m_pPassNotOwner(passentity)
  175. {
  176. }
  177. virtual bool ShouldHitEntity( IHandleEntity *pHandleEntity, int contentsMask )
  178. {
  179. if ( pHandleEntity != m_pPassNotOwner )
  180. return BaseClass::ShouldHitEntity( pHandleEntity, contentsMask );
  181. return false;
  182. }
  183. protected:
  184. const IHandleEntity *m_pPassNotOwner;
  185. };
  186. //-----------------------------------------------------------------------------
  187. // Purpose: Trace a line the special physcannon way!
  188. //-----------------------------------------------------------------------------
  189. void UTIL_PhyscannonTraceLine( const Vector &vecAbsStart, const Vector &vecAbsEnd, CBaseEntity *pTraceOwner, trace_t *pTrace )
  190. {
  191. // Default to HL2 vanilla
  192. if ( hl2_episodic.GetBool() == false )
  193. {
  194. CTraceFilterNoOwnerTest filter( pTraceOwner, COLLISION_GROUP_NONE );
  195. UTIL_TraceLine( vecAbsStart, vecAbsEnd, (MASK_SHOT|CONTENTS_GRATE), &filter, pTrace );
  196. return;
  197. }
  198. // First, trace against entities
  199. CTraceFilterPhyscannon filter( pTraceOwner, COLLISION_GROUP_NONE );
  200. UTIL_TraceLine( vecAbsStart, vecAbsEnd, (MASK_SHOT|CONTENTS_GRATE), &filter, pTrace );
  201. // If we've hit something, test again to make sure no brushes block us
  202. if ( pTrace->m_pEnt != NULL )
  203. {
  204. trace_t testTrace;
  205. CTraceFilterOnlyBrushes brushFilter( COLLISION_GROUP_NONE );
  206. UTIL_TraceLine( pTrace->startpos, pTrace->endpos, MASK_SHOT, &brushFilter, &testTrace );
  207. // If we hit a brush, replace the trace with that result
  208. if ( testTrace.fraction < 1.0f || testTrace.startsolid || testTrace.allsolid )
  209. {
  210. *pTrace = testTrace;
  211. }
  212. }
  213. }
  214. //-----------------------------------------------------------------------------
  215. // Purpose: Trace a hull for the physcannon
  216. //-----------------------------------------------------------------------------
  217. void UTIL_PhyscannonTraceHull( const Vector &vecAbsStart, const Vector &vecAbsEnd, const Vector &vecAbsMins, const Vector &vecAbsMaxs, CBaseEntity *pTraceOwner, trace_t *pTrace )
  218. {
  219. // Default to HL2 vanilla
  220. if ( hl2_episodic.GetBool() == false )
  221. {
  222. CTraceFilterNoOwnerTest filter( pTraceOwner, COLLISION_GROUP_NONE );
  223. UTIL_TraceHull( vecAbsStart, vecAbsEnd, vecAbsMins, vecAbsMaxs, (MASK_SHOT|CONTENTS_GRATE), &filter, pTrace );
  224. return;
  225. }
  226. // First, trace against entities
  227. CTraceFilterPhyscannon filter( pTraceOwner, COLLISION_GROUP_NONE );
  228. UTIL_TraceHull( vecAbsStart, vecAbsEnd, vecAbsMins, vecAbsMaxs, (MASK_SHOT|CONTENTS_GRATE), &filter, pTrace );
  229. // If we've hit something, test again to make sure no brushes block us
  230. if ( pTrace->m_pEnt != NULL )
  231. {
  232. trace_t testTrace;
  233. CTraceFilterOnlyBrushes brushFilter( COLLISION_GROUP_NONE );
  234. UTIL_TraceHull( pTrace->startpos, pTrace->endpos, vecAbsMins, vecAbsMaxs, MASK_SHOT, &brushFilter, &testTrace );
  235. // If we hit a brush, replace the trace with that result
  236. if ( testTrace.fraction < 1.0f || testTrace.startsolid || testTrace.allsolid )
  237. {
  238. *pTrace = testTrace;
  239. }
  240. }
  241. }
  242. static void MatrixOrthogonalize( matrix3x4_t &matrix, int column )
  243. {
  244. Vector columns[3];
  245. int i;
  246. for ( i = 0; i < 3; i++ )
  247. {
  248. MatrixGetColumn( matrix, i, columns[i] );
  249. }
  250. int index0 = column;
  251. int index1 = (column+1)%3;
  252. int index2 = (column+2)%3;
  253. columns[index2] = CrossProduct( columns[index0], columns[index1] );
  254. columns[index1] = CrossProduct( columns[index2], columns[index0] );
  255. VectorNormalize( columns[index2] );
  256. VectorNormalize( columns[index1] );
  257. MatrixSetColumn( columns[index1], index1, matrix );
  258. MatrixSetColumn( columns[index2], index2, matrix );
  259. }
  260. #define SIGN(x) ( (x) < 0 ? -1 : 1 )
  261. static QAngle AlignAngles( const QAngle &angles, float cosineAlignAngle )
  262. {
  263. matrix3x4_t alignMatrix;
  264. AngleMatrix( angles, alignMatrix );
  265. // NOTE: Must align z first
  266. for ( int j = 3; --j >= 0; )
  267. {
  268. Vector vec;
  269. MatrixGetColumn( alignMatrix, j, vec );
  270. for ( int i = 0; i < 3; i++ )
  271. {
  272. if ( fabs(vec[i]) > cosineAlignAngle )
  273. {
  274. vec[i] = SIGN(vec[i]);
  275. vec[(i+1)%3] = 0;
  276. vec[(i+2)%3] = 0;
  277. MatrixSetColumn( vec, j, alignMatrix );
  278. MatrixOrthogonalize( alignMatrix, j );
  279. break;
  280. }
  281. }
  282. }
  283. QAngle out;
  284. MatrixAngles( alignMatrix, out );
  285. return out;
  286. }
  287. static void TraceCollideAgainstBBox( const CPhysCollide *pCollide, const Vector &start, const Vector &end, const QAngle &angles, const Vector &boxOrigin, const Vector &mins, const Vector &maxs, trace_t *ptr )
  288. {
  289. physcollision->TraceBox( boxOrigin, boxOrigin + (start-end), mins, maxs, pCollide, start, angles, ptr );
  290. if ( ptr->DidHit() )
  291. {
  292. ptr->endpos = start * (1-ptr->fraction) + end * ptr->fraction;
  293. ptr->startpos = start;
  294. ptr->plane.dist = -ptr->plane.dist;
  295. ptr->plane.normal *= -1;
  296. }
  297. }
  298. //-----------------------------------------------------------------------------
  299. // Purpose: Finds the nearest ragdoll sub-piece to a location and returns it
  300. // Input : *pTarget - entity that is the potential ragdoll
  301. // &position - position we're testing against
  302. // Output : IPhysicsObject - sub-object (if any)
  303. //-----------------------------------------------------------------------------
  304. IPhysicsObject *GetRagdollChildAtPosition( CBaseEntity *pTarget, const Vector &position )
  305. {
  306. // Check for a ragdoll
  307. if ( dynamic_cast<CRagdollProp*>( pTarget ) == NULL )
  308. return NULL;
  309. // Get the root
  310. IPhysicsObject *pList[VPHYSICS_MAX_OBJECT_LIST_COUNT];
  311. int count = pTarget->VPhysicsGetObjectList( pList, ARRAYSIZE( pList ) );
  312. IPhysicsObject *pBestChild = NULL;
  313. float flBestDist = 99999999.0f;
  314. float flDist;
  315. Vector vPos;
  316. // Find the nearest child to where we're looking
  317. for ( int i = 0; i < count; i++ )
  318. {
  319. pList[i]->GetPosition( &vPos, NULL );
  320. flDist = ( position - vPos ).LengthSqr();
  321. if ( flDist < flBestDist )
  322. {
  323. pBestChild = pList[i];
  324. flBestDist = flDist;
  325. }
  326. }
  327. // Make this our base now
  328. pTarget->VPhysicsSwapObject( pBestChild );
  329. return pTarget->VPhysicsGetObject();
  330. }
  331. //-----------------------------------------------------------------------------
  332. // Purpose: Computes a local matrix for the player clamped to valid carry ranges
  333. //-----------------------------------------------------------------------------
  334. // when looking level, hold bottom of object 8 inches below eye level
  335. #define PLAYER_HOLD_LEVEL_EYES -8
  336. // when looking down, hold bottom of object 0 inches from feet
  337. #define PLAYER_HOLD_DOWN_FEET 2
  338. // when looking up, hold bottom of object 24 inches above eye level
  339. #define PLAYER_HOLD_UP_EYES 24
  340. // use a +/-30 degree range for the entire range of motion of pitch
  341. #define PLAYER_LOOK_PITCH_RANGE 30
  342. // player can reach down 2ft below his feet (otherwise he'll hold the object above the bottom)
  343. #define PLAYER_REACH_DOWN_DISTANCE 24
  344. static void ComputePlayerMatrix( CBasePlayer *pPlayer, matrix3x4_t &out )
  345. {
  346. if ( !pPlayer )
  347. return;
  348. QAngle angles = pPlayer->EyeAngles();
  349. Vector origin = pPlayer->EyePosition();
  350. // 0-360 / -180-180
  351. //angles.x = init ? 0 : AngleDistance( angles.x, 0 );
  352. //angles.x = clamp( angles.x, -PLAYER_LOOK_PITCH_RANGE, PLAYER_LOOK_PITCH_RANGE );
  353. angles.x = 0;
  354. float feet = pPlayer->GetAbsOrigin().z + pPlayer->WorldAlignMins().z;
  355. float eyes = origin.z;
  356. float zoffset = 0;
  357. // moving up (negative pitch is up)
  358. if ( angles.x < 0 )
  359. {
  360. zoffset = RemapVal( angles.x, 0, -PLAYER_LOOK_PITCH_RANGE, PLAYER_HOLD_LEVEL_EYES, PLAYER_HOLD_UP_EYES );
  361. }
  362. else
  363. {
  364. zoffset = RemapVal( angles.x, 0, PLAYER_LOOK_PITCH_RANGE, PLAYER_HOLD_LEVEL_EYES, PLAYER_HOLD_DOWN_FEET + (feet - eyes) );
  365. }
  366. origin.z += zoffset;
  367. angles.x = 0;
  368. AngleMatrix( angles, origin, out );
  369. }
  370. //-----------------------------------------------------------------------------
  371. // Purpose:
  372. //-----------------------------------------------------------------------------
  373. // derive from this so we can add save/load data to it
  374. struct game_shadowcontrol_params_t : public hlshadowcontrol_params_t
  375. {
  376. DECLARE_SIMPLE_DATADESC();
  377. };
  378. BEGIN_SIMPLE_DATADESC( game_shadowcontrol_params_t )
  379. DEFINE_FIELD( targetPosition, FIELD_POSITION_VECTOR ),
  380. DEFINE_FIELD( targetRotation, FIELD_VECTOR ),
  381. DEFINE_FIELD( maxAngular, FIELD_FLOAT ),
  382. DEFINE_FIELD( maxDampAngular, FIELD_FLOAT ),
  383. DEFINE_FIELD( maxSpeed, FIELD_FLOAT ),
  384. DEFINE_FIELD( maxDampSpeed, FIELD_FLOAT ),
  385. DEFINE_FIELD( dampFactor, FIELD_FLOAT ),
  386. DEFINE_FIELD( teleportDistance, FIELD_FLOAT ),
  387. END_DATADESC()
  388. //-----------------------------------------------------------------------------
  389. class CGrabController : public IMotionEvent
  390. {
  391. DECLARE_SIMPLE_DATADESC();
  392. public:
  393. CGrabController( void );
  394. ~CGrabController( void );
  395. void AttachEntity( CBasePlayer *pPlayer, CBaseEntity *pEntity, IPhysicsObject *pPhys, bool bIsMegaPhysCannon, const Vector &vGrabPosition, bool bUseGrabPosition );
  396. void DetachEntity( bool bClearVelocity );
  397. void OnRestore();
  398. bool UpdateObject( CBasePlayer *pPlayer, float flError );
  399. void SetTargetPosition( const Vector &target, const QAngle &targetOrientation );
  400. void GetTargetPosition( Vector *target, QAngle *targetOrientation );
  401. float ComputeError();
  402. float GetLoadWeight( void ) const { return m_flLoadWeight; }
  403. void SetAngleAlignment( float alignAngleCosine ) { m_angleAlignment = alignAngleCosine; }
  404. void SetIgnorePitch( bool bIgnore ) { m_bIgnoreRelativePitch = bIgnore; }
  405. QAngle TransformAnglesToPlayerSpace( const QAngle &anglesIn, CBasePlayer *pPlayer );
  406. QAngle TransformAnglesFromPlayerSpace( const QAngle &anglesIn, CBasePlayer *pPlayer );
  407. CBaseEntity *GetAttached() { return (CBaseEntity *)m_attachedEntity; }
  408. IMotionEvent::simresult_e Simulate( IPhysicsMotionController *pController, IPhysicsObject *pObject, float deltaTime, Vector &linear, AngularImpulse &angular );
  409. float GetSavedMass( IPhysicsObject *pObject );
  410. bool IsObjectAllowedOverhead( CBaseEntity *pEntity );
  411. //set when a held entity is penetrating another through a portal. Needed for special fixes
  412. void SetPortalPenetratingEntity( CBaseEntity *pPenetrated );
  413. private:
  414. // Compute the max speed for an attached object
  415. void ComputeMaxSpeed( CBaseEntity *pEntity, IPhysicsObject *pPhysics );
  416. game_shadowcontrol_params_t m_shadow;
  417. float m_timeToArrive;
  418. float m_errorTime;
  419. float m_error;
  420. float m_contactAmount;
  421. float m_angleAlignment;
  422. bool m_bCarriedEntityBlocksLOS;
  423. bool m_bIgnoreRelativePitch;
  424. float m_flLoadWeight;
  425. float m_savedRotDamping[VPHYSICS_MAX_OBJECT_LIST_COUNT];
  426. float m_savedMass[VPHYSICS_MAX_OBJECT_LIST_COUNT];
  427. EHANDLE m_attachedEntity;
  428. QAngle m_vecPreferredCarryAngles;
  429. bool m_bHasPreferredCarryAngles;
  430. float m_flDistanceOffset;
  431. QAngle m_attachedAnglesPlayerSpace;
  432. Vector m_attachedPositionObjectSpace;
  433. IPhysicsMotionController *m_controller;
  434. // NVNT player controlling this grab controller
  435. CBasePlayer* m_pControllingPlayer;
  436. bool m_bAllowObjectOverhead; // Can the player hold this object directly overhead? (Default is NO)
  437. //set when a held entity is penetrating another through a portal. Needed for special fixes
  438. EHANDLE m_PenetratedEntity;
  439. friend class CWeaponPhysCannon;
  440. friend void GetSavedParamsForCarriedPhysObject( CGrabController *pGrabController, IPhysicsObject *pObject, float *pSavedMassOut, float *pSavedRotationalDampingOut );
  441. };
  442. BEGIN_SIMPLE_DATADESC( CGrabController )
  443. DEFINE_EMBEDDED( m_shadow ),
  444. DEFINE_FIELD( m_timeToArrive, FIELD_FLOAT ),
  445. DEFINE_FIELD( m_errorTime, FIELD_FLOAT ),
  446. DEFINE_FIELD( m_error, FIELD_FLOAT ),
  447. DEFINE_FIELD( m_contactAmount, FIELD_FLOAT ),
  448. DEFINE_AUTO_ARRAY( m_savedRotDamping, FIELD_FLOAT ),
  449. DEFINE_AUTO_ARRAY( m_savedMass, FIELD_FLOAT ),
  450. DEFINE_FIELD( m_flLoadWeight, FIELD_FLOAT ),
  451. DEFINE_FIELD( m_bCarriedEntityBlocksLOS, FIELD_BOOLEAN ),
  452. DEFINE_FIELD( m_bIgnoreRelativePitch, FIELD_BOOLEAN ),
  453. DEFINE_FIELD( m_attachedEntity, FIELD_EHANDLE ),
  454. DEFINE_FIELD( m_angleAlignment, FIELD_FLOAT ),
  455. DEFINE_FIELD( m_vecPreferredCarryAngles, FIELD_VECTOR ),
  456. DEFINE_FIELD( m_bHasPreferredCarryAngles, FIELD_BOOLEAN ),
  457. DEFINE_FIELD( m_flDistanceOffset, FIELD_FLOAT ),
  458. DEFINE_FIELD( m_attachedAnglesPlayerSpace, FIELD_VECTOR ),
  459. DEFINE_FIELD( m_attachedPositionObjectSpace, FIELD_VECTOR ),
  460. DEFINE_FIELD( m_bAllowObjectOverhead, FIELD_BOOLEAN ),
  461. // Physptrs can't be inside embedded classes
  462. // DEFINE_PHYSPTR( m_controller ),
  463. END_DATADESC()
  464. const float DEFAULT_MAX_ANGULAR = 360.0f * 10.0f;
  465. const float REDUCED_CARRY_MASS = 1.0f;
  466. CGrabController::CGrabController( void )
  467. {
  468. m_shadow.dampFactor = 1.0;
  469. m_shadow.teleportDistance = 0;
  470. m_errorTime = 0;
  471. m_error = 0;
  472. // make this controller really stiff!
  473. m_shadow.maxSpeed = 1000;
  474. m_shadow.maxAngular = DEFAULT_MAX_ANGULAR;
  475. m_shadow.maxDampSpeed = m_shadow.maxSpeed*2;
  476. m_shadow.maxDampAngular = m_shadow.maxAngular;
  477. m_attachedEntity = NULL;
  478. m_vecPreferredCarryAngles = vec3_angle;
  479. m_bHasPreferredCarryAngles = false;
  480. m_flDistanceOffset = 0;
  481. // NVNT constructing m_pControllingPlayer to NULL
  482. m_pControllingPlayer = NULL;
  483. }
  484. CGrabController::~CGrabController( void )
  485. {
  486. DetachEntity( false );
  487. }
  488. void CGrabController::OnRestore()
  489. {
  490. if ( m_controller )
  491. {
  492. m_controller->SetEventHandler( this );
  493. }
  494. }
  495. void CGrabController::SetTargetPosition( const Vector &target, const QAngle &targetOrientation )
  496. {
  497. m_shadow.targetPosition = target;
  498. m_shadow.targetRotation = targetOrientation;
  499. m_timeToArrive = gpGlobals->frametime;
  500. CBaseEntity *pAttached = GetAttached();
  501. if ( pAttached )
  502. {
  503. IPhysicsObject *pObj = pAttached->VPhysicsGetObject();
  504. if ( pObj != NULL )
  505. {
  506. pObj->Wake();
  507. }
  508. else
  509. {
  510. DetachEntity( false );
  511. }
  512. }
  513. }
  514. void CGrabController::GetTargetPosition( Vector *target, QAngle *targetOrientation )
  515. {
  516. if ( target )
  517. *target = m_shadow.targetPosition;
  518. if ( targetOrientation )
  519. *targetOrientation = m_shadow.targetRotation;
  520. }
  521. //-----------------------------------------------------------------------------
  522. // Purpose:
  523. // Output : float
  524. //-----------------------------------------------------------------------------
  525. float CGrabController::ComputeError()
  526. {
  527. if ( m_errorTime <= 0 )
  528. return 0;
  529. CBaseEntity *pAttached = GetAttached();
  530. if ( pAttached )
  531. {
  532. Vector pos;
  533. IPhysicsObject *pObj = pAttached->VPhysicsGetObject();
  534. if ( pObj )
  535. {
  536. pObj->GetShadowPosition( &pos, NULL );
  537. float error = (m_shadow.targetPosition - pos).Length();
  538. if ( m_errorTime > 0 )
  539. {
  540. if ( m_errorTime > 1 )
  541. {
  542. m_errorTime = 1;
  543. }
  544. float speed = error / m_errorTime;
  545. if ( speed > m_shadow.maxSpeed )
  546. {
  547. error *= 0.5;
  548. }
  549. m_error = (1-m_errorTime) * m_error + error * m_errorTime;
  550. }
  551. }
  552. else
  553. {
  554. DevMsg( "Object attached to Physcannon has no physics object\n" );
  555. DetachEntity( false );
  556. return 9999; // force detach
  557. }
  558. }
  559. if ( pAttached->IsEFlagSet( EFL_IS_BEING_LIFTED_BY_BARNACLE ) )
  560. {
  561. m_error *= 3.0f;
  562. }
  563. // If held across a portal but not looking at the portal multiply error
  564. CPortal_Player *pPortalPlayer = (CPortal_Player *)GetPlayerHoldingEntity( pAttached );
  565. Assert( pPortalPlayer );
  566. if ( pPortalPlayer->IsHeldObjectOnOppositeSideOfPortal() )
  567. {
  568. Vector forward, right, up;
  569. QAngle playerAngles = pPortalPlayer->EyeAngles();
  570. float pitch = AngleDistance(playerAngles.x,0);
  571. playerAngles.x = clamp( pitch, -75, 75 );
  572. AngleVectors( playerAngles, &forward, &right, &up );
  573. Vector start = pPortalPlayer->Weapon_ShootPosition();
  574. // If the player is upside down then we need to hold the box closer to their feet.
  575. if ( up.z < 0.0f )
  576. start += pPortalPlayer->GetViewOffset() * up.z;
  577. if ( right.z < 0.0f )
  578. start += pPortalPlayer->GetViewOffset() * right.z;
  579. Ray_t rayPortalTest;
  580. rayPortalTest.Init( start, start + forward * 256.0f );
  581. if ( UTIL_IntersectRayWithPortal( rayPortalTest, pPortalPlayer->GetHeldObjectPortal() ) < 0.0f )
  582. {
  583. m_error *= 2.5f;
  584. }
  585. }
  586. m_errorTime = 0;
  587. return m_error;
  588. }
  589. #define MASS_SPEED_SCALE 60
  590. #define MAX_MASS 40
  591. void CGrabController::ComputeMaxSpeed( CBaseEntity *pEntity, IPhysicsObject *pPhysics )
  592. {
  593. m_shadow.maxSpeed = 1000;
  594. m_shadow.maxAngular = DEFAULT_MAX_ANGULAR;
  595. // Compute total mass...
  596. float flMass = PhysGetEntityMass( pEntity );
  597. float flMaxMass = physcannon_maxmass.GetFloat();
  598. if ( flMass <= flMaxMass )
  599. return;
  600. float flLerpFactor = clamp( flMass, flMaxMass, 500.0f );
  601. flLerpFactor = SimpleSplineRemapVal( flLerpFactor, flMaxMass, 500.0f, 0.0f, 1.0f );
  602. float invMass = pPhysics->GetInvMass();
  603. float invInertia = pPhysics->GetInvInertia().Length();
  604. float invMaxMass = 1.0f / MAX_MASS;
  605. float ratio = invMaxMass / invMass;
  606. invMass = invMaxMass;
  607. invInertia *= ratio;
  608. float maxSpeed = invMass * MASS_SPEED_SCALE * 200;
  609. float maxAngular = invInertia * MASS_SPEED_SCALE * 360;
  610. m_shadow.maxSpeed = Lerp( flLerpFactor, m_shadow.maxSpeed, maxSpeed );
  611. m_shadow.maxAngular = Lerp( flLerpFactor, m_shadow.maxAngular, maxAngular );
  612. }
  613. QAngle CGrabController::TransformAnglesToPlayerSpace( const QAngle &anglesIn, CBasePlayer *pPlayer )
  614. {
  615. if ( m_bIgnoreRelativePitch )
  616. {
  617. matrix3x4_t test;
  618. QAngle angleTest = pPlayer->EyeAngles();
  619. angleTest.x = 0;
  620. AngleMatrix( angleTest, test );
  621. return TransformAnglesToLocalSpace( anglesIn, test );
  622. }
  623. return TransformAnglesToLocalSpace( anglesIn, pPlayer->EntityToWorldTransform() );
  624. }
  625. QAngle CGrabController::TransformAnglesFromPlayerSpace( const QAngle &anglesIn, CBasePlayer *pPlayer )
  626. {
  627. if ( m_bIgnoreRelativePitch )
  628. {
  629. matrix3x4_t test;
  630. QAngle angleTest = pPlayer->EyeAngles();
  631. angleTest.x = 0;
  632. AngleMatrix( angleTest, test );
  633. return TransformAnglesToWorldSpace( anglesIn, test );
  634. }
  635. return TransformAnglesToWorldSpace( anglesIn, pPlayer->EntityToWorldTransform() );
  636. }
  637. void CGrabController::AttachEntity( CBasePlayer *pPlayer, CBaseEntity *pEntity, IPhysicsObject *pPhys, bool bIsMegaPhysCannon, const Vector &vGrabPosition, bool bUseGrabPosition )
  638. {
  639. CPortal_Player *pPortalPlayer = ToPortalPlayer( pPlayer );
  640. // play the impact sound of the object hitting the player
  641. // used as feedback to let the player know he picked up the object
  642. if ( !pPortalPlayer->m_bSilentDropAndPickup )
  643. {
  644. int hitMaterial = pPhys->GetMaterialIndex();
  645. int playerMaterial = pPlayer->VPhysicsGetObject() ? pPlayer->VPhysicsGetObject()->GetMaterialIndex() : hitMaterial;
  646. PhysicsImpactSound( pPlayer, pPhys, CHAN_STATIC, hitMaterial, playerMaterial, 1.0, 64 );
  647. }
  648. Vector position;
  649. QAngle angles;
  650. pPhys->GetPosition( &position, &angles );
  651. // If it has a preferred orientation, use that instead.
  652. Pickup_GetPreferredCarryAngles( pEntity, pPlayer, pPlayer->EntityToWorldTransform(), angles );
  653. //Fix attachment orientation weirdness
  654. if( pPortalPlayer->IsHeldObjectOnOppositeSideOfPortal() )
  655. {
  656. Vector vPlayerForward;
  657. pPlayer->EyeVectors( &vPlayerForward );
  658. Vector radial = physcollision->CollideGetExtent( pPhys->GetCollide(), vec3_origin, pEntity->GetAbsAngles(), -vPlayerForward );
  659. Vector player2d = pPlayer->CollisionProp()->OBBMaxs();
  660. float playerRadius = player2d.Length2D();
  661. float flDot = DotProduct( vPlayerForward, radial );
  662. float radius = playerRadius + fabs( flDot );
  663. float distance = 24 + ( radius * 2.0f );
  664. //find out which portal the object is on the other side of....
  665. Vector start = pPlayer->Weapon_ShootPosition();
  666. Vector end = start + ( vPlayerForward * distance );
  667. CProp_Portal *pObjectPortal = NULL;
  668. pObjectPortal = pPortalPlayer->GetHeldObjectPortal();
  669. // If our end point hasn't gone into the portal yet we at least need to know what portal is in front of us
  670. if ( !pObjectPortal )
  671. {
  672. Ray_t rayPortalTest;
  673. rayPortalTest.Init( start, start + vPlayerForward * 1024.0f );
  674. int iPortalCount = CProp_Portal_Shared::AllPortals.Count();
  675. if( iPortalCount != 0 )
  676. {
  677. CProp_Portal **pPortals = CProp_Portal_Shared::AllPortals.Base();
  678. float fMinDist = 2.0f;
  679. for( int i = 0; i != iPortalCount; ++i )
  680. {
  681. CProp_Portal *pTempPortal = pPortals[i];
  682. if( pTempPortal->m_bActivated &&
  683. (pTempPortal->m_hLinkedPortal.Get() != NULL) )
  684. {
  685. float fDist = UTIL_IntersectRayWithPortal( rayPortalTest, pTempPortal );
  686. if( (fDist >= 0.0f) && (fDist < fMinDist) )
  687. {
  688. fMinDist = fDist;
  689. pObjectPortal = pTempPortal;
  690. }
  691. }
  692. }
  693. }
  694. }
  695. if( pObjectPortal )
  696. {
  697. UTIL_Portal_AngleTransform( pObjectPortal->m_hLinkedPortal->MatrixThisToLinked(), angles, angles );
  698. }
  699. }
  700. VectorITransform( pEntity->WorldSpaceCenter(), pEntity->EntityToWorldTransform(), m_attachedPositionObjectSpace );
  701. // ComputeMaxSpeed( pEntity, pPhys );
  702. // If we haven't been killed by a grab, we allow the gun to grab the nearest part of a ragdoll
  703. if ( bUseGrabPosition )
  704. {
  705. IPhysicsObject *pChild = GetRagdollChildAtPosition( pEntity, vGrabPosition );
  706. if ( pChild )
  707. {
  708. pPhys = pChild;
  709. }
  710. }
  711. // Carried entities can never block LOS
  712. m_bCarriedEntityBlocksLOS = pEntity->BlocksLOS();
  713. pEntity->SetBlocksLOS( false );
  714. m_controller = physenv->CreateMotionController( this );
  715. m_controller->AttachObject( pPhys, true );
  716. // Don't do this, it's causing trouble with constraint solvers.
  717. //m_controller->SetPriority( IPhysicsMotionController::HIGH_PRIORITY );
  718. pPhys->Wake();
  719. PhysSetGameFlags( pPhys, FVPHYSICS_PLAYER_HELD );
  720. SetTargetPosition( position, angles );
  721. m_attachedEntity = pEntity;
  722. IPhysicsObject *pList[VPHYSICS_MAX_OBJECT_LIST_COUNT];
  723. int count = pEntity->VPhysicsGetObjectList( pList, ARRAYSIZE(pList) );
  724. m_flLoadWeight = 0;
  725. float damping = 10;
  726. float flFactor = count / 7.5f;
  727. if ( flFactor < 1.0f )
  728. {
  729. flFactor = 1.0f;
  730. }
  731. for ( int i = 0; i < count; i++ )
  732. {
  733. float mass = pList[i]->GetMass();
  734. pList[i]->GetDamping( NULL, &m_savedRotDamping[i] );
  735. m_flLoadWeight += mass;
  736. m_savedMass[i] = mass;
  737. // reduce the mass to prevent the player from adding crazy amounts of energy to the system
  738. pList[i]->SetMass( REDUCED_CARRY_MASS / flFactor );
  739. pList[i]->SetDamping( NULL, &damping );
  740. }
  741. // NVNT setting m_pControllingPlayer to the player attached
  742. m_pControllingPlayer = pPlayer;
  743. // Give extra mass to the phys object we're actually picking up
  744. pPhys->SetMass( REDUCED_CARRY_MASS );
  745. pPhys->EnableDrag( false );
  746. m_errorTime = bIsMegaPhysCannon ? -1.5f : -1.0f; // 1 seconds until error starts accumulating
  747. m_error = 0;
  748. m_contactAmount = 0;
  749. m_attachedAnglesPlayerSpace = TransformAnglesToPlayerSpace( angles, pPlayer );
  750. if ( m_angleAlignment != 0 )
  751. {
  752. m_attachedAnglesPlayerSpace = AlignAngles( m_attachedAnglesPlayerSpace, m_angleAlignment );
  753. }
  754. // Ragdolls don't offset this way
  755. if ( dynamic_cast<CRagdollProp*>(pEntity) )
  756. {
  757. m_attachedPositionObjectSpace.Init();
  758. }
  759. else
  760. {
  761. VectorITransform( pEntity->WorldSpaceCenter(), pEntity->EntityToWorldTransform(), m_attachedPositionObjectSpace );
  762. }
  763. // If it's a prop, see if it has desired carry angles
  764. CPhysicsProp *pProp = dynamic_cast<CPhysicsProp *>(pEntity);
  765. if ( pProp )
  766. {
  767. m_bHasPreferredCarryAngles = pProp->GetPropDataAngles( "preferred_carryangles", m_vecPreferredCarryAngles );
  768. m_flDistanceOffset = pProp->GetCarryDistanceOffset();
  769. }
  770. else
  771. {
  772. m_bHasPreferredCarryAngles = false;
  773. m_flDistanceOffset = 0;
  774. }
  775. m_bAllowObjectOverhead = IsObjectAllowedOverhead( pEntity );
  776. }
  777. static void ClampPhysicsVelocity( IPhysicsObject *pPhys, float linearLimit, float angularLimit )
  778. {
  779. Vector vel;
  780. AngularImpulse angVel;
  781. pPhys->GetVelocity( &vel, &angVel );
  782. float speed = VectorNormalize(vel) - linearLimit;
  783. float angSpeed = VectorNormalize(angVel) - angularLimit;
  784. speed = speed < 0 ? 0 : -speed;
  785. angSpeed = angSpeed < 0 ? 0 : -angSpeed;
  786. vel *= speed;
  787. angVel *= angSpeed;
  788. pPhys->AddVelocity( &vel, &angVel );
  789. }
  790. void CGrabController::DetachEntity( bool bClearVelocity )
  791. {
  792. Assert(!PhysIsInCallback());
  793. CBaseEntity *pEntity = GetAttached();
  794. if ( pEntity )
  795. {
  796. // Restore the LS blocking state
  797. pEntity->SetBlocksLOS( m_bCarriedEntityBlocksLOS );
  798. IPhysicsObject *pList[VPHYSICS_MAX_OBJECT_LIST_COUNT];
  799. int count = pEntity->VPhysicsGetObjectList( pList, ARRAYSIZE(pList) );
  800. for ( int i = 0; i < count; i++ )
  801. {
  802. IPhysicsObject *pPhys = pList[i];
  803. if ( !pPhys )
  804. continue;
  805. // on the odd chance that it's gone to sleep while under anti-gravity
  806. pPhys->EnableDrag( true );
  807. pPhys->Wake();
  808. pPhys->SetMass( m_savedMass[i] );
  809. pPhys->SetDamping( NULL, &m_savedRotDamping[i] );
  810. PhysClearGameFlags( pPhys, FVPHYSICS_PLAYER_HELD );
  811. if ( bClearVelocity )
  812. {
  813. PhysForceClearVelocity( pPhys );
  814. }
  815. else
  816. {
  817. ClampPhysicsVelocity( pPhys, hl2_normspeed.GetFloat() * 1.5f, 2.0f * 360.0f );
  818. }
  819. }
  820. }
  821. m_attachedEntity = NULL;
  822. physenv->DestroyMotionController( m_controller );
  823. m_controller = NULL;
  824. }
  825. static bool InContactWithHeavyObject( IPhysicsObject *pObject, float heavyMass )
  826. {
  827. bool contact = false;
  828. IPhysicsFrictionSnapshot *pSnapshot = pObject->CreateFrictionSnapshot();
  829. while ( pSnapshot->IsValid() )
  830. {
  831. IPhysicsObject *pOther = pSnapshot->GetObject( 1 );
  832. if ( !pOther->IsMoveable() || pOther->GetMass() > heavyMass )
  833. {
  834. contact = true;
  835. break;
  836. }
  837. pSnapshot->NextFrictionData();
  838. }
  839. pObject->DestroyFrictionSnapshot( pSnapshot );
  840. return contact;
  841. }
  842. IMotionEvent::simresult_e CGrabController::Simulate( IPhysicsMotionController *pController, IPhysicsObject *pObject, float deltaTime, Vector &linear, AngularImpulse &angular )
  843. {
  844. game_shadowcontrol_params_t shadowParams = m_shadow;
  845. if ( InContactWithHeavyObject( pObject, GetLoadWeight() ) )
  846. {
  847. m_contactAmount = Approach( 0.1f, m_contactAmount, deltaTime*2.0f );
  848. }
  849. else
  850. {
  851. m_contactAmount = Approach( 1.0f, m_contactAmount, deltaTime*2.0f );
  852. }
  853. shadowParams.maxAngular = m_shadow.maxAngular * m_contactAmount * m_contactAmount * m_contactAmount;
  854. m_timeToArrive = pObject->ComputeShadowControl( shadowParams, m_timeToArrive, deltaTime );
  855. // Slide along the current contact points to fix bouncing problems
  856. Vector velocity;
  857. AngularImpulse angVel;
  858. pObject->GetVelocity( &velocity, &angVel );
  859. PhysComputeSlideDirection( pObject, velocity, angVel, &velocity, &angVel, GetLoadWeight() );
  860. pObject->SetVelocityInstantaneous( &velocity, NULL );
  861. linear.Init();
  862. angular.Init();
  863. m_errorTime += deltaTime;
  864. return SIM_LOCAL_ACCELERATION;
  865. }
  866. float CGrabController::GetSavedMass( IPhysicsObject *pObject )
  867. {
  868. CBaseEntity *pHeld = m_attachedEntity;
  869. if ( pHeld )
  870. {
  871. if ( pObject->GetGameData() == (void*)pHeld )
  872. {
  873. IPhysicsObject *pList[VPHYSICS_MAX_OBJECT_LIST_COUNT];
  874. int count = pHeld->VPhysicsGetObjectList( pList, ARRAYSIZE(pList) );
  875. for ( int i = 0; i < count; i++ )
  876. {
  877. if ( pList[i] == pObject )
  878. return m_savedMass[i];
  879. }
  880. }
  881. }
  882. return 0.0f;
  883. }
  884. //-----------------------------------------------------------------------------
  885. // Is this an object that the player is allowed to lift to a position
  886. // directly overhead? The default behavior prevents lifting objects directly
  887. // overhead, but there are exceptions for gameplay purposes.
  888. //-----------------------------------------------------------------------------
  889. bool CGrabController::IsObjectAllowedOverhead( CBaseEntity *pEntity )
  890. {
  891. // Allow combine balls overhead
  892. if( UTIL_IsCombineBallDefinite(pEntity) )
  893. return true;
  894. // Allow props that are specifically flagged as such
  895. CPhysicsProp *pPhysProp = dynamic_cast<CPhysicsProp *>(pEntity);
  896. if ( pPhysProp != NULL && pPhysProp->HasInteraction( PROPINTER_PHYSGUN_ALLOW_OVERHEAD ) )
  897. return true;
  898. // String checks are fine here, we only run this code one time- when the object is picked up.
  899. if( pEntity->ClassMatches("grenade_helicopter") )
  900. return true;
  901. if( pEntity->ClassMatches("weapon_striderbuster") )
  902. return true;
  903. return false;
  904. }
  905. void CGrabController::SetPortalPenetratingEntity( CBaseEntity *pPenetrated )
  906. {
  907. m_PenetratedEntity = pPenetrated;
  908. }
  909. //-----------------------------------------------------------------------------
  910. // Player pickup controller
  911. //-----------------------------------------------------------------------------
  912. class CPlayerPickupController : public CBaseEntity
  913. {
  914. DECLARE_DATADESC();
  915. DECLARE_CLASS( CPlayerPickupController, CBaseEntity );
  916. public:
  917. void Init( CBasePlayer *pPlayer, CBaseEntity *pObject );
  918. void Shutdown( bool bThrown = false );
  919. bool OnControls( CBaseEntity *pControls ) { return true; }
  920. void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
  921. void OnRestore()
  922. {
  923. m_grabController.OnRestore();
  924. }
  925. void VPhysicsUpdate( IPhysicsObject *pPhysics ){}
  926. void VPhysicsShadowUpdate( IPhysicsObject *pPhysics ) {}
  927. bool IsHoldingEntity( CBaseEntity *pEnt );
  928. CGrabController &GetGrabController() { return m_grabController; }
  929. private:
  930. CGrabController m_grabController;
  931. CBasePlayer *m_pPlayer;
  932. };
  933. LINK_ENTITY_TO_CLASS( player_pickup, CPlayerPickupController );
  934. //---------------------------------------------------------
  935. // Save/Restore
  936. //---------------------------------------------------------
  937. BEGIN_DATADESC( CPlayerPickupController )
  938. DEFINE_EMBEDDED( m_grabController ),
  939. // Physptrs can't be inside embedded classes
  940. DEFINE_PHYSPTR( m_grabController.m_controller ),
  941. DEFINE_FIELD( m_pPlayer, FIELD_CLASSPTR ),
  942. END_DATADESC()
  943. //-----------------------------------------------------------------------------
  944. // Purpose:
  945. // Input : *pPlayer -
  946. // *pObject -
  947. //-----------------------------------------------------------------------------
  948. void CPlayerPickupController::Init( CBasePlayer *pPlayer, CBaseEntity *pObject )
  949. {
  950. // Holster player's weapon
  951. if ( pPlayer->GetActiveWeapon() )
  952. {
  953. // Don't holster the portalgun
  954. if ( FClassnameIs( pPlayer->GetActiveWeapon(), "weapon_portalgun" ) )
  955. {
  956. CWeaponPortalgun *pPortalGun = (CWeaponPortalgun*)(pPlayer->GetActiveWeapon());
  957. pPortalGun->OpenProngs( true );
  958. }
  959. else
  960. {
  961. if ( !pPlayer->GetActiveWeapon()->Holster() )
  962. {
  963. Shutdown();
  964. return;
  965. }
  966. }
  967. }
  968. CPortal_Player *pOwner = ToPortalPlayer( pPlayer );
  969. if ( pOwner )
  970. {
  971. pOwner->EnableSprint( false );
  972. }
  973. // If the target is debris, convert it to non-debris
  974. if ( pObject->GetCollisionGroup() == COLLISION_GROUP_DEBRIS )
  975. {
  976. // Interactive debris converts back to debris when it comes to rest
  977. pObject->SetCollisionGroup( COLLISION_GROUP_INTERACTIVE_DEBRIS );
  978. }
  979. // done so I'll go across level transitions with the player
  980. SetParent( pPlayer );
  981. m_grabController.SetIgnorePitch( true );
  982. m_grabController.SetAngleAlignment( DOT_30DEGREE );
  983. m_pPlayer = pPlayer;
  984. IPhysicsObject *pPhysics = pObject->VPhysicsGetObject();
  985. if ( !pOwner->m_bSilentDropAndPickup )
  986. {
  987. Pickup_OnPhysGunPickup( pObject, m_pPlayer, PICKED_UP_BY_PLAYER );
  988. }
  989. m_grabController.AttachEntity( pPlayer, pObject, pPhysics, false, vec3_origin, false );
  990. #ifdef WIN32
  991. // NVNT apply a downward force to simulate the mass of the held object.
  992. HapticSetConstantForce(m_pPlayer,clamp(m_grabController.GetLoadWeight()*0.1,1,6)*Vector(0,-1,0));
  993. #endif
  994. m_pPlayer->m_Local.m_iHideHUD |= HIDEHUD_WEAPONSELECTION;
  995. m_pPlayer->SetUseEntity( this );
  996. }
  997. //-----------------------------------------------------------------------------
  998. // Purpose:
  999. // Input : bool -
  1000. //-----------------------------------------------------------------------------
  1001. void CPlayerPickupController::Shutdown( bool bThrown )
  1002. {
  1003. CBaseEntity *pObject = m_grabController.GetAttached();
  1004. bool bClearVelocity = false;
  1005. if ( !bThrown && pObject && pObject->VPhysicsGetObject() && pObject->VPhysicsGetObject()->GetContactPoint(NULL,NULL) )
  1006. {
  1007. bClearVelocity = true;
  1008. }
  1009. m_grabController.DetachEntity( bClearVelocity );
  1010. #ifdef WIN32
  1011. // NVNT if we have a player, issue a zero constant force message
  1012. if(m_pPlayer)
  1013. HapticSetConstantForce(m_pPlayer,Vector(0,0,0));
  1014. #endif
  1015. if ( pObject != NULL )
  1016. {
  1017. if ( !ToPortalPlayer( m_pPlayer )->m_bSilentDropAndPickup )
  1018. {
  1019. Pickup_OnPhysGunDrop( pObject, m_pPlayer, bThrown ? THROWN_BY_PLAYER : DROPPED_BY_PLAYER );
  1020. }
  1021. }
  1022. if ( m_pPlayer )
  1023. {
  1024. CPortal_Player *pOwner = ToPortalPlayer( m_pPlayer );
  1025. if ( pOwner )
  1026. {
  1027. pOwner->EnableSprint( true );
  1028. }
  1029. m_pPlayer->SetUseEntity( NULL );
  1030. if ( !pOwner->m_bSilentDropAndPickup )
  1031. {
  1032. if ( m_pPlayer->GetActiveWeapon() )
  1033. {
  1034. if ( FClassnameIs( m_pPlayer->GetActiveWeapon(), "weapon_portalgun" ) )
  1035. {
  1036. m_pPlayer->SetNextAttack( gpGlobals->curtime + 0.5f );
  1037. CWeaponPortalgun *pPortalGun = (CWeaponPortalgun*)(m_pPlayer->GetActiveWeapon());
  1038. pPortalGun->DelayAttack( 0.5f );
  1039. pPortalGun->OpenProngs( false );
  1040. }
  1041. else
  1042. {
  1043. if ( !m_pPlayer->GetActiveWeapon()->Deploy() )
  1044. {
  1045. // We tried to restore the player's weapon, but we couldn't.
  1046. // This usually happens when they're holding an empty weapon that doesn't
  1047. // autoswitch away when out of ammo. Switch to next best weapon.
  1048. m_pPlayer->SwitchToNextBestWeapon( NULL );
  1049. }
  1050. }
  1051. }
  1052. m_pPlayer->m_Local.m_iHideHUD &= ~HIDEHUD_WEAPONSELECTION;
  1053. }
  1054. }
  1055. Remove();
  1056. }
  1057. void CPlayerPickupController::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
  1058. {
  1059. if ( ToBasePlayer(pActivator) == m_pPlayer )
  1060. {
  1061. CBaseEntity *pAttached = m_grabController.GetAttached();
  1062. // UNDONE: Use vphysics stress to decide to drop objects
  1063. // UNDONE: Must fix case of forcing objects into the ground you're standing on (causes stress) before that will work
  1064. if ( !pAttached || useType == USE_OFF || (m_pPlayer->m_nButtons & IN_ATTACK2) || m_grabController.ComputeError() > 12 )
  1065. {
  1066. Shutdown();
  1067. return;
  1068. }
  1069. //Adrian: Oops, our object became motion disabled, let go!
  1070. IPhysicsObject *pPhys = pAttached->VPhysicsGetObject();
  1071. if ( pPhys && pPhys->IsMoveable() == false )
  1072. {
  1073. Shutdown();
  1074. return;
  1075. }
  1076. #if STRESS_TEST
  1077. vphysics_objectstress_t stress;
  1078. CalculateObjectStress( pPhys, pAttached, &stress );
  1079. if ( stress.exertedStress > 250 )
  1080. {
  1081. Shutdown();
  1082. return;
  1083. }
  1084. #endif
  1085. // +ATTACK will throw phys objects
  1086. if ( m_pPlayer->m_nButtons & IN_ATTACK )
  1087. {
  1088. Shutdown( true );
  1089. Vector vecLaunch;
  1090. m_pPlayer->EyeVectors( &vecLaunch );
  1091. // If throwing from the opposite side of a portal, reorient the direction
  1092. if( ((CPortal_Player *)m_pPlayer)->IsHeldObjectOnOppositeSideOfPortal() )
  1093. {
  1094. CProp_Portal *pHeldPortal = ((CPortal_Player *)m_pPlayer)->GetHeldObjectPortal();
  1095. UTIL_Portal_VectorTransform( pHeldPortal->MatrixThisToLinked(), vecLaunch, vecLaunch );
  1096. }
  1097. ((CPortal_Player *)m_pPlayer)->SetHeldObjectOnOppositeSideOfPortal( false );
  1098. // JAY: Scale this with mass because some small objects really go flying
  1099. float massFactor = clamp( pPhys->GetMass(), 0.5, 15 );
  1100. massFactor = RemapVal( massFactor, 0.5, 15, 0.5, 4 );
  1101. vecLaunch *= player_throwforce.GetFloat() * massFactor;
  1102. pPhys->ApplyForceCenter( vecLaunch );
  1103. AngularImpulse aVel = RandomAngularImpulse( -10, 10 ) * massFactor;
  1104. pPhys->ApplyTorqueCenter( aVel );
  1105. return;
  1106. }
  1107. if ( useType == USE_SET )
  1108. {
  1109. // update position
  1110. m_grabController.UpdateObject( m_pPlayer, 12 );
  1111. }
  1112. }
  1113. }
  1114. //-----------------------------------------------------------------------------
  1115. // Purpose:
  1116. // Input : *pEnt -
  1117. // Output : Returns true on success, false on failure.
  1118. //-----------------------------------------------------------------------------
  1119. bool CPlayerPickupController::IsHoldingEntity( CBaseEntity *pEnt )
  1120. {
  1121. if ( pEnt )
  1122. {
  1123. return ( m_grabController.GetAttached() == pEnt );
  1124. }
  1125. return ( m_grabController.GetAttached() != 0 );
  1126. }
  1127. void PlayerPickupObject( CBasePlayer *pPlayer, CBaseEntity *pObject )
  1128. {
  1129. //Don't pick up if we don't have a phys object.
  1130. if ( pObject->VPhysicsGetObject() == NULL )
  1131. return;
  1132. if ( pObject->GetBaseAnimating() && pObject->GetBaseAnimating()->IsDissolving() )
  1133. return;
  1134. CPlayerPickupController *pController = (CPlayerPickupController *)CBaseEntity::Create( "player_pickup", pObject->GetAbsOrigin(), vec3_angle, pPlayer );
  1135. if ( !pController )
  1136. return;
  1137. pController->Init( pPlayer, pObject );
  1138. }
  1139. //-----------------------------------------------------------------------------
  1140. //-----------------------------------------------------------------------------
  1141. // Physcannon
  1142. //-----------------------------------------------------------------------------
  1143. #define NUM_BEAMS 4
  1144. #define NUM_SPRITES 6
  1145. struct thrown_objects_t
  1146. {
  1147. float fTimeThrown;
  1148. EHANDLE hEntity;
  1149. DECLARE_SIMPLE_DATADESC();
  1150. };
  1151. BEGIN_SIMPLE_DATADESC( thrown_objects_t )
  1152. DEFINE_FIELD( fTimeThrown, FIELD_TIME ),
  1153. DEFINE_FIELD( hEntity, FIELD_EHANDLE ),
  1154. END_DATADESC()
  1155. class CWeaponPhysCannon : public CBasePortalCombatWeapon
  1156. {
  1157. public:
  1158. DECLARE_CLASS( CWeaponPhysCannon, CBasePortalCombatWeapon );
  1159. DECLARE_SERVERCLASS();
  1160. DECLARE_DATADESC();
  1161. CWeaponPhysCannon( void );
  1162. void Drop( const Vector &vecVelocity );
  1163. void Precache();
  1164. virtual void Spawn();
  1165. virtual void OnRestore();
  1166. virtual void StopLoopingSounds();
  1167. virtual void UpdateOnRemove(void);
  1168. void PrimaryAttack();
  1169. void SecondaryAttack();
  1170. void WeaponIdle();
  1171. void ItemPreFrame();
  1172. void ItemPostFrame();
  1173. void ItemBusyFrame();
  1174. virtual float GetMaxAutoAimDeflection() { return 0.90f; }
  1175. void ForceDrop( void );
  1176. bool DropIfEntityHeld( CBaseEntity *pTarget ); // Drops its held entity if it matches the entity passed in
  1177. CGrabController &GetGrabController() { return m_grabController; }
  1178. bool CanHolster( void );
  1179. bool Holster( CBaseCombatWeapon *pSwitchingTo = NULL );
  1180. bool Deploy( void );
  1181. bool HasAnyAmmo( void ) { return true; }
  1182. void InputBecomeMegaCannon( inputdata_t &inputdata );
  1183. void BeginUpgrade();
  1184. virtual void SetViewModel( void );
  1185. virtual const char *GetShootSound( int iIndex ) const;
  1186. void RecordThrownObject( CBaseEntity *pObject );
  1187. void PurgeThrownObjects();
  1188. bool IsAccountableForObject( CBaseEntity *pObject );
  1189. bool ShouldDisplayHUDHint() { return true; }
  1190. protected:
  1191. enum FindObjectResult_t
  1192. {
  1193. OBJECT_FOUND = 0,
  1194. OBJECT_NOT_FOUND,
  1195. OBJECT_BEING_DETACHED,
  1196. };
  1197. void DoMegaEffect( int effectType, Vector *pos = NULL );
  1198. void DoEffect( int effectType, Vector *pos = NULL );
  1199. void OpenElements( void );
  1200. void CloseElements( void );
  1201. // Pickup and throw objects.
  1202. bool CanPickupObject( CBaseEntity *pTarget );
  1203. void CheckForTarget( void );
  1204. FindObjectResult_t FindObject( void );
  1205. void FindObjectTrace( CBasePlayer *pPlayer, trace_t *pTraceResult );
  1206. CBaseEntity *MegaPhysCannonFindObjectInCone( const Vector &vecOrigin, const Vector &vecDir, float flCone, float flCombineBallCone, bool bOnlyCombineBalls );
  1207. CBaseEntity *FindObjectInCone( const Vector &vecOrigin, const Vector &vecDir, float flCone );
  1208. bool AttachObject( CBaseEntity *pObject, const Vector &vPosition );
  1209. void UpdateObject( void );
  1210. void DetachObject( bool playSound = true, bool wasLaunched = false );
  1211. void LaunchObject( const Vector &vecDir, float flForce );
  1212. void StartEffects( void ); // Initialize all sprites and beams
  1213. void StopEffects( bool stopSound = true ); // Hide all effects temporarily
  1214. void DestroyEffects( void ); // Destroy all sprites and beams
  1215. // Punt objects - this is pointing at an object in the world and applying a force to it.
  1216. void PuntNonVPhysics( CBaseEntity *pEntity, const Vector &forward, trace_t &tr );
  1217. void PuntVPhysics( CBaseEntity *pEntity, const Vector &forward, trace_t &tr );
  1218. void PuntRagdoll( CBaseEntity *pEntity, const Vector &forward, trace_t &tr );
  1219. // Velocity-based throw common to punt and launch code.
  1220. void ApplyVelocityBasedForce( CBaseEntity *pEntity, const Vector &forward, const Vector &vecHitPos, PhysGunForce_t reason );
  1221. // Physgun effects
  1222. void DoEffectClosed( void );
  1223. void DoMegaEffectClosed( void );
  1224. void DoEffectReady( void );
  1225. void DoMegaEffectReady( void );
  1226. void DoMegaEffectHolding( void );
  1227. void DoEffectHolding( void );
  1228. void DoMegaEffectLaunch( Vector *pos );
  1229. void DoEffectLaunch( Vector *pos );
  1230. void DoEffectNone( void );
  1231. void DoEffectIdle( void );
  1232. // Trace length
  1233. float TraceLength();
  1234. // Do we have the super-phys gun?
  1235. inline bool IsMegaPhysCannon()
  1236. {
  1237. return PlayerHasMegaPhysCannon();
  1238. }
  1239. // Sprite scale factor
  1240. float SpriteScaleFactor();
  1241. float GetLoadPercentage();
  1242. CSoundPatch *GetMotorSound( void );
  1243. void DryFire( void );
  1244. void PrimaryFireEffect( void );
  1245. // What happens when the physgun picks up something
  1246. void Physgun_OnPhysGunPickup( CBaseEntity *pEntity, CBasePlayer *pOwner, PhysGunPickup_t reason );
  1247. // Wait until we're done upgrading
  1248. void WaitForUpgradeThink();
  1249. bool EntityAllowsPunts( CBaseEntity *pEntity );
  1250. bool m_bOpen;
  1251. bool m_bActive;
  1252. int m_nChangeState; //For delayed state change of elements
  1253. float m_flCheckSuppressTime; //Amount of time to suppress the checking for targets
  1254. bool m_flLastDenySoundPlayed; //Debounce for deny sound
  1255. int m_nAttack2Debounce;
  1256. CNetworkVar( bool, m_bIsCurrentlyUpgrading );
  1257. float m_flElementDebounce;
  1258. float m_flElementPosition;
  1259. float m_flElementDestination;
  1260. CHandle<CBeam> m_hBeams[NUM_BEAMS];
  1261. CHandle<CSprite> m_hGlowSprites[NUM_SPRITES];
  1262. CHandle<CSprite> m_hEndSprites[2];
  1263. float m_flEndSpritesOverride[2];
  1264. CHandle<CSprite> m_hCenterSprite;
  1265. CHandle<CSprite> m_hBlastSprite;
  1266. CSoundPatch *m_sndMotor; // Whirring sound for the gun
  1267. CGrabController m_grabController;
  1268. int m_EffectState; // Current state of the effects on the gun
  1269. bool m_bPhyscannonState;
  1270. // A list of the objects thrown or punted recently, and the time done so.
  1271. CUtlVector< thrown_objects_t > m_ThrownEntities;
  1272. float m_flTimeNextObjectPurge;
  1273. };
  1274. IMPLEMENT_SERVERCLASS_ST(CWeaponPhysCannon, DT_WeaponPhysCannon)
  1275. SendPropBool( SENDINFO( m_bIsCurrentlyUpgrading ) ),
  1276. END_SEND_TABLE()
  1277. LINK_ENTITY_TO_CLASS( weapon_physcannon, CWeaponPhysCannon );
  1278. PRECACHE_WEAPON_REGISTER( weapon_physcannon );
  1279. BEGIN_DATADESC( CWeaponPhysCannon )
  1280. DEFINE_FIELD( m_bOpen, FIELD_BOOLEAN ),
  1281. DEFINE_FIELD( m_bActive, FIELD_BOOLEAN ),
  1282. DEFINE_FIELD( m_nChangeState, FIELD_INTEGER ),
  1283. DEFINE_FIELD( m_flCheckSuppressTime, FIELD_TIME ),
  1284. DEFINE_FIELD( m_flElementDebounce, FIELD_TIME ),
  1285. DEFINE_FIELD( m_flElementPosition, FIELD_FLOAT ),
  1286. DEFINE_FIELD( m_flElementDestination, FIELD_FLOAT ),
  1287. DEFINE_FIELD( m_nAttack2Debounce, FIELD_INTEGER ),
  1288. DEFINE_FIELD( m_bIsCurrentlyUpgrading, FIELD_BOOLEAN ),
  1289. DEFINE_FIELD( m_EffectState, FIELD_INTEGER ),
  1290. DEFINE_AUTO_ARRAY( m_hBeams, FIELD_EHANDLE ),
  1291. DEFINE_AUTO_ARRAY( m_hGlowSprites, FIELD_EHANDLE ),
  1292. DEFINE_AUTO_ARRAY( m_hEndSprites, FIELD_EHANDLE ),
  1293. DEFINE_AUTO_ARRAY( m_flEndSpritesOverride, FIELD_TIME ),
  1294. DEFINE_FIELD( m_hCenterSprite, FIELD_EHANDLE ),
  1295. DEFINE_FIELD( m_hBlastSprite, FIELD_EHANDLE ),
  1296. DEFINE_FIELD( m_flLastDenySoundPlayed, FIELD_BOOLEAN ),
  1297. DEFINE_FIELD( m_bPhyscannonState, FIELD_BOOLEAN ),
  1298. DEFINE_SOUNDPATCH( m_sndMotor ),
  1299. DEFINE_EMBEDDED( m_grabController ),
  1300. // Physptrs can't be inside embedded classes
  1301. DEFINE_PHYSPTR( m_grabController.m_controller ),
  1302. DEFINE_THINKFUNC( WaitForUpgradeThink ),
  1303. DEFINE_UTLVECTOR( m_ThrownEntities, FIELD_EMBEDDED ),
  1304. DEFINE_FIELD( m_flTimeNextObjectPurge, FIELD_TIME ),
  1305. END_DATADESC()
  1306. enum
  1307. {
  1308. ELEMENT_STATE_NONE = -1,
  1309. ELEMENT_STATE_OPEN,
  1310. ELEMENT_STATE_CLOSED,
  1311. };
  1312. enum
  1313. {
  1314. EFFECT_NONE,
  1315. EFFECT_CLOSED,
  1316. EFFECT_READY,
  1317. EFFECT_HOLDING,
  1318. EFFECT_LAUNCH,
  1319. };
  1320. //-----------------------------------------------------------------------------
  1321. // Do we have the super-phys gun?
  1322. //-----------------------------------------------------------------------------
  1323. bool PlayerHasMegaPhysCannon()
  1324. {
  1325. return ( HL2GameRules()->MegaPhyscannonActive() == true );
  1326. }
  1327. //-----------------------------------------------------------------------------
  1328. // Constructor
  1329. //-----------------------------------------------------------------------------
  1330. CWeaponPhysCannon::CWeaponPhysCannon( void )
  1331. {
  1332. m_flElementPosition = 0.0f;
  1333. m_flElementDestination = 0.0f;
  1334. m_bOpen = false;
  1335. m_nChangeState = ELEMENT_STATE_NONE;
  1336. m_flCheckSuppressTime = 0.0f;
  1337. m_EffectState = EFFECT_NONE;
  1338. m_flLastDenySoundPlayed = false;
  1339. m_flEndSpritesOverride[0] = 0.0f;
  1340. m_flEndSpritesOverride[1] = 0.0f;
  1341. m_bPhyscannonState = false;
  1342. }
  1343. //-----------------------------------------------------------------------------
  1344. // Purpose: Precache
  1345. //-----------------------------------------------------------------------------
  1346. void CWeaponPhysCannon::Precache( void )
  1347. {
  1348. PrecacheModel( PHYSCANNON_BEAM_SPRITE );
  1349. PrecacheModel( PHYSCANNON_GLOW_SPRITE );
  1350. PrecacheModel( PHYSCANNON_ENDCAP_SPRITE );
  1351. PrecacheModel( PHYSCANNON_CENTER_GLOW );
  1352. PrecacheModel( PHYSCANNON_BLAST_SPRITE );
  1353. PrecacheModel( MEGACANNON_BEAM_SPRITE );
  1354. PrecacheModel( MEGACANNON_GLOW_SPRITE );
  1355. PrecacheModel( MEGACANNON_ENDCAP_SPRITE );
  1356. PrecacheModel( MEGACANNON_CENTER_GLOW );
  1357. PrecacheModel( MEGACANNON_BLAST_SPRITE );
  1358. PrecacheModel( MEGACANNON_RAGDOLL_BOOGIE_SPRITE );
  1359. // Precache our alternate model
  1360. PrecacheModel( MEGACANNON_MODEL );
  1361. PrecacheScriptSound( "Weapon_PhysCannon.HoldSound" );
  1362. PrecacheScriptSound( "Weapon_Physgun.Off" );
  1363. PrecacheScriptSound( "Weapon_MegaPhysCannon.DryFire" );
  1364. PrecacheScriptSound( "Weapon_MegaPhysCannon.Launch" );
  1365. PrecacheScriptSound( "Weapon_MegaPhysCannon.Pickup");
  1366. PrecacheScriptSound( "Weapon_MegaPhysCannon.Drop");
  1367. PrecacheScriptSound( "Weapon_MegaPhysCannon.HoldSound");
  1368. PrecacheScriptSound( "Weapon_MegaPhysCannon.ChargeZap");
  1369. BaseClass::Precache();
  1370. }
  1371. //-----------------------------------------------------------------------------
  1372. // Purpose:
  1373. //-----------------------------------------------------------------------------
  1374. void CWeaponPhysCannon::Spawn( void )
  1375. {
  1376. BaseClass::Spawn();
  1377. // Need to get close to pick it up
  1378. CollisionProp()->UseTriggerBounds( false );
  1379. m_bPhyscannonState = IsMegaPhysCannon();
  1380. // The megacannon uses a different skin
  1381. if ( IsMegaPhysCannon() )
  1382. {
  1383. m_nSkin = MEGACANNON_SKIN;
  1384. }
  1385. }
  1386. //-----------------------------------------------------------------------------
  1387. // Purpose: Restore
  1388. //-----------------------------------------------------------------------------
  1389. void CWeaponPhysCannon::OnRestore()
  1390. {
  1391. BaseClass::OnRestore();
  1392. m_grabController.OnRestore();
  1393. m_bPhyscannonState = IsMegaPhysCannon();
  1394. // Tracker 8106: Physcannon effects disappear through level transition, so
  1395. // just recreate any effects here
  1396. if ( m_EffectState != EFFECT_NONE )
  1397. {
  1398. DoEffect( m_EffectState, NULL );
  1399. }
  1400. }
  1401. //-----------------------------------------------------------------------------
  1402. // On Remove
  1403. //-----------------------------------------------------------------------------
  1404. void CWeaponPhysCannon::UpdateOnRemove(void)
  1405. {
  1406. DestroyEffects( );
  1407. BaseClass::UpdateOnRemove();
  1408. }
  1409. //-----------------------------------------------------------------------------
  1410. // Sprite scale factor
  1411. //-----------------------------------------------------------------------------
  1412. inline float CWeaponPhysCannon::SpriteScaleFactor()
  1413. {
  1414. return IsMegaPhysCannon() ? 1.5f : 1.0f;
  1415. }
  1416. //-----------------------------------------------------------------------------
  1417. // Purpose:
  1418. // Output : Returns true on success, false on failure.
  1419. //-----------------------------------------------------------------------------
  1420. bool CWeaponPhysCannon::Deploy( void )
  1421. {
  1422. CloseElements();
  1423. DoEffect( EFFECT_READY );
  1424. // Unbloat our bounds
  1425. if ( IsMegaPhysCannon() )
  1426. {
  1427. CollisionProp()->UseTriggerBounds( false );
  1428. }
  1429. m_flTimeNextObjectPurge = gpGlobals->curtime;
  1430. return BaseClass::Deploy();
  1431. }
  1432. //-----------------------------------------------------------------------------
  1433. // Purpose:
  1434. //-----------------------------------------------------------------------------
  1435. void CWeaponPhysCannon::SetViewModel( void )
  1436. {
  1437. if ( !IsMegaPhysCannon() )
  1438. {
  1439. BaseClass::SetViewModel();
  1440. return;
  1441. }
  1442. CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
  1443. if ( pOwner == NULL )
  1444. return;
  1445. CBaseViewModel *vm = pOwner->GetViewModel( m_nViewModelIndex );
  1446. if ( vm == NULL )
  1447. return;
  1448. vm->SetWeaponModel( MEGACANNON_MODEL, this );
  1449. }
  1450. //-----------------------------------------------------------------------------
  1451. // Purpose: Force the cannon to drop anything it's carrying
  1452. //-----------------------------------------------------------------------------
  1453. void CWeaponPhysCannon::ForceDrop( void )
  1454. {
  1455. CloseElements();
  1456. DetachObject();
  1457. StopEffects();
  1458. }
  1459. //-----------------------------------------------------------------------------
  1460. // Purpose: Drops its held entity if it matches the entity passed in
  1461. // Input : *pTarget -
  1462. // Output : Returns true on success, false on failure.
  1463. //-----------------------------------------------------------------------------
  1464. bool CWeaponPhysCannon::DropIfEntityHeld( CBaseEntity *pTarget )
  1465. {
  1466. if ( pTarget == NULL )
  1467. return false;
  1468. CBaseEntity *pHeld = m_grabController.GetAttached();
  1469. if ( pHeld == NULL )
  1470. return false;
  1471. if ( pHeld == pTarget )
  1472. {
  1473. ForceDrop();
  1474. return true;
  1475. }
  1476. return false;
  1477. }
  1478. //-----------------------------------------------------------------------------
  1479. // Purpose:
  1480. //-----------------------------------------------------------------------------
  1481. void CWeaponPhysCannon::Drop( const Vector &vecVelocity )
  1482. {
  1483. ForceDrop();
  1484. BaseClass::Drop( vecVelocity );
  1485. }
  1486. //-----------------------------------------------------------------------------
  1487. // Purpose:
  1488. //-----------------------------------------------------------------------------
  1489. bool CWeaponPhysCannon::CanHolster( void )
  1490. {
  1491. //Don't holster this weapon if we're holding onto something
  1492. if ( m_bActive )
  1493. return false;
  1494. return BaseClass::CanHolster();
  1495. };
  1496. //-----------------------------------------------------------------------------
  1497. // Purpose:
  1498. // Output : Returns true on success, false on failure.
  1499. //-----------------------------------------------------------------------------
  1500. bool CWeaponPhysCannon::Holster( CBaseCombatWeapon *pSwitchingTo )
  1501. {
  1502. //Don't holster this weapon if we're holding onto something
  1503. if ( m_bActive )
  1504. {
  1505. if ( m_grabController.GetAttached() == pSwitchingTo &&
  1506. GetOwner()->Weapon_OwnsThisType( pSwitchingTo->GetClassname(), pSwitchingTo->GetSubType()) )
  1507. {
  1508. }
  1509. else
  1510. {
  1511. return false;
  1512. }
  1513. }
  1514. CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
  1515. if ( pOwner )
  1516. {
  1517. pOwner->RumbleEffect( RUMBLE_PHYSCANNON_OPEN, 0, RUMBLE_FLAG_STOP );
  1518. }
  1519. ForceDrop();
  1520. return BaseClass::Holster( pSwitchingTo );
  1521. }
  1522. //-----------------------------------------------------------------------------
  1523. // Purpose:
  1524. //-----------------------------------------------------------------------------
  1525. void CWeaponPhysCannon::DryFire( void )
  1526. {
  1527. SendWeaponAnim( ACT_VM_PRIMARYATTACK );
  1528. WeaponSound( EMPTY );
  1529. CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
  1530. if ( pOwner )
  1531. {
  1532. pOwner->RumbleEffect( RUMBLE_PISTOL, 0, RUMBLE_FLAG_RESTART );
  1533. }
  1534. }
  1535. //-----------------------------------------------------------------------------
  1536. // Purpose:
  1537. //-----------------------------------------------------------------------------
  1538. void CWeaponPhysCannon::PrimaryFireEffect( void )
  1539. {
  1540. CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
  1541. if ( pOwner == NULL )
  1542. return;
  1543. pOwner->ViewPunch( QAngle(-6, random->RandomInt(-2,2) ,0) );
  1544. color32 white = { 245, 245, 255, 32 };
  1545. UTIL_ScreenFade( pOwner, white, 0.1f, 0.0f, FFADE_IN );
  1546. WeaponSound( SINGLE );
  1547. }
  1548. #define MAX_KNOCKBACK_FORCE 128
  1549. //-----------------------------------------------------------------------------
  1550. // Punt non-physics
  1551. //-----------------------------------------------------------------------------
  1552. void CWeaponPhysCannon::PuntNonVPhysics( CBaseEntity *pEntity, const Vector &forward, trace_t &tr )
  1553. {
  1554. float flDamage = 1.0f;
  1555. if ( FClassnameIs( pEntity, "func_breakable" ) )
  1556. {
  1557. CBreakable *pBreak = dynamic_cast <CBreakable *>(pEntity);
  1558. if ( pBreak && ( pBreak->GetMaterialType() == matGlass ) )
  1559. {
  1560. flDamage = physcannon_dmg_glass.GetFloat();
  1561. }
  1562. }
  1563. CTakeDamageInfo info;
  1564. info.SetAttacker( GetOwner() );
  1565. info.SetInflictor( this );
  1566. info.SetDamage( flDamage );
  1567. info.SetDamageType( DMG_CRUSH | DMG_PHYSGUN );
  1568. info.SetDamageForce( forward ); // Scale?
  1569. info.SetDamagePosition( tr.endpos );
  1570. pEntity->DispatchTraceAttack( info, forward, &tr );
  1571. ApplyMultiDamage();
  1572. //Explosion effect
  1573. DoEffect( EFFECT_LAUNCH, &tr.endpos );
  1574. PrimaryFireEffect();
  1575. SendWeaponAnim( ACT_VM_SECONDARYATTACK );
  1576. m_nChangeState = ELEMENT_STATE_CLOSED;
  1577. m_flElementDebounce = gpGlobals->curtime + 0.5f;
  1578. m_flCheckSuppressTime = gpGlobals->curtime + 0.25f;
  1579. }
  1580. //-----------------------------------------------------------------------------
  1581. // What happens when the physgun picks up something
  1582. //-----------------------------------------------------------------------------
  1583. void CWeaponPhysCannon::Physgun_OnPhysGunPickup( CBaseEntity *pEntity, CBasePlayer *pOwner, PhysGunPickup_t reason )
  1584. {
  1585. // If the target is debris, convert it to non-debris
  1586. if ( pEntity->GetCollisionGroup() == COLLISION_GROUP_DEBRIS )
  1587. {
  1588. // Interactive debris converts back to debris when it comes to rest
  1589. pEntity->SetCollisionGroup( COLLISION_GROUP_INTERACTIVE_DEBRIS );
  1590. }
  1591. float mass = 0.0f;
  1592. if( pEntity->VPhysicsGetObject() )
  1593. {
  1594. mass = pEntity->VPhysicsGetObject()->GetMass();
  1595. }
  1596. if( reason == PUNTED_BY_CANNON )
  1597. {
  1598. pOwner->RumbleEffect( RUMBLE_357, 0, RUMBLE_FLAGS_NONE );
  1599. RecordThrownObject( pEntity );
  1600. }
  1601. // Warn Alyx if the player is punting a car around.
  1602. if( hl2_episodic.GetBool() && mass > 250.0f )
  1603. {
  1604. CAI_BaseNPC **ppAIs = g_AI_Manager.AccessAIs();
  1605. int nAIs = g_AI_Manager.NumAIs();
  1606. for ( int i = 0; i < nAIs; i++ )
  1607. {
  1608. if( ppAIs[ i ]->Classify() == CLASS_PLAYER_ALLY_VITAL )
  1609. {
  1610. ppAIs[ i ]->DispatchInteraction( g_interactionPlayerPuntedHeavyObject, pEntity, pOwner );
  1611. }
  1612. }
  1613. }
  1614. Pickup_OnPhysGunPickup( pEntity, pOwner, reason );
  1615. }
  1616. //-----------------------------------------------------------------------------
  1617. // Punt vphysics
  1618. //-----------------------------------------------------------------------------
  1619. void CWeaponPhysCannon::PuntVPhysics( CBaseEntity *pEntity, const Vector &vecForward, trace_t &tr )
  1620. {
  1621. CTakeDamageInfo info;
  1622. Vector forward = vecForward;
  1623. info.SetAttacker( GetOwner() );
  1624. info.SetInflictor( this );
  1625. info.SetDamage( 0.0f );
  1626. info.SetDamageType( DMG_PHYSGUN );
  1627. pEntity->DispatchTraceAttack( info, forward, &tr );
  1628. ApplyMultiDamage();
  1629. CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
  1630. if ( Pickup_OnAttemptPhysGunPickup( pEntity, pOwner, PUNTED_BY_CANNON ) )
  1631. {
  1632. IPhysicsObject *pList[VPHYSICS_MAX_OBJECT_LIST_COUNT];
  1633. int listCount = pEntity->VPhysicsGetObjectList( pList, ARRAYSIZE(pList) );
  1634. if ( !listCount )
  1635. {
  1636. //FIXME: Do we want to do this if there's no physics object?
  1637. Physgun_OnPhysGunPickup( pEntity, pOwner, PUNTED_BY_CANNON );
  1638. DryFire();
  1639. return;
  1640. }
  1641. if( forward.z < 0 )
  1642. {
  1643. //reflect, but flatten the trajectory out a bit so it's easier to hit standing targets
  1644. forward.z *= -0.65f;
  1645. }
  1646. // NOTE: Do this first to enable motion (if disabled) - so forces will work
  1647. // Tell the object it's been punted
  1648. Physgun_OnPhysGunPickup( pEntity, pOwner, PUNTED_BY_CANNON );
  1649. // don't push vehicles that are attached to the world via fixed constraints
  1650. // they will just wiggle...
  1651. if ( (pList[0]->GetGameFlags() & FVPHYSICS_CONSTRAINT_STATIC) && pEntity->GetServerVehicle() )
  1652. {
  1653. forward.Init();
  1654. }
  1655. if ( !IsMegaPhysCannon() && !Pickup_ShouldPuntUseLaunchForces( pEntity, PHYSGUN_FORCE_PUNTED ) )
  1656. {
  1657. int i;
  1658. // limit mass to avoid punting REALLY huge things
  1659. float totalMass = 0;
  1660. for ( i = 0; i < listCount; i++ )
  1661. {
  1662. totalMass += pList[i]->GetMass();
  1663. }
  1664. float maxMass = 250;
  1665. IServerVehicle *pVehicle = pEntity->GetServerVehicle();
  1666. if ( pVehicle )
  1667. {
  1668. maxMass *= 2.5; // 625 for vehicles
  1669. }
  1670. float mass = MIN(totalMass, maxMass); // max 250kg of additional force
  1671. // Put some spin on the object
  1672. for ( i = 0; i < listCount; i++ )
  1673. {
  1674. const float hitObjectFactor = 0.5f;
  1675. const float otherObjectFactor = 1.0f - hitObjectFactor;
  1676. // Must be light enough
  1677. float ratio = pList[i]->GetMass() / totalMass;
  1678. if ( pList[i] == pEntity->VPhysicsGetObject() )
  1679. {
  1680. ratio += hitObjectFactor;
  1681. ratio = MIN(ratio,1.0f);
  1682. }
  1683. else
  1684. {
  1685. ratio *= otherObjectFactor;
  1686. }
  1687. pList[i]->ApplyForceCenter( forward * 15000.0f * ratio );
  1688. pList[i]->ApplyForceOffset( forward * mass * 600.0f * ratio, tr.endpos );
  1689. }
  1690. }
  1691. else
  1692. {
  1693. ApplyVelocityBasedForce( pEntity, vecForward, tr.endpos, PHYSGUN_FORCE_PUNTED );
  1694. }
  1695. }
  1696. // Add recoil
  1697. QAngle recoil = QAngle( random->RandomFloat( 1.0f, 2.0f ), random->RandomFloat( -1.0f, 1.0f ), 0 );
  1698. pOwner->ViewPunch( recoil );
  1699. //Explosion effect
  1700. DoEffect( EFFECT_LAUNCH, &tr.endpos );
  1701. PrimaryFireEffect();
  1702. SendWeaponAnim( ACT_VM_SECONDARYATTACK );
  1703. m_nChangeState = ELEMENT_STATE_CLOSED;
  1704. m_flElementDebounce = gpGlobals->curtime + 0.5f;
  1705. m_flCheckSuppressTime = gpGlobals->curtime + 0.25f;
  1706. // Don't allow the gun to regrab a thrown object!!
  1707. m_flNextSecondaryAttack = m_flNextPrimaryAttack = gpGlobals->curtime + 0.5f;
  1708. }
  1709. //-----------------------------------------------------------------------------
  1710. // Purpose: Applies velocity-based forces to throw the entity. This code is
  1711. // called from both punt and launch carried code.
  1712. // ASSUMES: that pEntity is a vphysics entity.
  1713. // Input : -
  1714. //-----------------------------------------------------------------------------
  1715. void CWeaponPhysCannon::ApplyVelocityBasedForce( CBaseEntity *pEntity, const Vector &forward, const Vector &vecHitPos, PhysGunForce_t reason )
  1716. {
  1717. // Get the launch velocity
  1718. Vector vVel = Pickup_PhysGunLaunchVelocity( pEntity, forward, reason );
  1719. // Get the launch angular impulse
  1720. AngularImpulse aVel = Pickup_PhysGunLaunchAngularImpulse( pEntity, reason );
  1721. // Get the physics object (MUST have one)
  1722. IPhysicsObject *pPhysicsObject = pEntity->VPhysicsGetObject();
  1723. if ( pPhysicsObject == NULL )
  1724. {
  1725. Assert( 0 );
  1726. return;
  1727. }
  1728. // Affect the object
  1729. CRagdollProp *pRagdoll = dynamic_cast<CRagdollProp*>( pEntity );
  1730. if ( pRagdoll == NULL )
  1731. {
  1732. #ifdef HL2_EPISODIC
  1733. // The jeep being punted needs special force overrides
  1734. if ( reason == PHYSGUN_FORCE_PUNTED && pEntity->GetServerVehicle() )
  1735. {
  1736. // We want the point to emanate low on the vehicle to move it along the ground, not to twist it
  1737. Vector vecFinalPos = vecHitPos;
  1738. vecFinalPos.z = pEntity->GetAbsOrigin().z;
  1739. pPhysicsObject->ApplyForceOffset( vVel, vecFinalPos );
  1740. }
  1741. else
  1742. {
  1743. pPhysicsObject->AddVelocity( &vVel, &aVel );
  1744. }
  1745. #else
  1746. pPhysicsObject->AddVelocity( &vVel, &aVel );
  1747. #endif // HL2_EPISODIC
  1748. }
  1749. else
  1750. {
  1751. Vector vTempVel;
  1752. AngularImpulse vTempAVel;
  1753. ragdoll_t *pRagdollPhys = pRagdoll->GetRagdoll( );
  1754. for ( int j = 0; j < pRagdollPhys->listCount; ++j )
  1755. {
  1756. pRagdollPhys->list[j].pObject->AddVelocity( &vVel, &aVel );
  1757. }
  1758. }
  1759. }
  1760. //-----------------------------------------------------------------------------
  1761. // Punt non-physics
  1762. //-----------------------------------------------------------------------------
  1763. void CWeaponPhysCannon::PuntRagdoll( CBaseEntity *pEntity, const Vector &vecForward, trace_t &tr )
  1764. {
  1765. CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
  1766. Pickup_OnPhysGunDrop( pEntity, pOwner, LAUNCHED_BY_CANNON );
  1767. CTakeDamageInfo info;
  1768. Vector forward = vecForward;
  1769. info.SetAttacker( GetOwner() );
  1770. info.SetInflictor( this );
  1771. info.SetDamage( 0.0f );
  1772. info.SetDamageType( DMG_PHYSGUN );
  1773. pEntity->DispatchTraceAttack( info, forward, &tr );
  1774. ApplyMultiDamage();
  1775. if ( Pickup_OnAttemptPhysGunPickup( pEntity, pOwner, PUNTED_BY_CANNON ) )
  1776. {
  1777. Physgun_OnPhysGunPickup( pEntity, pOwner, PUNTED_BY_CANNON );
  1778. if( forward.z < 0 )
  1779. {
  1780. //reflect, but flatten the trajectory out a bit so it's easier to hit standing targets
  1781. forward.z *= -0.65f;
  1782. }
  1783. Vector vVel = forward * 1500;
  1784. AngularImpulse aVel = Pickup_PhysGunLaunchAngularImpulse( pEntity, PHYSGUN_FORCE_PUNTED );
  1785. CRagdollProp *pRagdoll = dynamic_cast<CRagdollProp*>( pEntity );
  1786. ragdoll_t *pRagdollPhys = pRagdoll->GetRagdoll( );
  1787. int j;
  1788. for ( j = 0; j < pRagdollPhys->listCount; ++j )
  1789. {
  1790. pRagdollPhys->list[j].pObject->AddVelocity( &vVel, NULL );
  1791. }
  1792. }
  1793. // Add recoil
  1794. QAngle recoil = QAngle( random->RandomFloat( 1.0f, 2.0f ), random->RandomFloat( -1.0f, 1.0f ), 0 );
  1795. pOwner->ViewPunch( recoil );
  1796. //Explosion effect
  1797. DoEffect( EFFECT_LAUNCH, &tr.endpos );
  1798. PrimaryFireEffect();
  1799. SendWeaponAnim( ACT_VM_SECONDARYATTACK );
  1800. m_nChangeState = ELEMENT_STATE_CLOSED;
  1801. m_flElementDebounce = gpGlobals->curtime + 0.5f;
  1802. m_flCheckSuppressTime = gpGlobals->curtime + 0.25f;
  1803. // Don't allow the gun to regrab a thrown object!!
  1804. m_flNextSecondaryAttack = m_flNextPrimaryAttack = gpGlobals->curtime + 0.5f;
  1805. }
  1806. //-----------------------------------------------------------------------------
  1807. // Trace length
  1808. //-----------------------------------------------------------------------------
  1809. float CWeaponPhysCannon::TraceLength()
  1810. {
  1811. if ( !IsMegaPhysCannon() )
  1812. {
  1813. return physcannon_tracelength.GetFloat();
  1814. }
  1815. return physcannon_mega_tracelength.GetFloat();
  1816. }
  1817. //-----------------------------------------------------------------------------
  1818. // If there's any special rejection code you need to do per entity then do it here
  1819. // This is kinda nasty but I'd hate to move more physcannon related stuff into CBaseEntity
  1820. //-----------------------------------------------------------------------------
  1821. bool CWeaponPhysCannon::EntityAllowsPunts( CBaseEntity *pEntity )
  1822. {
  1823. if ( pEntity->HasSpawnFlags( SF_PHYSBOX_NEVER_PUNT ) )
  1824. {
  1825. CPhysBox *pPhysBox = dynamic_cast<CPhysBox*>(pEntity);
  1826. if ( pPhysBox != NULL )
  1827. {
  1828. if ( pPhysBox->HasSpawnFlags( SF_PHYSBOX_NEVER_PUNT ) )
  1829. {
  1830. return false;
  1831. }
  1832. }
  1833. }
  1834. if ( pEntity->HasSpawnFlags( SF_WEAPON_NO_PHYSCANNON_PUNT ) )
  1835. {
  1836. CBaseCombatWeapon *pWeapon = dynamic_cast<CBaseCombatWeapon*>(pEntity);
  1837. if ( pWeapon != NULL )
  1838. {
  1839. if ( pWeapon->HasSpawnFlags( SF_WEAPON_NO_PHYSCANNON_PUNT ) )
  1840. {
  1841. return false;
  1842. }
  1843. }
  1844. }
  1845. return true;
  1846. }
  1847. //-----------------------------------------------------------------------------
  1848. // Purpose:
  1849. //
  1850. // This mode is a toggle. Primary fire one time to pick up a physics object.
  1851. // With an object held, click primary fire again to drop object.
  1852. //-----------------------------------------------------------------------------
  1853. void CWeaponPhysCannon::PrimaryAttack( void )
  1854. {
  1855. if( m_flNextPrimaryAttack > gpGlobals->curtime )
  1856. return;
  1857. CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
  1858. if ( pOwner == NULL )
  1859. return;
  1860. if( m_bActive )
  1861. {
  1862. // Punch the object being held!!
  1863. Vector forward;
  1864. pOwner->EyeVectors( &forward );
  1865. // Validate the item is within punt range
  1866. CBaseEntity *pHeld = m_grabController.GetAttached();
  1867. Assert( pHeld != NULL );
  1868. if ( pHeld != NULL )
  1869. {
  1870. float heldDist = ( pHeld->WorldSpaceCenter() - pOwner->WorldSpaceCenter() ).Length();
  1871. if ( heldDist > physcannon_tracelength.GetFloat() )
  1872. {
  1873. // We can't punt this yet
  1874. DryFire();
  1875. return;
  1876. }
  1877. }
  1878. LaunchObject( forward, physcannon_maxforce.GetFloat() );
  1879. PrimaryFireEffect();
  1880. SendWeaponAnim( ACT_VM_SECONDARYATTACK );
  1881. return;
  1882. }
  1883. // If not active, just issue a physics punch in the world.
  1884. m_flNextPrimaryAttack = gpGlobals->curtime + 0.5f;
  1885. Vector forward;
  1886. pOwner->EyeVectors( &forward );
  1887. // NOTE: Notice we're *not* using the mega tracelength here
  1888. // when you have the mega cannon. Punting has shorter range.
  1889. Vector start, end;
  1890. start = pOwner->Weapon_ShootPosition();
  1891. float flPuntDistance = physcannon_tracelength.GetFloat();
  1892. VectorMA( start, flPuntDistance, forward, end );
  1893. trace_t tr;
  1894. UTIL_PhyscannonTraceHull( start, end, -Vector(8,8,8), Vector(8,8,8), pOwner, &tr );
  1895. bool bValid = true;
  1896. CBaseEntity *pEntity = tr.m_pEnt;
  1897. if ( tr.fraction == 1 || !tr.m_pEnt || tr.m_pEnt->IsEFlagSet( EFL_NO_PHYSCANNON_INTERACTION ) )
  1898. {
  1899. bValid = false;
  1900. }
  1901. else if ( (pEntity->GetMoveType() != MOVETYPE_VPHYSICS) && ( pEntity->m_takedamage == DAMAGE_NO ) )
  1902. {
  1903. bValid = false;
  1904. }
  1905. // If the entity we've hit is invalid, try a traceline instead
  1906. if ( !bValid )
  1907. {
  1908. UTIL_PhyscannonTraceLine( start, end, pOwner, &tr );
  1909. if ( tr.fraction == 1 || !tr.m_pEnt || tr.m_pEnt->IsEFlagSet( EFL_NO_PHYSCANNON_INTERACTION ) )
  1910. {
  1911. if( hl2_episodic.GetBool() )
  1912. {
  1913. // Try to find something in a very small cone.
  1914. CBaseEntity *pObject = FindObjectInCone( start, forward, physcannon_punt_cone.GetFloat() );
  1915. if( pObject )
  1916. {
  1917. // Trace to the object.
  1918. UTIL_PhyscannonTraceLine( start, pObject->WorldSpaceCenter(), pOwner, &tr );
  1919. if( tr.m_pEnt && tr.m_pEnt == pObject && !(pObject->IsEFlagSet(EFL_NO_PHYSCANNON_INTERACTION)) )
  1920. {
  1921. bValid = true;
  1922. pEntity = pObject;
  1923. }
  1924. }
  1925. }
  1926. }
  1927. else
  1928. {
  1929. bValid = true;
  1930. pEntity = tr.m_pEnt;
  1931. }
  1932. }
  1933. if ( ToPortalPlayer( pOwner )->IsHeldObjectOnOppositeSideOfPortal() )
  1934. {
  1935. CProp_Portal *pPortal = ToPortalPlayer( pOwner )->GetHeldObjectPortal();
  1936. UTIL_Portal_VectorTransform( pPortal->MatrixThisToLinked(), forward, forward );
  1937. }
  1938. if( !bValid )
  1939. {
  1940. DryFire();
  1941. return;
  1942. }
  1943. // See if we hit something
  1944. if ( pEntity->GetMoveType() != MOVETYPE_VPHYSICS )
  1945. {
  1946. if ( pEntity->m_takedamage == DAMAGE_NO )
  1947. {
  1948. DryFire();
  1949. return;
  1950. }
  1951. if( GetOwner()->IsPlayer() && !IsMegaPhysCannon() )
  1952. {
  1953. // Don't let the player zap any NPC's except regular antlions and headcrabs.
  1954. if( pEntity->IsNPC() && pEntity->Classify() != CLASS_HEADCRAB && !FClassnameIs(pEntity, "npc_antlion") )
  1955. {
  1956. DryFire();
  1957. return;
  1958. }
  1959. }
  1960. if ( IsMegaPhysCannon() )
  1961. {
  1962. if ( pEntity->IsNPC() && !pEntity->IsEFlagSet( EFL_NO_MEGAPHYSCANNON_RAGDOLL ) && pEntity->MyNPCPointer()->CanBecomeRagdoll() )
  1963. {
  1964. CTakeDamageInfo info( pOwner, pOwner, 1.0f, DMG_GENERIC );
  1965. CBaseEntity *pRagdoll = CreateServerRagdoll( pEntity->MyNPCPointer(), 0, info, COLLISION_GROUP_INTERACTIVE_DEBRIS, true );
  1966. PhysSetEntityGameFlags( pRagdoll, FVPHYSICS_NO_SELF_COLLISIONS );
  1967. pRagdoll->SetCollisionBounds( pEntity->CollisionProp()->OBBMins(), pEntity->CollisionProp()->OBBMaxs() );
  1968. // Necessary to cause it to do the appropriate death cleanup
  1969. CTakeDamageInfo ragdollInfo( pOwner, pOwner, 10000.0, DMG_PHYSGUN | DMG_REMOVENORAGDOLL );
  1970. pEntity->TakeDamage( ragdollInfo );
  1971. PuntRagdoll( pRagdoll, forward, tr );
  1972. return;
  1973. }
  1974. }
  1975. PuntNonVPhysics( pEntity, forward, tr );
  1976. }
  1977. else
  1978. {
  1979. if ( EntityAllowsPunts( pEntity) == false )
  1980. {
  1981. DryFire();
  1982. return;
  1983. }
  1984. if ( !IsMegaPhysCannon() )
  1985. {
  1986. if ( pEntity->VPhysicsIsFlesh( ) )
  1987. {
  1988. DryFire();
  1989. return;
  1990. }
  1991. PuntVPhysics( pEntity, forward, tr );
  1992. }
  1993. else
  1994. {
  1995. if ( dynamic_cast<CRagdollProp*>(pEntity) )
  1996. {
  1997. PuntRagdoll( pEntity, forward, tr );
  1998. }
  1999. else
  2000. {
  2001. PuntVPhysics( pEntity, forward, tr );
  2002. }
  2003. }
  2004. }
  2005. }
  2006. //-----------------------------------------------------------------------------
  2007. // Purpose: Click secondary attack whilst holding an object to hurl it.
  2008. //-----------------------------------------------------------------------------
  2009. void CWeaponPhysCannon::SecondaryAttack( void )
  2010. {
  2011. if ( m_flNextSecondaryAttack > gpGlobals->curtime )
  2012. return;
  2013. CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
  2014. if ( pOwner == NULL )
  2015. return;
  2016. // See if we should drop a held item
  2017. if ( ( m_bActive ) && ( pOwner->m_afButtonPressed & IN_ATTACK2 ) )
  2018. {
  2019. // Drop the held object
  2020. m_flNextPrimaryAttack = gpGlobals->curtime + 0.5;
  2021. m_flNextSecondaryAttack = gpGlobals->curtime + 0.5;
  2022. DetachObject();
  2023. DoEffect( EFFECT_READY );
  2024. SendWeaponAnim( ACT_VM_PRIMARYATTACK );
  2025. }
  2026. else
  2027. {
  2028. // Otherwise pick it up
  2029. FindObjectResult_t result = FindObject();
  2030. switch ( result )
  2031. {
  2032. case OBJECT_FOUND:
  2033. WeaponSound( SPECIAL1 );
  2034. SendWeaponAnim( ACT_VM_PRIMARYATTACK );
  2035. m_flNextSecondaryAttack = gpGlobals->curtime + 0.5f;
  2036. // We found an object. Debounce the button
  2037. m_nAttack2Debounce |= pOwner->m_nButtons;
  2038. break;
  2039. case OBJECT_NOT_FOUND:
  2040. m_flNextSecondaryAttack = gpGlobals->curtime + 0.1f;
  2041. CloseElements();
  2042. break;
  2043. case OBJECT_BEING_DETACHED:
  2044. m_flNextSecondaryAttack = gpGlobals->curtime + 0.01f;
  2045. break;
  2046. }
  2047. DoEffect( EFFECT_HOLDING );
  2048. }
  2049. }
  2050. //-----------------------------------------------------------------------------
  2051. // Purpose:
  2052. //-----------------------------------------------------------------------------
  2053. void CWeaponPhysCannon::WeaponIdle( void )
  2054. {
  2055. if ( HasWeaponIdleTimeElapsed() )
  2056. {
  2057. if ( m_bActive )
  2058. {
  2059. //Shake when holding an item
  2060. SendWeaponAnim( ACT_VM_RELOAD );
  2061. }
  2062. else
  2063. {
  2064. //Otherwise idle simply
  2065. SendWeaponAnim( ACT_VM_IDLE );
  2066. }
  2067. }
  2068. }
  2069. //-----------------------------------------------------------------------------
  2070. // Purpose:
  2071. // Input : *pObject -
  2072. //-----------------------------------------------------------------------------
  2073. bool CWeaponPhysCannon::AttachObject( CBaseEntity *pObject, const Vector &vPosition )
  2074. {
  2075. if ( m_bActive )
  2076. return false;
  2077. if ( CanPickupObject( pObject ) == false )
  2078. return false;
  2079. m_grabController.SetIgnorePitch( false );
  2080. m_grabController.SetAngleAlignment( 0 );
  2081. bool bKilledByGrab = false;
  2082. bool bIsMegaPhysCannon = IsMegaPhysCannon();
  2083. if ( bIsMegaPhysCannon )
  2084. {
  2085. if ( pObject->IsNPC() && !pObject->IsEFlagSet( EFL_NO_MEGAPHYSCANNON_RAGDOLL ) )
  2086. {
  2087. Assert( pObject->MyNPCPointer()->CanBecomeRagdoll() );
  2088. CTakeDamageInfo info( GetOwner(), GetOwner(), 1.0f, DMG_GENERIC );
  2089. CBaseEntity *pRagdoll = CreateServerRagdoll( pObject->MyNPCPointer(), 0, info, COLLISION_GROUP_INTERACTIVE_DEBRIS, true );
  2090. PhysSetEntityGameFlags( pRagdoll, FVPHYSICS_NO_SELF_COLLISIONS );
  2091. pRagdoll->SetCollisionBounds( pObject->CollisionProp()->OBBMins(), pObject->CollisionProp()->OBBMaxs() );
  2092. // Necessary to cause it to do the appropriate death cleanup
  2093. CTakeDamageInfo ragdollInfo( GetOwner(), GetOwner(), 10000.0, DMG_PHYSGUN | DMG_REMOVENORAGDOLL );
  2094. pObject->TakeDamage( ragdollInfo );
  2095. // Now we act on the ragdoll for the remainder of the time
  2096. pObject = pRagdoll;
  2097. bKilledByGrab = true;
  2098. }
  2099. }
  2100. IPhysicsObject *pPhysics = pObject->VPhysicsGetObject();
  2101. // Must be valid
  2102. if ( !pPhysics )
  2103. return false;
  2104. CPortal_Player *pOwner = ToPortalPlayer( GetOwner() );
  2105. m_bActive = true;
  2106. if( pOwner )
  2107. {
  2108. #ifdef HL2_EPISODIC
  2109. CBreakableProp *pProp = dynamic_cast< CBreakableProp * >( pObject );
  2110. if ( pProp && pProp->HasInteraction( PROPINTER_PHYSGUN_CREATE_FLARE ) )
  2111. {
  2112. pOwner->FlashlightTurnOff();
  2113. }
  2114. #endif
  2115. // NOTE: This can change the mass; so it must be done before max speed setting
  2116. Physgun_OnPhysGunPickup( pObject, pOwner, PICKED_UP_BY_CANNON );
  2117. }
  2118. // NOTE :This must happen after OnPhysGunPickup because that can change the mass
  2119. m_grabController.AttachEntity( pOwner, pObject, pPhysics, bIsMegaPhysCannon, vPosition, (!bKilledByGrab) );
  2120. if( pOwner )
  2121. {
  2122. #ifdef WIN32
  2123. // NVNT set the players constant force to simulate holding mass
  2124. HapticSetConstantForce(pOwner,clamp(m_grabController.GetLoadWeight()*0.05,1,5)*Vector(0,-1,0));
  2125. #endif
  2126. pOwner->EnableSprint( false );
  2127. float loadWeight = ( 1.0f - GetLoadPercentage() );
  2128. float maxSpeed = hl2_walkspeed.GetFloat() + ( ( hl2_normspeed.GetFloat() - hl2_walkspeed.GetFloat() ) * loadWeight );
  2129. //Msg( "Load perc: %f -- Movement speed: %f/%f\n", loadWeight, maxSpeed, hl2_normspeed.GetFloat() );
  2130. pOwner->SetMaxSpeed( maxSpeed );
  2131. }
  2132. // Don't drop again for a slight delay, in case they were pulling objects near them
  2133. m_flNextSecondaryAttack = gpGlobals->curtime + 0.4f;
  2134. DoEffect( EFFECT_HOLDING );
  2135. OpenElements();
  2136. if ( GetMotorSound() )
  2137. {
  2138. (CSoundEnvelopeController::GetController()).Play( GetMotorSound(), 0.0f, 50 );
  2139. (CSoundEnvelopeController::GetController()).SoundChangePitch( GetMotorSound(), 100, 0.5f );
  2140. (CSoundEnvelopeController::GetController()).SoundChangeVolume( GetMotorSound(), 0.8f, 0.5f );
  2141. }
  2142. return true;
  2143. }
  2144. void CWeaponPhysCannon::FindObjectTrace( CBasePlayer *pPlayer, trace_t *pTraceResult )
  2145. {
  2146. Vector forward;
  2147. pPlayer->EyeVectors( &forward );
  2148. // Setup our positions
  2149. Vector start = pPlayer->Weapon_ShootPosition();
  2150. float testLength = TraceLength() * 4.0f;
  2151. Vector end = start + forward * testLength;
  2152. if( IsMegaPhysCannon() && hl2_episodic.GetBool() )
  2153. {
  2154. Vector vecAutoAimDir = pPlayer->GetAutoaimVector( 1.0f, testLength );
  2155. end = start + vecAutoAimDir * testLength;
  2156. }
  2157. // Try to find an object by looking straight ahead
  2158. UTIL_PhyscannonTraceLine( start, end, pPlayer, pTraceResult );
  2159. // Try again with a hull trace
  2160. if ( !pTraceResult->DidHitNonWorldEntity() )
  2161. {
  2162. UTIL_PhyscannonTraceHull( start, end, -Vector(4,4,4), Vector(4,4,4), pPlayer, pTraceResult );
  2163. }
  2164. }
  2165. CWeaponPhysCannon::FindObjectResult_t CWeaponPhysCannon::FindObject( void )
  2166. {
  2167. CBasePlayer *pPlayer = ToBasePlayer( GetOwner() );
  2168. Assert( pPlayer );
  2169. if ( pPlayer == NULL )
  2170. return OBJECT_NOT_FOUND;
  2171. Vector forward;
  2172. pPlayer->EyeVectors( &forward );
  2173. // Setup our positions
  2174. Vector start = pPlayer->Weapon_ShootPosition();
  2175. float testLength = TraceLength() * 4.0f;
  2176. Vector end = start + forward * testLength;
  2177. trace_t tr;
  2178. FindObjectTrace( pPlayer, &tr );
  2179. CBaseEntity *pEntity = tr.m_pEnt ? tr.m_pEnt->GetRootMoveParent() : NULL;
  2180. bool bAttach = false;
  2181. bool bPull = false;
  2182. // If we hit something, pick it up or pull it
  2183. if ( ( tr.fraction != 1.0f ) && ( tr.m_pEnt ) && ( tr.m_pEnt->IsWorld() == false ) )
  2184. {
  2185. // Attempt to attach if within range
  2186. if ( tr.fraction <= 0.25f )
  2187. {
  2188. bAttach = true;
  2189. }
  2190. else if ( tr.fraction > 0.25f )
  2191. {
  2192. bPull = true;
  2193. }
  2194. }
  2195. // Find anything within a general cone in front
  2196. CBaseEntity *pConeEntity = NULL;
  2197. if ( !IsMegaPhysCannon() )
  2198. {
  2199. if (!bAttach && !bPull)
  2200. {
  2201. pConeEntity = FindObjectInCone( start, forward, physcannon_cone.GetFloat() );
  2202. }
  2203. }
  2204. else
  2205. {
  2206. pConeEntity = MegaPhysCannonFindObjectInCone( start, forward,
  2207. physcannon_cone.GetFloat(), physcannon_ball_cone.GetFloat(), bAttach || bPull );
  2208. }
  2209. if ( pConeEntity )
  2210. {
  2211. pEntity = pConeEntity;
  2212. // If the object is near, grab it. Else, pull it a bit.
  2213. if ( pEntity->WorldSpaceCenter().DistToSqr( start ) <= (testLength * testLength) )
  2214. {
  2215. bAttach = true;
  2216. }
  2217. else
  2218. {
  2219. bPull = true;
  2220. }
  2221. }
  2222. if ( CanPickupObject( pEntity ) == false )
  2223. {
  2224. CBaseEntity *pNewObject = Pickup_OnFailedPhysGunPickup( pEntity, start );
  2225. if ( pNewObject && CanPickupObject( pNewObject ) )
  2226. {
  2227. pEntity = pNewObject;
  2228. }
  2229. else
  2230. {
  2231. // Make a noise to signify we can't pick this up
  2232. if ( !m_flLastDenySoundPlayed )
  2233. {
  2234. m_flLastDenySoundPlayed = true;
  2235. WeaponSound( SPECIAL3 );
  2236. }
  2237. return OBJECT_NOT_FOUND;
  2238. }
  2239. }
  2240. // Check to see if the object is constrained + needs to be ripped off...
  2241. CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
  2242. if ( !Pickup_OnAttemptPhysGunPickup( pEntity, pOwner, PICKED_UP_BY_CANNON ) )
  2243. return OBJECT_BEING_DETACHED;
  2244. if ( bAttach )
  2245. {
  2246. return AttachObject( pEntity, tr.endpos ) ? OBJECT_FOUND : OBJECT_NOT_FOUND;
  2247. }
  2248. if ( !bPull )
  2249. return OBJECT_NOT_FOUND;
  2250. // FIXME: This needs to be run through the CanPickupObject logic
  2251. IPhysicsObject *pObj = pEntity->VPhysicsGetObject();
  2252. if ( !pObj )
  2253. return OBJECT_NOT_FOUND;
  2254. // If we're too far, simply start to pull the object towards us
  2255. Vector pullDir = start - pEntity->WorldSpaceCenter();
  2256. VectorNormalize( pullDir );
  2257. pullDir *= IsMegaPhysCannon() ? physcannon_mega_pullforce.GetFloat() : physcannon_pullforce.GetFloat();
  2258. float mass = PhysGetEntityMass( pEntity );
  2259. if ( mass < 50.0f )
  2260. {
  2261. pullDir *= (mass + 0.5) * (1/50.0f);
  2262. }
  2263. // Nudge it towards us
  2264. pObj->ApplyForceCenter( pullDir );
  2265. return OBJECT_NOT_FOUND;
  2266. }
  2267. //-----------------------------------------------------------------------------
  2268. //-----------------------------------------------------------------------------
  2269. CBaseEntity *CWeaponPhysCannon::MegaPhysCannonFindObjectInCone( const Vector &vecOrigin,
  2270. const Vector &vecDir, float flCone, float flCombineBallCone, bool bOnlyCombineBalls )
  2271. {
  2272. // Find the nearest physics-based item in a cone in front of me.
  2273. CBaseEntity *list[1024];
  2274. float flMaxDist = TraceLength() + 1.0;
  2275. float flNearestDist = flMaxDist;
  2276. bool bNearestIsCombineBall = bOnlyCombineBalls ? true : false;
  2277. Vector mins = vecOrigin - Vector( flNearestDist, flNearestDist, flNearestDist );
  2278. Vector maxs = vecOrigin + Vector( flNearestDist, flNearestDist, flNearestDist );
  2279. CBaseEntity *pNearest = NULL;
  2280. int count = UTIL_EntitiesInBox( list, 1024, mins, maxs, 0 );
  2281. for( int i = 0 ; i < count ; i++ )
  2282. {
  2283. if ( !list[ i ]->VPhysicsGetObject() )
  2284. continue;
  2285. bool bIsCombineBall = FClassnameIs( list[ i ], "prop_combine_ball" );
  2286. if ( !bIsCombineBall && bNearestIsCombineBall )
  2287. continue;
  2288. // Closer than other objects
  2289. Vector los;
  2290. VectorSubtract( list[ i ]->WorldSpaceCenter(), vecOrigin, los );
  2291. float flDist = VectorNormalize( los );
  2292. if ( !bIsCombineBall || bNearestIsCombineBall )
  2293. {
  2294. // Closer than other objects
  2295. if( flDist >= flNearestDist )
  2296. continue;
  2297. // Cull to the cone
  2298. if ( DotProduct( los, vecDir ) <= flCone )
  2299. continue;
  2300. }
  2301. else
  2302. {
  2303. // Close enough?
  2304. if ( flDist >= flMaxDist )
  2305. continue;
  2306. // Cull to the cone
  2307. if ( DotProduct( los, vecDir ) <= flCone )
  2308. continue;
  2309. // NOW: If it's either closer than nearest dist or within the ball cone, use it!
  2310. if ( (flDist > flNearestDist) && (DotProduct( los, vecDir ) <= flCombineBallCone) )
  2311. continue;
  2312. }
  2313. // Make sure it isn't occluded!
  2314. trace_t tr;
  2315. UTIL_PhyscannonTraceLine( vecOrigin, list[ i ]->WorldSpaceCenter(), GetOwner(), &tr );
  2316. if( tr.m_pEnt == list[ i ] )
  2317. {
  2318. flNearestDist = flDist;
  2319. pNearest = list[ i ];
  2320. bNearestIsCombineBall = bIsCombineBall;
  2321. }
  2322. }
  2323. return pNearest;
  2324. }
  2325. //-----------------------------------------------------------------------------
  2326. //-----------------------------------------------------------------------------
  2327. CBaseEntity *CWeaponPhysCannon::FindObjectInCone( const Vector &vecOrigin, const Vector &vecDir, float flCone )
  2328. {
  2329. // Find the nearest physics-based item in a cone in front of me.
  2330. CBaseEntity *list[256];
  2331. float flNearestDist = physcannon_tracelength.GetFloat() + 1.0; //Use regular distance.
  2332. Vector mins = vecOrigin - Vector( flNearestDist, flNearestDist, flNearestDist );
  2333. Vector maxs = vecOrigin + Vector( flNearestDist, flNearestDist, flNearestDist );
  2334. CBaseEntity *pNearest = NULL;
  2335. int count = UTIL_EntitiesInBox( list, 256, mins, maxs, 0 );
  2336. for( int i = 0 ; i < count ; i++ )
  2337. {
  2338. if ( !list[ i ]->VPhysicsGetObject() )
  2339. continue;
  2340. // Closer than other objects
  2341. Vector los = ( list[ i ]->WorldSpaceCenter() - vecOrigin );
  2342. float flDist = VectorNormalize( los );
  2343. if( flDist >= flNearestDist )
  2344. continue;
  2345. // Cull to the cone
  2346. if ( DotProduct( los, vecDir ) <= flCone )
  2347. continue;
  2348. // Make sure it isn't occluded!
  2349. trace_t tr;
  2350. UTIL_PhyscannonTraceLine( vecOrigin, list[ i ]->WorldSpaceCenter(), GetOwner(), &tr );
  2351. if( tr.m_pEnt == list[ i ] )
  2352. {
  2353. flNearestDist = flDist;
  2354. pNearest = list[ i ];
  2355. }
  2356. }
  2357. return pNearest;
  2358. }
  2359. //-----------------------------------------------------------------------------
  2360. //-----------------------------------------------------------------------------
  2361. bool CGrabController::UpdateObject( CBasePlayer *pPlayer, float flError )
  2362. {
  2363. CBaseEntity *pPenetratedEntity = m_PenetratedEntity.Get();
  2364. if( pPenetratedEntity )
  2365. {
  2366. //FindClosestPassableSpace( pPenetratedEntity, Vector( 0.0f, 0.0f, 1.0f ) );
  2367. IPhysicsObject *pPhysObject = pPenetratedEntity->VPhysicsGetObject();
  2368. if( pPhysObject )
  2369. pPhysObject->Wake();
  2370. m_PenetratedEntity = NULL; //assume we won
  2371. }
  2372. CBaseEntity *pEntity = GetAttached();
  2373. if ( !pEntity || ComputeError() > flError || pPlayer->GetGroundEntity() == pEntity || !pEntity->VPhysicsGetObject() )
  2374. {
  2375. return false;
  2376. }
  2377. //Adrian: Oops, our object became motion disabled, let go!
  2378. IPhysicsObject *pPhys = pEntity->VPhysicsGetObject();
  2379. if ( pPhys && pPhys->IsMoveable() == false )
  2380. {
  2381. return false;
  2382. }
  2383. Vector forward, right, up;
  2384. QAngle playerAngles = pPlayer->EyeAngles();
  2385. float pitch = AngleDistance(playerAngles.x,0);
  2386. if( !m_bAllowObjectOverhead )
  2387. {
  2388. playerAngles.x = clamp( pitch, -75, 75 );
  2389. }
  2390. else
  2391. {
  2392. playerAngles.x = clamp( pitch, -90, 75 );
  2393. }
  2394. AngleVectors( playerAngles, &forward, &right, &up );
  2395. if ( HL2GameRules()->MegaPhyscannonActive() )
  2396. {
  2397. Vector los = ( pEntity->WorldSpaceCenter() - pPlayer->Weapon_ShootPosition() );
  2398. VectorNormalize( los );
  2399. float flDot = DotProduct( los, forward );
  2400. //Let go of the item if we turn around too fast.
  2401. if ( flDot <= 0.35f )
  2402. return false;
  2403. }
  2404. Vector start = pPlayer->Weapon_ShootPosition();
  2405. // If the player is upside down then we need to hold the box closer to their feet.
  2406. if ( up.z < 0.0f )
  2407. start += pPlayer->GetViewOffset() * up.z;
  2408. if ( right.z < 0.0f )
  2409. start += pPlayer->GetViewOffset() * right.z;
  2410. CPortal_Player *pPortalPlayer = ToPortalPlayer( pPlayer );
  2411. // Find out if it's being held across a portal
  2412. bool bLookingAtHeldPortal = true;
  2413. CProp_Portal *pPortal = pPortalPlayer->GetHeldObjectPortal();
  2414. if ( !pPortal )
  2415. {
  2416. // If the portal is invalid make sure we don't try to hold it across the portal
  2417. pPortalPlayer->SetHeldObjectOnOppositeSideOfPortal( false );
  2418. }
  2419. if ( pPortalPlayer->IsHeldObjectOnOppositeSideOfPortal() )
  2420. {
  2421. Ray_t rayPortalTest;
  2422. rayPortalTest.Init( start, start + forward * 1024.0f );
  2423. // Check if we're looking at the portal we're holding through
  2424. if ( pPortal )
  2425. {
  2426. if ( UTIL_IntersectRayWithPortal( rayPortalTest, pPortal ) < 0.0f )
  2427. {
  2428. bLookingAtHeldPortal = false;
  2429. }
  2430. }
  2431. // If our end point hasn't gone into the portal yet we at least need to know what portal is in front of us
  2432. else
  2433. {
  2434. int iPortalCount = CProp_Portal_Shared::AllPortals.Count();
  2435. if( iPortalCount != 0 )
  2436. {
  2437. CProp_Portal **pPortals = CProp_Portal_Shared::AllPortals.Base();
  2438. float fMinDist = 2.0f;
  2439. for( int i = 0; i != iPortalCount; ++i )
  2440. {
  2441. CProp_Portal *pTempPortal = pPortals[i];
  2442. if( pTempPortal->m_bActivated &&
  2443. (pTempPortal->m_hLinkedPortal.Get() != NULL) )
  2444. {
  2445. float fDist = UTIL_IntersectRayWithPortal( rayPortalTest, pTempPortal );
  2446. if( (fDist >= 0.0f) && (fDist < fMinDist) )
  2447. {
  2448. fMinDist = fDist;
  2449. pPortal = pTempPortal;
  2450. }
  2451. }
  2452. }
  2453. }
  2454. }
  2455. }
  2456. else
  2457. {
  2458. pPortal = NULL;
  2459. }
  2460. QAngle qEntityAngles = pEntity->GetAbsAngles();
  2461. if ( pPortal )
  2462. {
  2463. // If the portal isn't linked we need to drop the object
  2464. if ( !pPortal->m_hLinkedPortal.Get() )
  2465. {
  2466. pPlayer->ForceDropOfCarriedPhysObjects();
  2467. return false;
  2468. }
  2469. UTIL_Portal_AngleTransform( pPortal->m_hLinkedPortal->MatrixThisToLinked(), qEntityAngles, qEntityAngles );
  2470. }
  2471. // Now clamp a sphere of object radius at end to the player's bbox
  2472. Vector radial = physcollision->CollideGetExtent( pPhys->GetCollide(), vec3_origin, qEntityAngles, -forward );
  2473. Vector player2d = pPlayer->CollisionProp()->OBBMaxs();
  2474. float playerRadius = player2d.Length2D();
  2475. float radius = playerRadius + radial.Length();
  2476. float distance = 24 + ( radius * 2.0f );
  2477. // Add the prop's distance offset
  2478. distance += m_flDistanceOffset;
  2479. Vector end = start + ( forward * distance );
  2480. trace_t tr;
  2481. CTraceFilterSkipTwoEntities traceFilter( pPlayer, pEntity, COLLISION_GROUP_NONE );
  2482. Ray_t ray;
  2483. ray.Init( start, end );
  2484. //enginetrace->TraceRay( ray, MASK_SOLID_BRUSHONLY, &traceFilter, &tr );
  2485. UTIL_Portal_TraceRay( ray, MASK_SOLID_BRUSHONLY, &traceFilter, &tr );
  2486. if ( tr.fraction < 0.5 )
  2487. {
  2488. end = start + forward * (radius*0.5f);
  2489. }
  2490. else if ( tr.fraction <= 1.0f )
  2491. {
  2492. end = start + forward * ( distance - radius );
  2493. }
  2494. Vector playerMins, playerMaxs, nearest;
  2495. pPlayer->CollisionProp()->WorldSpaceAABB( &playerMins, &playerMaxs );
  2496. Vector playerLine = pPlayer->CollisionProp()->WorldSpaceCenter();
  2497. CalcClosestPointOnLine( end, playerLine+Vector(0,0,playerMins.z), playerLine+Vector(0,0,playerMaxs.z), nearest, NULL );
  2498. if( !m_bAllowObjectOverhead )
  2499. {
  2500. Vector delta = end - nearest;
  2501. float len = VectorNormalize(delta);
  2502. if ( len < radius )
  2503. {
  2504. end = nearest + radius * delta;
  2505. }
  2506. }
  2507. QAngle angles = TransformAnglesFromPlayerSpace( m_attachedAnglesPlayerSpace, pPlayer );
  2508. //Show overlays of radius
  2509. if ( g_debug_physcannon.GetBool() )
  2510. {
  2511. NDebugOverlay::Box( end, -Vector( 2,2,2 ), Vector(2,2,2), 0, 255, 0, true, 0 );
  2512. NDebugOverlay::Box( GetAttached()->WorldSpaceCenter(),
  2513. -Vector( radius, radius, radius),
  2514. Vector( radius, radius, radius ),
  2515. 255, 0, 0,
  2516. true,
  2517. 0.0f );
  2518. }
  2519. // If it has a preferred orientation, update to ensure we're still oriented correctly.
  2520. Pickup_GetPreferredCarryAngles( pEntity, pPlayer, pPlayer->EntityToWorldTransform(), angles );
  2521. // We may be holding a prop that has preferred carry angles
  2522. if ( m_bHasPreferredCarryAngles )
  2523. {
  2524. matrix3x4_t tmp;
  2525. ComputePlayerMatrix( pPlayer, tmp );
  2526. angles = TransformAnglesToWorldSpace( m_vecPreferredCarryAngles, tmp );
  2527. }
  2528. matrix3x4_t attachedToWorld;
  2529. Vector offset;
  2530. AngleMatrix( angles, attachedToWorld );
  2531. VectorRotate( m_attachedPositionObjectSpace, attachedToWorld, offset );
  2532. // Translate hold position and angles across portal
  2533. if ( pPortalPlayer->IsHeldObjectOnOppositeSideOfPortal() )
  2534. {
  2535. CProp_Portal *pPortalLinked = pPortal->m_hLinkedPortal;
  2536. if ( pPortal && pPortal->m_bActivated && pPortalLinked != NULL )
  2537. {
  2538. Vector vTeleportedPosition;
  2539. QAngle qTeleportedAngles;
  2540. if ( !bLookingAtHeldPortal && ( start - pPortal->GetAbsOrigin() ).Length() > distance - radius )
  2541. {
  2542. // Pull the object through the portal
  2543. Vector vPortalLinkedForward;
  2544. pPortalLinked->GetVectors( &vPortalLinkedForward, NULL, NULL );
  2545. vTeleportedPosition = pPortalLinked->GetAbsOrigin() - vPortalLinkedForward * ( 1.0f + offset.Length() );
  2546. qTeleportedAngles = pPortalLinked->GetAbsAngles();
  2547. }
  2548. else
  2549. {
  2550. // Translate hold position and angles across the portal
  2551. VMatrix matThisToLinked = pPortal->MatrixThisToLinked();
  2552. UTIL_Portal_PointTransform( matThisToLinked, end - offset, vTeleportedPosition );
  2553. UTIL_Portal_AngleTransform( matThisToLinked, angles, qTeleportedAngles );
  2554. }
  2555. SetTargetPosition( vTeleportedPosition, qTeleportedAngles );
  2556. pPortalPlayer->SetHeldObjectPortal( pPortal );
  2557. }
  2558. else
  2559. {
  2560. pPlayer->ForceDropOfCarriedPhysObjects();
  2561. }
  2562. }
  2563. else
  2564. {
  2565. SetTargetPosition( end - offset, angles );
  2566. pPortalPlayer->SetHeldObjectPortal( NULL );
  2567. }
  2568. return true;
  2569. }
  2570. void CWeaponPhysCannon::UpdateObject( void )
  2571. {
  2572. CPortal_Player *pPlayer = ToPortalPlayer( GetOwner() );
  2573. Assert( pPlayer );
  2574. float flError = IsMegaPhysCannon() ? 18 : 12;
  2575. if ( !m_grabController.UpdateObject( pPlayer, flError ) )
  2576. {
  2577. pPlayer->SetHeldObjectOnOppositeSideOfPortal( false );
  2578. DetachObject();
  2579. return;
  2580. }
  2581. }
  2582. //-----------------------------------------------------------------------------
  2583. //-----------------------------------------------------------------------------
  2584. void CWeaponPhysCannon::DetachObject( bool playSound, bool wasLaunched )
  2585. {
  2586. if ( m_bActive == false )
  2587. return;
  2588. CPortal_Player *pOwner = (CPortal_Player *)ToBasePlayer( GetOwner() );
  2589. if( pOwner != NULL )
  2590. {
  2591. pOwner->EnableSprint( true );
  2592. pOwner->SetMaxSpeed( hl2_normspeed.GetFloat() );
  2593. pOwner->SetHeldObjectOnOppositeSideOfPortal( false );
  2594. if( wasLaunched )
  2595. {
  2596. pOwner->RumbleEffect( RUMBLE_357, 0, RUMBLE_FLAG_RESTART );
  2597. }
  2598. #ifdef WIN32
  2599. // NVNT clear constant force
  2600. HapticSetConstantForce(pOwner,Vector(0,0,0));
  2601. #endif
  2602. }
  2603. CBaseEntity *pObject = m_grabController.GetAttached();
  2604. m_grabController.DetachEntity( wasLaunched );
  2605. if ( pObject != NULL )
  2606. {
  2607. Pickup_OnPhysGunDrop( pObject, pOwner, wasLaunched ? LAUNCHED_BY_CANNON : DROPPED_BY_CANNON );
  2608. }
  2609. // Stop our looping sound
  2610. if ( GetMotorSound() )
  2611. {
  2612. (CSoundEnvelopeController::GetController()).SoundChangeVolume( GetMotorSound(), 0.0f, 1.0f );
  2613. (CSoundEnvelopeController::GetController()).SoundChangePitch( GetMotorSound(), 50, 1.0f );
  2614. }
  2615. m_bActive = false;
  2616. if ( playSound )
  2617. {
  2618. //Play the detach sound
  2619. WeaponSound( MELEE_MISS );
  2620. }
  2621. RecordThrownObject( pObject );
  2622. }
  2623. //-----------------------------------------------------------------------------
  2624. //-----------------------------------------------------------------------------
  2625. void CWeaponPhysCannon::ItemPreFrame()
  2626. {
  2627. BaseClass::ItemPreFrame();
  2628. CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
  2629. if ( pOwner == NULL )
  2630. return;
  2631. m_flElementPosition = UTIL_Approach( m_flElementDestination, m_flElementPosition, 0.1f );
  2632. CBaseViewModel *vm = pOwner->GetViewModel();
  2633. if ( vm != NULL )
  2634. {
  2635. vm->SetPoseParameter( "active", m_flElementPosition );
  2636. }
  2637. // Update the object if the weapon is switched on.
  2638. if( m_bActive )
  2639. {
  2640. UpdateObject();
  2641. }
  2642. if( gpGlobals->curtime >= m_flTimeNextObjectPurge )
  2643. {
  2644. PurgeThrownObjects();
  2645. m_flTimeNextObjectPurge = gpGlobals->curtime + 0.5f;
  2646. }
  2647. }
  2648. //-----------------------------------------------------------------------------
  2649. // Purpose:
  2650. //-----------------------------------------------------------------------------
  2651. void CWeaponPhysCannon::CheckForTarget( void )
  2652. {
  2653. //See if we're suppressing this
  2654. if ( m_flCheckSuppressTime > gpGlobals->curtime )
  2655. return;
  2656. // holstered
  2657. if ( IsEffectActive( EF_NODRAW ) )
  2658. return;
  2659. CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
  2660. if ( pOwner == NULL )
  2661. return;
  2662. if ( m_bActive )
  2663. return;
  2664. trace_t tr;
  2665. FindObjectTrace( pOwner, &tr );
  2666. if ( ( tr.fraction != 1.0f ) && ( tr.m_pEnt != NULL ) )
  2667. {
  2668. float dist = (tr.endpos - tr.startpos).Length();
  2669. if ( dist <= TraceLength() )
  2670. {
  2671. // FIXME: Try just having the elements always open when pointed at a physics object
  2672. if ( CanPickupObject( tr.m_pEnt ) || Pickup_ForcePhysGunOpen( tr.m_pEnt, pOwner ) )
  2673. // if ( ( tr.m_pEnt->VPhysicsGetObject() != NULL ) && ( tr.m_pEnt->GetMoveType() == MOVETYPE_VPHYSICS ) )
  2674. {
  2675. m_nChangeState = ELEMENT_STATE_NONE;
  2676. OpenElements();
  2677. return;
  2678. }
  2679. }
  2680. }
  2681. // Close the elements after a delay to prevent overact state switching
  2682. if ( ( m_flElementDebounce < gpGlobals->curtime ) && ( m_nChangeState == ELEMENT_STATE_NONE ) )
  2683. {
  2684. m_nChangeState = ELEMENT_STATE_CLOSED;
  2685. m_flElementDebounce = gpGlobals->curtime + 0.5f;
  2686. }
  2687. }
  2688. //-----------------------------------------------------------------------------
  2689. // Begin upgrading!
  2690. //-----------------------------------------------------------------------------
  2691. void CWeaponPhysCannon::BeginUpgrade()
  2692. {
  2693. if ( IsMegaPhysCannon() )
  2694. return;
  2695. if ( m_bIsCurrentlyUpgrading )
  2696. return;
  2697. SetSequence( SelectWeightedSequence( ACT_PHYSCANNON_UPGRADE ) );
  2698. ResetSequenceInfo();
  2699. m_bIsCurrentlyUpgrading = true;
  2700. SetContextThink( &CWeaponPhysCannon::WaitForUpgradeThink, gpGlobals->curtime + 6.0f, s_pWaitForUpgradeContext );
  2701. EmitSound( "WeaponDissolve.Charge" );
  2702. // Bloat our bounds
  2703. CollisionProp()->UseTriggerBounds( true, 32.0f );
  2704. // Turn on the new skin
  2705. m_nSkin = MEGACANNON_SKIN;
  2706. }
  2707. //-----------------------------------------------------------------------------
  2708. // Wait until we're done upgrading
  2709. //-----------------------------------------------------------------------------
  2710. void CWeaponPhysCannon::WaitForUpgradeThink()
  2711. {
  2712. Assert( m_bIsCurrentlyUpgrading );
  2713. StudioFrameAdvance();
  2714. if ( !IsActivityFinished() )
  2715. {
  2716. SetContextThink( &CWeaponPhysCannon::WaitForUpgradeThink, gpGlobals->curtime + 0.1f, s_pWaitForUpgradeContext );
  2717. return;
  2718. }
  2719. if ( !GlobalEntity_IsInTable( "super_phys_gun" ) )
  2720. {
  2721. GlobalEntity_Add( MAKE_STRING("super_phys_gun"), gpGlobals->mapname, GLOBAL_ON );
  2722. }
  2723. else
  2724. {
  2725. GlobalEntity_SetState( MAKE_STRING("super_phys_gun"), GLOBAL_ON );
  2726. }
  2727. m_bIsCurrentlyUpgrading = false;
  2728. // This is necessary to get the effects to look different
  2729. DestroyEffects();
  2730. // HACK: Hacky notification back to the level that we've finish upgrading
  2731. CBaseEntity *pEnt = gEntList.FindEntityByName( NULL, "script_physcannon_upgrade" );
  2732. if ( pEnt )
  2733. {
  2734. variant_t emptyVariant;
  2735. pEnt->AcceptInput( "Trigger", this, this, emptyVariant, 0 );
  2736. }
  2737. StopSound( "WeaponDissolve.Charge" );
  2738. // Re-enable weapon pickup
  2739. AddSolidFlags( FSOLID_TRIGGER );
  2740. SetContextThink( NULL, gpGlobals->curtime, s_pWaitForUpgradeContext );
  2741. }
  2742. //-----------------------------------------------------------------------------
  2743. // Purpose:
  2744. //-----------------------------------------------------------------------------
  2745. void CWeaponPhysCannon::DoEffectIdle( void )
  2746. {
  2747. if ( IsEffectActive( EF_NODRAW ) )
  2748. {
  2749. StopEffects();
  2750. return;
  2751. }
  2752. CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
  2753. if ( pOwner == NULL )
  2754. return;
  2755. if ( m_bPhyscannonState != IsMegaPhysCannon() )
  2756. {
  2757. DestroyEffects();
  2758. StartEffects();
  2759. m_bPhyscannonState = IsMegaPhysCannon();
  2760. //This means we just switched to regular physcannon this frame.
  2761. if ( m_bPhyscannonState == false )
  2762. {
  2763. EmitSound( "Weapon_Physgun.Off" );
  2764. #ifdef HL2_EPISODIC
  2765. ForceDrop();
  2766. CHL2_Player *pPlayer = dynamic_cast<CHL2_Player*>( pOwner );
  2767. if ( pPlayer )
  2768. {
  2769. pPlayer->StartArmorReduction();
  2770. }
  2771. #endif
  2772. CCitadelEnergyCore *pCore = static_cast<CCitadelEnergyCore*>( CreateEntityByName( "env_citadel_energy_core" ) );
  2773. if ( pCore == NULL )
  2774. return;
  2775. CBaseAnimating *pBeamEnt = pOwner->GetViewModel();
  2776. if ( pBeamEnt )
  2777. {
  2778. int iAttachment = pBeamEnt->LookupAttachment( "muzzle" );
  2779. Vector vOrigin;
  2780. QAngle vAngle;
  2781. pBeamEnt->GetAttachment( iAttachment, vOrigin, vAngle );
  2782. pCore->SetAbsOrigin( vOrigin );
  2783. pCore->SetAbsAngles( vAngle );
  2784. DispatchSpawn( pCore );
  2785. pCore->Activate();
  2786. pCore->SetParent( pBeamEnt, iAttachment );
  2787. pCore->SetScale( 2.5f );
  2788. variant_t variant;
  2789. variant.SetFloat( 1.0f );
  2790. g_EventQueue.AddEvent( pCore, "StartDischarge", 0, pOwner, pOwner );
  2791. g_EventQueue.AddEvent( pCore, "Stop", variant, 1, pOwner, pOwner );
  2792. pCore->SetThink ( &CCitadelEnergyCore::SUB_Remove );
  2793. pCore->SetNextThink( gpGlobals->curtime + 10.0f );
  2794. m_nSkin = 0;
  2795. }
  2796. }
  2797. }
  2798. float flScaleFactor = SpriteScaleFactor();
  2799. // Flicker the end sprites
  2800. if ( ( m_hEndSprites[0] != NULL ) && ( m_hEndSprites[1] != NULL ) )
  2801. {
  2802. //Make the end points flicker as fast as possible
  2803. //FIXME: Make this a property of the CSprite class!
  2804. for ( int i = 0; i < 2; i++ )
  2805. {
  2806. m_hEndSprites[i]->SetBrightness( random->RandomInt( 200, 255 ) );
  2807. m_hEndSprites[i]->SetScale( random->RandomFloat( 0.1, 0.15 ) * flScaleFactor );
  2808. }
  2809. }
  2810. // Flicker the glow sprites
  2811. for ( int i = 0; i < NUM_SPRITES; i++ )
  2812. {
  2813. if ( m_hGlowSprites[i] == NULL )
  2814. continue;
  2815. if ( IsMegaPhysCannon() )
  2816. {
  2817. m_hGlowSprites[i]->SetBrightness( random->RandomInt( 32, 48 ) );
  2818. m_hGlowSprites[i]->SetScale( random->RandomFloat( 0.15, 0.2 ) * flScaleFactor );
  2819. }
  2820. else
  2821. {
  2822. m_hGlowSprites[i]->SetBrightness( random->RandomInt( 16, 24 ) );
  2823. m_hGlowSprites[i]->SetScale( random->RandomFloat( 0.3, 0.35 ) * flScaleFactor );
  2824. }
  2825. }
  2826. // Only do these effects on the mega-cannon
  2827. if ( IsMegaPhysCannon() )
  2828. {
  2829. // Randomly arc between the elements and core
  2830. if ( random->RandomInt( 0, 100 ) == 0 && !engine->IsPaused() )
  2831. {
  2832. CBeam *pBeam = CBeam::BeamCreate( MEGACANNON_BEAM_SPRITE, 1 );
  2833. CBaseEntity *pBeamEnt = pOwner->GetViewModel();
  2834. pBeam->EntsInit( pBeamEnt, pBeamEnt );
  2835. int startAttachment;
  2836. int sprite;
  2837. if ( random->RandomInt( 0, 1 ) )
  2838. {
  2839. startAttachment = LookupAttachment( "fork1t" );
  2840. sprite = 0;
  2841. }
  2842. else
  2843. {
  2844. startAttachment = LookupAttachment( "fork2t" );
  2845. sprite = 1;
  2846. }
  2847. int endAttachment = 1;
  2848. pBeam->SetStartAttachment( startAttachment );
  2849. pBeam->SetEndAttachment( endAttachment );
  2850. pBeam->SetNoise( random->RandomFloat( 8.0f, 16.0f ) );
  2851. pBeam->SetColor( 255, 255, 255 );
  2852. pBeam->SetScrollRate( 25 );
  2853. pBeam->SetBrightness( 128 );
  2854. pBeam->SetWidth( 1 );
  2855. pBeam->SetEndWidth( random->RandomFloat( 2, 8 ) );
  2856. float lifetime = random->RandomFloat( 0.2f, 0.4f );
  2857. pBeam->LiveForTime( lifetime );
  2858. if ( m_hEndSprites[sprite] != NULL )
  2859. {
  2860. // Turn on the sprite for awhile
  2861. m_hEndSprites[sprite]->TurnOn();
  2862. m_flEndSpritesOverride[sprite] = gpGlobals->curtime + lifetime;
  2863. EmitSound( "Weapon_MegaPhysCannon.ChargeZap" );
  2864. }
  2865. }
  2866. if ( m_hCenterSprite != NULL )
  2867. {
  2868. if ( m_EffectState == EFFECT_HOLDING )
  2869. {
  2870. m_hCenterSprite->SetBrightness( random->RandomInt( 32, 64 ) );
  2871. m_hCenterSprite->SetScale( random->RandomFloat( 0.2, 0.25 ) * flScaleFactor );
  2872. }
  2873. else
  2874. {
  2875. m_hCenterSprite->SetBrightness( random->RandomInt( 32, 64 ) );
  2876. m_hCenterSprite->SetScale( random->RandomFloat( 0.125, 0.15 ) * flScaleFactor );
  2877. }
  2878. }
  2879. if ( m_hBlastSprite != NULL )
  2880. {
  2881. if ( m_EffectState == EFFECT_HOLDING )
  2882. {
  2883. m_hBlastSprite->SetBrightness( random->RandomInt( 125, 150 ) );
  2884. m_hBlastSprite->SetScale( random->RandomFloat( 0.125, 0.15 ) * flScaleFactor );
  2885. }
  2886. else
  2887. {
  2888. m_hBlastSprite->SetBrightness( random->RandomInt( 32, 64 ) );
  2889. m_hBlastSprite->SetScale( random->RandomFloat( 0.075, 0.15 ) * flScaleFactor );
  2890. }
  2891. }
  2892. }
  2893. }
  2894. //-----------------------------------------------------------------------------
  2895. // Purpose: Update our idle effects even when deploying
  2896. //-----------------------------------------------------------------------------
  2897. void CWeaponPhysCannon::ItemBusyFrame( void )
  2898. {
  2899. DoEffectIdle();
  2900. BaseClass::ItemBusyFrame();
  2901. }
  2902. //-----------------------------------------------------------------------------
  2903. //-----------------------------------------------------------------------------
  2904. void CWeaponPhysCannon::ItemPostFrame()
  2905. {
  2906. CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
  2907. if ( pOwner == NULL )
  2908. {
  2909. // We found an object. Debounce the button
  2910. m_nAttack2Debounce = 0;
  2911. return;
  2912. }
  2913. //Check for object in pickup range
  2914. if ( m_bActive == false )
  2915. {
  2916. CheckForTarget();
  2917. if ( ( m_flElementDebounce < gpGlobals->curtime ) && ( m_nChangeState != ELEMENT_STATE_NONE ) )
  2918. {
  2919. if ( m_nChangeState == ELEMENT_STATE_OPEN )
  2920. {
  2921. OpenElements();
  2922. }
  2923. else if ( m_nChangeState == ELEMENT_STATE_CLOSED )
  2924. {
  2925. CloseElements();
  2926. }
  2927. m_nChangeState = ELEMENT_STATE_NONE;
  2928. }
  2929. }
  2930. // NOTE: Attack2 will be considered to be pressed until the first item is picked up.
  2931. int nAttack2Mask = pOwner->m_nButtons & (~m_nAttack2Debounce);
  2932. if ( nAttack2Mask & IN_ATTACK2 )
  2933. {
  2934. SecondaryAttack();
  2935. }
  2936. else
  2937. {
  2938. // Reset our debouncer
  2939. m_flLastDenySoundPlayed = false;
  2940. if ( m_bActive == false )
  2941. {
  2942. DoEffect( EFFECT_READY );
  2943. }
  2944. }
  2945. if (( pOwner->m_nButtons & IN_ATTACK2 ) == 0 )
  2946. {
  2947. m_nAttack2Debounce = 0;
  2948. }
  2949. if ( pOwner->m_nButtons & IN_ATTACK )
  2950. {
  2951. PrimaryAttack();
  2952. }
  2953. else
  2954. {
  2955. WeaponIdle();
  2956. }
  2957. if ( hl2_episodic.GetBool() == true )
  2958. {
  2959. if ( IsMegaPhysCannon() )
  2960. {
  2961. if ( !( pOwner->m_nButtons & IN_ATTACK ) )
  2962. {
  2963. m_flNextPrimaryAttack = gpGlobals->curtime;
  2964. }
  2965. }
  2966. }
  2967. // Update our idle effects (flickers, etc)
  2968. DoEffectIdle();
  2969. }
  2970. //-----------------------------------------------------------------------------
  2971. //-----------------------------------------------------------------------------
  2972. #define PHYSCANNON_DANGER_SOUND_RADIUS 128
  2973. void CWeaponPhysCannon::LaunchObject( const Vector &vecDir, float flForce )
  2974. {
  2975. // FIRE!!!
  2976. if( m_grabController.GetAttached() )
  2977. {
  2978. CBaseEntity *pObject = m_grabController.GetAttached();
  2979. gamestats->Event_Punted( pObject );
  2980. DetachObject( false, true );
  2981. // Trace ahead a bit and make a chain of danger sounds ahead of the phys object
  2982. // to scare potential targets
  2983. trace_t tr;
  2984. Vector vecStart = pObject->GetAbsOrigin();
  2985. Vector vecSpot;
  2986. int iLength;
  2987. int i;
  2988. UTIL_TraceLine( vecStart, vecStart + vecDir * flForce, MASK_SHOT, pObject, COLLISION_GROUP_NONE, &tr );
  2989. iLength = ( tr.startpos - tr.endpos ).Length();
  2990. vecSpot = vecStart + vecDir * PHYSCANNON_DANGER_SOUND_RADIUS;
  2991. for( i = PHYSCANNON_DANGER_SOUND_RADIUS ; i < iLength ; i += PHYSCANNON_DANGER_SOUND_RADIUS )
  2992. {
  2993. CSoundEnt::InsertSound( SOUND_PHYSICS_DANGER, vecSpot, PHYSCANNON_DANGER_SOUND_RADIUS, 0.5, pObject );
  2994. vecSpot = vecSpot + ( vecDir * PHYSCANNON_DANGER_SOUND_RADIUS );
  2995. }
  2996. // Launch
  2997. ApplyVelocityBasedForce( pObject, vecDir, tr.endpos, PHYSGUN_FORCE_LAUNCHED );
  2998. // Don't allow the gun to regrab a thrown object!!
  2999. m_flNextSecondaryAttack = m_flNextPrimaryAttack = gpGlobals->curtime + 0.5;
  3000. Vector center = pObject->WorldSpaceCenter();
  3001. //Do repulse effect
  3002. DoEffect( EFFECT_LAUNCH, &center );
  3003. }
  3004. // Stop our looping sound
  3005. if ( GetMotorSound() )
  3006. {
  3007. (CSoundEnvelopeController::GetController()).SoundChangeVolume( GetMotorSound(), 0.0f, 1.0f );
  3008. (CSoundEnvelopeController::GetController()).SoundChangePitch( GetMotorSound(), 50, 1.0f );
  3009. }
  3010. //Close the elements and suppress checking for a bit
  3011. m_nChangeState = ELEMENT_STATE_CLOSED;
  3012. m_flElementDebounce = gpGlobals->curtime + 0.1f;
  3013. m_flCheckSuppressTime = gpGlobals->curtime + 0.25f;
  3014. }
  3015. //-----------------------------------------------------------------------------
  3016. // Purpose:
  3017. // Input : *pTarget -
  3018. // Output : Returns true on success, false on failure.
  3019. //-----------------------------------------------------------------------------
  3020. bool CWeaponPhysCannon::CanPickupObject( CBaseEntity *pTarget )
  3021. {
  3022. if ( pTarget == NULL )
  3023. return false;
  3024. if ( pTarget->GetBaseAnimating() && pTarget->GetBaseAnimating()->IsDissolving() )
  3025. return false;
  3026. if ( pTarget->HasSpawnFlags( SF_PHYSBOX_ALWAYS_PICK_UP ) || pTarget->HasSpawnFlags( SF_PHYSBOX_NEVER_PICK_UP ) )
  3027. {
  3028. // It may seem strange to check this spawnflag before we know the class of this object, since the
  3029. // spawnflag only applies to func_physbox, but it can act as a filter of sorts to reduce the number
  3030. // of irrelevant entities that fall through to this next casting check, which is slower.
  3031. CPhysBox *pPhysBox = dynamic_cast<CPhysBox*>(pTarget);
  3032. if ( pPhysBox != NULL )
  3033. {
  3034. if ( pTarget->HasSpawnFlags( SF_PHYSBOX_NEVER_PICK_UP ) )
  3035. return false;
  3036. else
  3037. return true;
  3038. }
  3039. }
  3040. if ( pTarget->HasSpawnFlags(SF_PHYSPROP_ALWAYS_PICK_UP) )
  3041. {
  3042. // It may seem strange to check this spawnflag before we know the class of this object, since the
  3043. // spawnflag only applies to func_physbox, but it can act as a filter of sorts to reduce the number
  3044. // of irrelevant entities that fall through to this next casting check, which is slower.
  3045. CPhysicsProp *pPhysProp = dynamic_cast<CPhysicsProp*>(pTarget);
  3046. if ( pPhysProp != NULL )
  3047. return true;
  3048. }
  3049. if ( pTarget->IsEFlagSet( EFL_NO_PHYSCANNON_INTERACTION ) )
  3050. return false;
  3051. CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
  3052. if ( pOwner && pOwner->GetGroundEntity() == pTarget )
  3053. return false;
  3054. if ( !IsMegaPhysCannon() )
  3055. {
  3056. if ( pTarget->VPhysicsIsFlesh( ) )
  3057. return false;
  3058. return CBasePlayer::CanPickupObject( pTarget, physcannon_maxmass.GetFloat(), 0 );
  3059. }
  3060. if ( pTarget->IsNPC() && pTarget->MyNPCPointer()->CanBecomeRagdoll() )
  3061. return true;
  3062. if ( dynamic_cast<CRagdollProp*>(pTarget) )
  3063. return true;
  3064. return CBasePlayer::CanPickupObject( pTarget, 0, 0 );
  3065. }
  3066. //-----------------------------------------------------------------------------
  3067. // Purpose:
  3068. //-----------------------------------------------------------------------------
  3069. void CWeaponPhysCannon::OpenElements( void )
  3070. {
  3071. if ( m_bOpen )
  3072. return;
  3073. WeaponSound( SPECIAL2 );
  3074. CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
  3075. if ( pOwner == NULL )
  3076. return;
  3077. if( !IsMegaPhysCannon() )
  3078. {
  3079. pOwner->RumbleEffect(RUMBLE_PHYSCANNON_OPEN, 0, RUMBLE_FLAG_RESTART|RUMBLE_FLAG_LOOP);
  3080. }
  3081. if ( m_flElementPosition < 0.0f )
  3082. m_flElementPosition = 0.0f;
  3083. m_flElementDestination = 1.0f;
  3084. SendWeaponAnim( ACT_VM_IDLE );
  3085. m_bOpen = true;
  3086. DoEffect( EFFECT_READY );
  3087. }
  3088. //-----------------------------------------------------------------------------
  3089. // Purpose:
  3090. //-----------------------------------------------------------------------------
  3091. void CWeaponPhysCannon::CloseElements( void )
  3092. {
  3093. // The mega cannon cannot be closed!
  3094. if ( IsMegaPhysCannon() )
  3095. {
  3096. OpenElements();
  3097. return;
  3098. }
  3099. if ( m_bOpen == false )
  3100. return;
  3101. WeaponSound( MELEE_HIT );
  3102. CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
  3103. if ( pOwner == NULL )
  3104. return;
  3105. pOwner->RumbleEffect(RUMBLE_PHYSCANNON_OPEN, 0, RUMBLE_FLAG_STOP);
  3106. if ( m_flElementPosition > 1.0f )
  3107. m_flElementPosition = 1.0f;
  3108. m_flElementDestination = 0.0f;
  3109. SendWeaponAnim( ACT_VM_IDLE );
  3110. m_bOpen = false;
  3111. if ( GetMotorSound() )
  3112. {
  3113. (CSoundEnvelopeController::GetController()).SoundChangeVolume( GetMotorSound(), 0.0f, 1.0f );
  3114. (CSoundEnvelopeController::GetController()).SoundChangePitch( GetMotorSound(), 50, 1.0f );
  3115. }
  3116. DoEffect( EFFECT_CLOSED );
  3117. }
  3118. #define PHYSCANNON_MAX_MASS 500
  3119. //-----------------------------------------------------------------------------
  3120. // Purpose:
  3121. // Output : float
  3122. //-----------------------------------------------------------------------------
  3123. float CWeaponPhysCannon::GetLoadPercentage( void )
  3124. {
  3125. float loadWeight = m_grabController.GetLoadWeight();
  3126. loadWeight /= physcannon_maxmass.GetFloat();
  3127. loadWeight = clamp( loadWeight, 0.0f, 1.0f );
  3128. return loadWeight;
  3129. }
  3130. //-----------------------------------------------------------------------------
  3131. // Purpose:
  3132. // Output : CSoundPatch
  3133. //-----------------------------------------------------------------------------
  3134. CSoundPatch *CWeaponPhysCannon::GetMotorSound( void )
  3135. {
  3136. if ( m_sndMotor == NULL )
  3137. {
  3138. CPASAttenuationFilter filter( this );
  3139. if ( IsMegaPhysCannon() )
  3140. {
  3141. m_sndMotor = (CSoundEnvelopeController::GetController()).SoundCreate( filter, entindex(), CHAN_STATIC, "Weapon_MegaPhysCannon.HoldSound", ATTN_NORM );
  3142. }
  3143. else
  3144. {
  3145. m_sndMotor = (CSoundEnvelopeController::GetController()).SoundCreate( filter, entindex(), CHAN_STATIC, "Weapon_PhysCannon.HoldSound", ATTN_NORM );
  3146. }
  3147. }
  3148. return m_sndMotor;
  3149. }
  3150. //-----------------------------------------------------------------------------
  3151. // Shuts down sounds
  3152. //-----------------------------------------------------------------------------
  3153. void CWeaponPhysCannon::StopLoopingSounds()
  3154. {
  3155. if ( m_sndMotor != NULL )
  3156. {
  3157. (CSoundEnvelopeController::GetController()).SoundDestroy( m_sndMotor );
  3158. m_sndMotor = NULL;
  3159. }
  3160. BaseClass::StopLoopingSounds();
  3161. }
  3162. //-----------------------------------------------------------------------------
  3163. // Purpose:
  3164. //-----------------------------------------------------------------------------
  3165. void CWeaponPhysCannon::DestroyEffects( void )
  3166. {
  3167. //Turn off main glow
  3168. if ( m_hCenterSprite != NULL )
  3169. {
  3170. UTIL_Remove( m_hCenterSprite );
  3171. m_hCenterSprite = NULL;
  3172. }
  3173. if ( m_hBlastSprite != NULL )
  3174. {
  3175. UTIL_Remove( m_hBlastSprite );
  3176. m_hBlastSprite = NULL;
  3177. }
  3178. // Turn off beams
  3179. for ( int i = 0; i < NUM_BEAMS; i++ )
  3180. {
  3181. if ( m_hBeams[i] != NULL )
  3182. {
  3183. UTIL_Remove( m_hBeams[i] );
  3184. m_hBeams[i] = NULL;
  3185. }
  3186. }
  3187. // Turn off sprites
  3188. for ( int i = 0; i < NUM_SPRITES; i++ )
  3189. {
  3190. if ( m_hGlowSprites[i] != NULL )
  3191. {
  3192. UTIL_Remove( m_hGlowSprites[i] );
  3193. m_hGlowSprites[i] = NULL;
  3194. }
  3195. }
  3196. for ( int i = 0; i < 2; i++ )
  3197. {
  3198. if ( m_hEndSprites[i] != NULL )
  3199. {
  3200. UTIL_Remove( m_hEndSprites[i] );
  3201. m_hEndSprites[i] = NULL;
  3202. }
  3203. }
  3204. }
  3205. //-----------------------------------------------------------------------------
  3206. // Purpose:
  3207. //-----------------------------------------------------------------------------
  3208. void CWeaponPhysCannon::StopEffects( bool stopSound )
  3209. {
  3210. // Turn off our effect state
  3211. DoEffect( EFFECT_NONE );
  3212. //Turn off main glow
  3213. if ( m_hCenterSprite != NULL )
  3214. {
  3215. m_hCenterSprite->TurnOff();
  3216. }
  3217. if ( m_hBlastSprite != NULL )
  3218. {
  3219. m_hBlastSprite->TurnOff();
  3220. }
  3221. //Turn off beams
  3222. for ( int i = 0; i < NUM_BEAMS; i++ )
  3223. {
  3224. if ( m_hBeams[i] != NULL )
  3225. {
  3226. m_hBeams[i]->SetBrightness( 0 );
  3227. }
  3228. }
  3229. //Turn off sprites
  3230. for ( int i = 0; i < NUM_SPRITES; i++ )
  3231. {
  3232. if ( m_hGlowSprites[i] != NULL )
  3233. {
  3234. m_hGlowSprites[i]->TurnOff();
  3235. }
  3236. }
  3237. for ( int i = 0; i < 2; i++ )
  3238. {
  3239. if ( m_hEndSprites[i] != NULL )
  3240. {
  3241. m_hEndSprites[i]->TurnOff();
  3242. }
  3243. }
  3244. //Shut off sounds
  3245. if ( stopSound && GetMotorSound() != NULL )
  3246. {
  3247. (CSoundEnvelopeController::GetController()).SoundFadeOut( GetMotorSound(), 0.1f );
  3248. }
  3249. }
  3250. //-----------------------------------------------------------------------------
  3251. // Purpose:
  3252. //-----------------------------------------------------------------------------
  3253. void CWeaponPhysCannon::StartEffects( void )
  3254. {
  3255. CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
  3256. if ( pOwner == NULL )
  3257. return;
  3258. bool bIsMegaCannon = IsMegaPhysCannon();
  3259. int i;
  3260. float flScaleFactor = SpriteScaleFactor();
  3261. CBaseEntity *pBeamEnt = pOwner->GetViewModel();
  3262. // Create the beams
  3263. for ( i = 0; i < NUM_BEAMS; i++ )
  3264. {
  3265. if ( m_hBeams[i] )
  3266. continue;
  3267. const char *beamAttachNames[] =
  3268. {
  3269. "fork1t",
  3270. "fork2t",
  3271. "fork1t",
  3272. "fork2t",
  3273. "fork1t",
  3274. "fork2t",
  3275. };
  3276. m_hBeams[i] = CBeam::BeamCreate(
  3277. bIsMegaCannon ? MEGACANNON_BEAM_SPRITE : PHYSCANNON_BEAM_SPRITE, 1.0f );
  3278. m_hBeams[i]->EntsInit( pBeamEnt, pBeamEnt );
  3279. int startAttachment = LookupAttachment( beamAttachNames[i] );
  3280. int endAttachment = 1;
  3281. m_hBeams[i]->FollowEntity( pBeamEnt );
  3282. m_hBeams[i]->AddSpawnFlags( SF_BEAM_TEMPORARY );
  3283. m_hBeams[i]->SetStartAttachment( startAttachment );
  3284. m_hBeams[i]->SetEndAttachment( endAttachment );
  3285. m_hBeams[i]->SetNoise( random->RandomFloat( 8.0f, 16.0f ) );
  3286. m_hBeams[i]->SetColor( 255, 255, 255 );
  3287. m_hBeams[i]->SetScrollRate( 25 );
  3288. m_hBeams[i]->SetBrightness( 128 );
  3289. m_hBeams[i]->SetWidth( 0 );
  3290. m_hBeams[i]->SetEndWidth( random->RandomFloat( 2, 4 ) );
  3291. }
  3292. //Create the glow sprites
  3293. for ( i = 0; i < NUM_SPRITES; i++ )
  3294. {
  3295. if ( m_hGlowSprites[i] )
  3296. continue;
  3297. const char *attachNames[] =
  3298. {
  3299. "fork1b",
  3300. "fork1m",
  3301. "fork1t",
  3302. "fork2b",
  3303. "fork2m",
  3304. "fork2t"
  3305. };
  3306. m_hGlowSprites[i] = CSprite::SpriteCreate(
  3307. bIsMegaCannon ? MEGACANNON_GLOW_SPRITE : PHYSCANNON_GLOW_SPRITE,
  3308. GetAbsOrigin(), false );
  3309. m_hGlowSprites[i]->SetAsTemporary();
  3310. m_hGlowSprites[i]->SetAttachment( pOwner->GetViewModel(), LookupAttachment( attachNames[i] ) );
  3311. if ( bIsMegaCannon )
  3312. {
  3313. m_hGlowSprites[i]->SetTransparency( kRenderTransAdd, 255, 255, 255, 128, kRenderFxNone );
  3314. }
  3315. else
  3316. {
  3317. m_hGlowSprites[i]->SetTransparency( kRenderTransAdd, 255, 128, 0, 64, kRenderFxNoDissipation );
  3318. }
  3319. m_hGlowSprites[i]->SetBrightness( 255, 0.2f );
  3320. m_hGlowSprites[i]->SetScale( 0.25f * flScaleFactor, 0.2f );
  3321. }
  3322. //Create the endcap sprites
  3323. for ( i = 0; i < 2; i++ )
  3324. {
  3325. if ( m_hEndSprites[i] == NULL )
  3326. {
  3327. const char *attachNames[] =
  3328. {
  3329. "fork1t",
  3330. "fork2t"
  3331. };
  3332. m_hEndSprites[i] = CSprite::SpriteCreate(
  3333. bIsMegaCannon ? MEGACANNON_ENDCAP_SPRITE : PHYSCANNON_ENDCAP_SPRITE,
  3334. GetAbsOrigin(), false );
  3335. m_hEndSprites[i]->SetAsTemporary();
  3336. m_hEndSprites[i]->SetAttachment( pOwner->GetViewModel(), LookupAttachment( attachNames[i] ) );
  3337. m_hEndSprites[i]->SetTransparency( kRenderTransAdd, 255, 255, 255, 255, kRenderFxNoDissipation );
  3338. m_hEndSprites[i]->SetBrightness( 255, 0.2f );
  3339. m_hEndSprites[i]->SetScale( 0.25f * flScaleFactor, 0.2f );
  3340. m_hEndSprites[i]->TurnOff();
  3341. }
  3342. }
  3343. //Create the center glow
  3344. if ( m_hCenterSprite == NULL )
  3345. {
  3346. m_hCenterSprite = CSprite::SpriteCreate(
  3347. bIsMegaCannon ? MEGACANNON_CENTER_GLOW : PHYSCANNON_CENTER_GLOW,
  3348. GetAbsOrigin(), false );
  3349. m_hCenterSprite->SetAsTemporary();
  3350. m_hCenterSprite->SetAttachment( pOwner->GetViewModel(), 1 );
  3351. m_hCenterSprite->SetTransparency( kRenderTransAdd, 255, 255, 255, 255, kRenderFxNone );
  3352. m_hCenterSprite->SetBrightness( 255, 0.2f );
  3353. m_hCenterSprite->SetScale( 0.1f, 0.2f );
  3354. }
  3355. //Create the blast sprite
  3356. if ( m_hBlastSprite == NULL )
  3357. {
  3358. m_hBlastSprite = CSprite::SpriteCreate(
  3359. bIsMegaCannon ? MEGACANNON_BLAST_SPRITE : PHYSCANNON_BLAST_SPRITE,
  3360. GetAbsOrigin(), false );
  3361. m_hBlastSprite->SetAsTemporary();
  3362. m_hBlastSprite->SetAttachment( pOwner->GetViewModel(), 1 );
  3363. m_hBlastSprite->SetTransparency( kRenderTransAdd, 255, 255, 255, 255, kRenderFxNone );
  3364. m_hBlastSprite->SetBrightness( 255, 0.2f );
  3365. m_hBlastSprite->SetScale( 0.1f, 0.2f );
  3366. m_hBlastSprite->TurnOff();
  3367. }
  3368. }
  3369. //-----------------------------------------------------------------------------
  3370. // Closing effects
  3371. //-----------------------------------------------------------------------------
  3372. void CWeaponPhysCannon::DoEffectClosed( void )
  3373. {
  3374. float flScaleFactor = SpriteScaleFactor();
  3375. // Turn off the center sprite
  3376. if ( m_hCenterSprite != NULL )
  3377. {
  3378. m_hCenterSprite->SetBrightness( 0.0, 0.1f );
  3379. m_hCenterSprite->SetScale( 0.0f, 0.1f );
  3380. m_hCenterSprite->TurnOff();
  3381. }
  3382. // Turn off the end-caps
  3383. for ( int i = 0; i < 2; i++ )
  3384. {
  3385. if ( m_hEndSprites[i] != NULL )
  3386. {
  3387. m_hEndSprites[i]->TurnOff();
  3388. }
  3389. }
  3390. // Turn off the lightning
  3391. for ( int i = 0; i < NUM_BEAMS; i++ )
  3392. {
  3393. if ( m_hBeams[i] != NULL )
  3394. {
  3395. m_hBeams[i]->SetBrightness( 0 );
  3396. }
  3397. }
  3398. // Turn on the glow sprites
  3399. for ( int i = 0; i < NUM_SPRITES; i++ )
  3400. {
  3401. if ( m_hGlowSprites[i] != NULL )
  3402. {
  3403. m_hGlowSprites[i]->TurnOn();
  3404. m_hGlowSprites[i]->SetBrightness( 16.0f, 0.2f );
  3405. m_hGlowSprites[i]->SetScale( 0.3f * flScaleFactor, 0.2f );
  3406. }
  3407. }
  3408. // Prepare for scale down
  3409. if ( m_hBlastSprite != NULL )
  3410. {
  3411. m_hBlastSprite->TurnOn();
  3412. m_hBlastSprite->SetScale( 1.0f, 0.0f );
  3413. m_hBlastSprite->SetBrightness( 0, 0.0f );
  3414. }
  3415. }
  3416. //-----------------------------------------------------------------------------
  3417. // Closing effects
  3418. //-----------------------------------------------------------------------------
  3419. void CWeaponPhysCannon::DoMegaEffectClosed( void )
  3420. {
  3421. float flScaleFactor = SpriteScaleFactor();
  3422. // Turn off the center sprite
  3423. if ( m_hCenterSprite != NULL )
  3424. {
  3425. m_hCenterSprite->SetBrightness( 0.0, 0.1f );
  3426. m_hCenterSprite->SetScale( 0.0f, 0.1f );
  3427. m_hCenterSprite->TurnOff();
  3428. }
  3429. // Turn off the end-caps
  3430. for ( int i = 0; i < 2; i++ )
  3431. {
  3432. if ( m_hEndSprites[i] != NULL )
  3433. {
  3434. m_hEndSprites[i]->TurnOff();
  3435. }
  3436. }
  3437. // Turn off the lightning
  3438. for ( int i = 0; i < NUM_BEAMS; i++ )
  3439. {
  3440. if ( m_hBeams[i] != NULL )
  3441. {
  3442. m_hBeams[i]->SetBrightness( 0 );
  3443. }
  3444. }
  3445. // Turn on the glow sprites
  3446. for ( int i = 0; i < NUM_SPRITES; i++ )
  3447. {
  3448. if ( m_hGlowSprites[i] != NULL )
  3449. {
  3450. m_hGlowSprites[i]->TurnOn();
  3451. m_hGlowSprites[i]->SetBrightness( 16.0f, 0.2f );
  3452. m_hGlowSprites[i]->SetScale( 0.3f * flScaleFactor, 0.2f );
  3453. }
  3454. }
  3455. // Prepare for scale down
  3456. if ( m_hBlastSprite != NULL )
  3457. {
  3458. m_hBlastSprite->TurnOn();
  3459. m_hBlastSprite->SetScale( 1.0f, 0.0f );
  3460. m_hBlastSprite->SetBrightness( 0, 0.0f );
  3461. }
  3462. }
  3463. //-----------------------------------------------------------------------------
  3464. // Ready effects
  3465. //-----------------------------------------------------------------------------
  3466. void CWeaponPhysCannon::DoEffectReady( )
  3467. {
  3468. float flScaleFactor = SpriteScaleFactor();
  3469. //Turn on the center sprite
  3470. if ( m_hCenterSprite != NULL )
  3471. {
  3472. m_hCenterSprite->SetBrightness( 128, 0.2f );
  3473. m_hCenterSprite->SetScale( 0.15f, 0.2f );
  3474. m_hCenterSprite->TurnOn();
  3475. }
  3476. //Turn off the end-caps
  3477. for ( int i = 0; i < 2; i++ )
  3478. {
  3479. if ( m_hEndSprites[i] != NULL )
  3480. {
  3481. m_hEndSprites[i]->TurnOff();
  3482. }
  3483. }
  3484. //Turn off the lightning
  3485. for ( int i = 0; i < NUM_BEAMS; i++ )
  3486. {
  3487. if ( m_hBeams[i] != NULL )
  3488. {
  3489. m_hBeams[i]->SetBrightness( 0 );
  3490. }
  3491. }
  3492. //Turn on the glow sprites
  3493. for ( int i = 0; i < NUM_SPRITES; i++ )
  3494. {
  3495. if ( m_hGlowSprites[i] != NULL )
  3496. {
  3497. m_hGlowSprites[i]->TurnOn();
  3498. m_hGlowSprites[i]->SetBrightness( 32.0f, 0.2f );
  3499. m_hGlowSprites[i]->SetScale( 0.4f * flScaleFactor, 0.2f );
  3500. }
  3501. }
  3502. //Scale down
  3503. if ( m_hBlastSprite != NULL )
  3504. {
  3505. m_hBlastSprite->TurnOn();
  3506. m_hBlastSprite->SetScale( 0.1f, 0.2f );
  3507. m_hBlastSprite->SetBrightness( 255, 0.1f );
  3508. }
  3509. }
  3510. //-----------------------------------------------------------------------------
  3511. // Holding effects
  3512. //-----------------------------------------------------------------------------
  3513. void CWeaponPhysCannon::DoEffectHolding( )
  3514. {
  3515. float flScaleFactor = SpriteScaleFactor();
  3516. // Turn off the center sprite
  3517. if ( m_hCenterSprite != NULL )
  3518. {
  3519. m_hCenterSprite->SetBrightness( 255, 0.1f );
  3520. m_hCenterSprite->SetScale( 0.2f, 0.2f );
  3521. m_hCenterSprite->TurnOn();
  3522. }
  3523. // Turn off the end-caps
  3524. for ( int i = 0; i < 2; i++ )
  3525. {
  3526. if ( m_hEndSprites[i] != NULL )
  3527. {
  3528. m_hEndSprites[i]->TurnOn();
  3529. }
  3530. }
  3531. // Turn off the lightning
  3532. for ( int i = 0; i < NUM_BEAMS; i++ )
  3533. {
  3534. if ( m_hBeams[i] != NULL )
  3535. {
  3536. m_hBeams[i]->SetBrightness( 128 );
  3537. }
  3538. }
  3539. // Turn on the glow sprites
  3540. for ( int i = 0; i < NUM_SPRITES; i++ )
  3541. {
  3542. if ( m_hGlowSprites[i] != NULL )
  3543. {
  3544. m_hGlowSprites[i]->TurnOn();
  3545. m_hGlowSprites[i]->SetBrightness( 64.0f, 0.2f );
  3546. m_hGlowSprites[i]->SetScale( 0.5f * flScaleFactor, 0.2f );
  3547. }
  3548. }
  3549. // Prepare for scale up
  3550. if ( m_hBlastSprite != NULL )
  3551. {
  3552. m_hBlastSprite->TurnOff();
  3553. m_hBlastSprite->SetScale( 0.1f, 0.0f );
  3554. m_hBlastSprite->SetBrightness( 0, 0.0f );
  3555. }
  3556. }
  3557. //-----------------------------------------------------------------------------
  3558. // Launch effects
  3559. //-----------------------------------------------------------------------------
  3560. void CWeaponPhysCannon::DoEffectLaunch( Vector *pos )
  3561. {
  3562. Assert( pos );
  3563. if ( pos == NULL )
  3564. return;
  3565. CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
  3566. if ( pOwner == NULL )
  3567. return;
  3568. Vector endpos = *pos;
  3569. // Check to store off our view model index
  3570. CBeam *pBeam = CBeam::BeamCreate( IsMegaPhysCannon() ? MEGACANNON_BEAM_SPRITE : PHYSCANNON_BEAM_SPRITE, 8 );
  3571. if ( pBeam != NULL )
  3572. {
  3573. pBeam->PointEntInit( endpos, this );
  3574. pBeam->SetEndAttachment( 1 );
  3575. pBeam->SetWidth( 6.4 );
  3576. pBeam->SetEndWidth( 12.8 );
  3577. pBeam->SetBrightness( 255 );
  3578. pBeam->SetColor( 255, 255, 255 );
  3579. pBeam->LiveForTime( 0.1f );
  3580. pBeam->RelinkBeam();
  3581. pBeam->SetNoise( 2 );
  3582. }
  3583. Vector shotDir = ( endpos - pOwner->Weapon_ShootPosition() );
  3584. VectorNormalize( shotDir );
  3585. //End hit
  3586. //FIXME: Probably too big
  3587. CPVSFilter filter( endpos );
  3588. te->GaussExplosion( filter, 0.0f, endpos - ( shotDir * 4.0f ), RandomVector(-1.0f, 1.0f), 0 );
  3589. if ( m_hBlastSprite != NULL )
  3590. {
  3591. m_hBlastSprite->TurnOn();
  3592. m_hBlastSprite->SetScale( 2.0f, 0.1f );
  3593. m_hBlastSprite->SetBrightness( 0.0f, 0.1f );
  3594. }
  3595. }
  3596. //-----------------------------------------------------------------------------
  3597. // Purpose:
  3598. // Input : *pos -
  3599. //-----------------------------------------------------------------------------
  3600. void CWeaponPhysCannon::DoMegaEffectLaunch( Vector *pos )
  3601. {
  3602. Assert( pos );
  3603. if ( pos == NULL )
  3604. return;
  3605. CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
  3606. if ( pOwner == NULL )
  3607. return;
  3608. Vector endpos = *pos;
  3609. // Check to store off our view model index
  3610. CBaseViewModel *vm = pOwner->GetViewModel();
  3611. int numBeams = random->RandomInt( 1, 2 );
  3612. CBeam *pBeam = CBeam::BeamCreate( IsMegaPhysCannon() ? MEGACANNON_BEAM_SPRITE : PHYSCANNON_BEAM_SPRITE, 0.8 );
  3613. if ( pBeam != NULL )
  3614. {
  3615. pBeam->PointEntInit( endpos, vm );
  3616. pBeam->SetEndAttachment( 1 );
  3617. pBeam->SetWidth( 2 );
  3618. pBeam->SetEndWidth( 12 );
  3619. pBeam->SetBrightness( 255 );
  3620. pBeam->SetColor( 255, 255, 255 );
  3621. pBeam->LiveForTime( 0.1f );
  3622. pBeam->RelinkBeam();
  3623. pBeam->SetNoise( 0 );
  3624. }
  3625. for ( int i = 0; i < numBeams; i++ )
  3626. {
  3627. pBeam = CBeam::BeamCreate( IsMegaPhysCannon() ? MEGACANNON_BEAM_SPRITE : PHYSCANNON_BEAM_SPRITE, 0.8 );
  3628. if ( pBeam != NULL )
  3629. {
  3630. pBeam->PointEntInit( endpos, vm );
  3631. pBeam->SetEndAttachment( 1 );
  3632. pBeam->SetWidth( 2 );
  3633. pBeam->SetEndWidth( random->RandomInt( 1, 2 ) );
  3634. pBeam->SetBrightness( 255 );
  3635. pBeam->SetColor( 255, 255, 255 );
  3636. pBeam->LiveForTime( 0.1f );
  3637. pBeam->RelinkBeam();
  3638. pBeam->SetNoise( random->RandomInt( 8, 12 ) );
  3639. }
  3640. }
  3641. Vector shotDir = ( endpos - pOwner->Weapon_ShootPosition() );
  3642. VectorNormalize( shotDir );
  3643. //End hit
  3644. //FIXME: Probably too big
  3645. CPVSFilter filter( endpos );
  3646. te->GaussExplosion( filter, 0.0f, endpos - ( shotDir * 4.0f ), RandomVector(-1.0f, 1.0f), 0 );
  3647. }
  3648. //-----------------------------------------------------------------------------
  3649. // Holding effects
  3650. //-----------------------------------------------------------------------------
  3651. void CWeaponPhysCannon::DoMegaEffectHolding( void )
  3652. {
  3653. float flScaleFactor = SpriteScaleFactor();
  3654. // Turn off the center sprite
  3655. if ( m_hCenterSprite != NULL )
  3656. {
  3657. m_hCenterSprite->SetBrightness( 255, 0.1f );
  3658. m_hCenterSprite->SetScale( 0.2f, 0.2f );
  3659. m_hCenterSprite->TurnOn();
  3660. }
  3661. // Turn off the end-caps
  3662. for ( int i = 0; i < 2; i++ )
  3663. {
  3664. if ( m_hEndSprites[i] != NULL )
  3665. {
  3666. m_hEndSprites[i]->TurnOn();
  3667. }
  3668. }
  3669. // Turn off the lightning
  3670. for ( int i = 0; i < NUM_BEAMS; i++ )
  3671. {
  3672. if ( m_hBeams[i] != NULL )
  3673. {
  3674. m_hBeams[i]->SetBrightness( 128 );
  3675. }
  3676. }
  3677. // Turn on the glow sprites
  3678. for ( int i = 0; i < NUM_SPRITES; i++ )
  3679. {
  3680. if ( m_hGlowSprites[i] != NULL )
  3681. {
  3682. m_hGlowSprites[i]->TurnOn();
  3683. m_hGlowSprites[i]->SetBrightness( 32.0f, 0.2f );
  3684. m_hGlowSprites[i]->SetScale( 0.25f * flScaleFactor, 0.2f );
  3685. }
  3686. }
  3687. }
  3688. //-----------------------------------------------------------------------------
  3689. // Ready effects
  3690. //-----------------------------------------------------------------------------
  3691. void CWeaponPhysCannon::DoMegaEffectReady( void )
  3692. {
  3693. float flScaleFactor = SpriteScaleFactor();
  3694. //Turn on the center sprite
  3695. if ( m_hCenterSprite != NULL )
  3696. {
  3697. m_hCenterSprite->SetBrightness( 128, 0.2f );
  3698. m_hCenterSprite->SetScale( 0.15f, 0.2f );
  3699. m_hCenterSprite->TurnOn();
  3700. }
  3701. //Turn off the end-caps
  3702. for ( int i = 0; i < 2; i++ )
  3703. {
  3704. if ( m_hEndSprites[i] != NULL )
  3705. {
  3706. if ( m_flEndSpritesOverride[i] < gpGlobals->curtime )
  3707. {
  3708. m_hEndSprites[i]->TurnOff();
  3709. }
  3710. }
  3711. }
  3712. //Turn off the lightning
  3713. for ( int i = 0; i < NUM_BEAMS; i++ )
  3714. {
  3715. if ( m_hBeams[i] != NULL )
  3716. {
  3717. m_hBeams[i]->SetBrightness( 0 );
  3718. }
  3719. }
  3720. //Turn on the glow sprites
  3721. for ( int i = 0; i < NUM_SPRITES; i++ )
  3722. {
  3723. if ( m_hGlowSprites[i] != NULL )
  3724. {
  3725. m_hGlowSprites[i]->TurnOn();
  3726. m_hGlowSprites[i]->SetBrightness( 24.0f, 0.2f );
  3727. m_hGlowSprites[i]->SetScale( 0.2f * flScaleFactor, 0.2f );
  3728. }
  3729. }
  3730. }
  3731. //-----------------------------------------------------------------------------
  3732. // Purpose: Shutdown for the weapon when it's holstered
  3733. //-----------------------------------------------------------------------------
  3734. void CWeaponPhysCannon::DoEffectNone( void )
  3735. {
  3736. if ( m_hBlastSprite != NULL )
  3737. {
  3738. // Become small
  3739. m_hBlastSprite->SetScale( 0.001f );
  3740. }
  3741. }
  3742. //-----------------------------------------------------------------------------
  3743. // Purpose:
  3744. // Input : effectType -
  3745. // *pos -
  3746. //-----------------------------------------------------------------------------
  3747. void CWeaponPhysCannon::DoMegaEffect( int effectType, Vector *pos )
  3748. {
  3749. switch( effectType )
  3750. {
  3751. case EFFECT_CLOSED:
  3752. DoMegaEffectClosed();
  3753. break;
  3754. case EFFECT_READY:
  3755. DoMegaEffectReady();
  3756. break;
  3757. case EFFECT_HOLDING:
  3758. DoMegaEffectHolding();
  3759. break;
  3760. case EFFECT_LAUNCH:
  3761. DoMegaEffectLaunch( pos );
  3762. break;
  3763. default:
  3764. case EFFECT_NONE:
  3765. break;
  3766. }
  3767. }
  3768. //-----------------------------------------------------------------------------
  3769. // Purpose:
  3770. // Input : effectType -
  3771. //-----------------------------------------------------------------------------
  3772. void CWeaponPhysCannon::DoEffect( int effectType, Vector *pos )
  3773. {
  3774. // Make sure we're active
  3775. StartEffects();
  3776. m_EffectState = effectType;
  3777. // Do different effects when upgraded
  3778. if ( IsMegaPhysCannon() )
  3779. {
  3780. DoMegaEffect( effectType, pos );
  3781. return;
  3782. }
  3783. switch( effectType )
  3784. {
  3785. case EFFECT_CLOSED:
  3786. DoEffectClosed( );
  3787. break;
  3788. case EFFECT_READY:
  3789. DoEffectReady( );
  3790. break;
  3791. case EFFECT_HOLDING:
  3792. DoEffectHolding();
  3793. break;
  3794. case EFFECT_LAUNCH:
  3795. DoEffectLaunch( pos );
  3796. break;
  3797. default:
  3798. case EFFECT_NONE:
  3799. DoEffectNone();
  3800. break;
  3801. }
  3802. }
  3803. //-----------------------------------------------------------------------------
  3804. // Purpose:
  3805. // Input : iIndex -
  3806. // Output : const char
  3807. //-----------------------------------------------------------------------------
  3808. const char *CWeaponPhysCannon::GetShootSound( int iIndex ) const
  3809. {
  3810. // Just do this normally if we're a normal physcannon
  3811. if ( PlayerHasMegaPhysCannon() == false )
  3812. return BaseClass::GetShootSound( iIndex );
  3813. // We override this if we're the charged up version
  3814. switch( iIndex )
  3815. {
  3816. case EMPTY:
  3817. return "Weapon_MegaPhysCannon.DryFire";
  3818. break;
  3819. case SINGLE:
  3820. return "Weapon_MegaPhysCannon.Launch";
  3821. break;
  3822. case SPECIAL1:
  3823. return "Weapon_MegaPhysCannon.Pickup";
  3824. break;
  3825. case MELEE_MISS:
  3826. return "Weapon_MegaPhysCannon.Drop";
  3827. break;
  3828. default:
  3829. break;
  3830. }
  3831. return BaseClass::GetShootSound( iIndex );
  3832. }
  3833. //-----------------------------------------------------------------------------
  3834. // Purpose: Adds the specified object to the list of objects that have been
  3835. // propelled by this physgun, along with a timestamp of when the
  3836. // object was added to the list. This list is checked when a physics
  3837. // object strikes another entity, to resolve whether the player is
  3838. // accountable for the impact.
  3839. //
  3840. // Input : pObject - pointer to the object being thrown by the physcannon.
  3841. //-----------------------------------------------------------------------------
  3842. void CWeaponPhysCannon::RecordThrownObject( CBaseEntity *pObject )
  3843. {
  3844. thrown_objects_t thrown;
  3845. thrown.hEntity = pObject;
  3846. thrown.fTimeThrown = gpGlobals->curtime;
  3847. // Get rid of stale and dead objects in the list.
  3848. PurgeThrownObjects();
  3849. // See if this object is already in the list.
  3850. int count = m_ThrownEntities.Count();
  3851. for( int i = 0 ; i < count ; i++ )
  3852. {
  3853. if( m_ThrownEntities[i].hEntity == pObject )
  3854. {
  3855. // Just update the time.
  3856. //Msg("++UPDATING: %s (%d)\n", m_ThrownEntities[i].hEntity->GetClassname(), m_ThrownEntities[i].hEntity->entindex() );
  3857. m_ThrownEntities[i] = thrown;
  3858. return;
  3859. }
  3860. }
  3861. //Msg("++ADDING: %s (%d)\n", pObject->GetClassname(), pObject->entindex() );
  3862. m_ThrownEntities.AddToTail(thrown);
  3863. }
  3864. //-----------------------------------------------------------------------------
  3865. // Purpose: Go through the objects in the thrown objects list and discard any
  3866. // objects that have gone 'stale'. (Were thrown several seconds ago), or
  3867. // have been destroyed or removed.
  3868. //
  3869. //-----------------------------------------------------------------------------
  3870. #define PHYSCANNON_THROWN_LIST_TIMEOUT 10.0f
  3871. void CWeaponPhysCannon::PurgeThrownObjects()
  3872. {
  3873. bool bListChanged;
  3874. // This is bubble-sorty, but the list is also very short.
  3875. do
  3876. {
  3877. bListChanged = false;
  3878. int count = m_ThrownEntities.Count();
  3879. for( int i = 0 ; i < count ; i++ )
  3880. {
  3881. bool bRemove = false;
  3882. if( !m_ThrownEntities[i].hEntity.Get() )
  3883. {
  3884. bRemove = true;
  3885. }
  3886. else if( gpGlobals->curtime > (m_ThrownEntities[i].fTimeThrown + PHYSCANNON_THROWN_LIST_TIMEOUT) )
  3887. {
  3888. bRemove = true;
  3889. }
  3890. else
  3891. {
  3892. IPhysicsObject *pObject = m_ThrownEntities[i].hEntity->VPhysicsGetObject();
  3893. if( pObject && pObject->IsAsleep() )
  3894. {
  3895. bRemove = true;
  3896. }
  3897. }
  3898. if( bRemove )
  3899. {
  3900. //Msg("--REMOVING: %s (%d)\n", m_ThrownEntities[i].hEntity->GetClassname(), m_ThrownEntities[i].hEntity->entindex() );
  3901. m_ThrownEntities.Remove(i);
  3902. bListChanged = true;
  3903. break;
  3904. }
  3905. }
  3906. } while( bListChanged );
  3907. }
  3908. bool CWeaponPhysCannon::IsAccountableForObject( CBaseEntity *pObject )
  3909. {
  3910. // Clean out the stale and dead items.
  3911. PurgeThrownObjects();
  3912. // Now if this object is in the list, the player is accountable for it striking something.
  3913. int count = m_ThrownEntities.Count();
  3914. for( int i = 0 ; i < count ; i++ )
  3915. {
  3916. if( m_ThrownEntities[i].hEntity == pObject )
  3917. {
  3918. return true;
  3919. }
  3920. }
  3921. return false;
  3922. }
  3923. //-----------------------------------------------------------------------------
  3924. // EXTERNAL API
  3925. //-----------------------------------------------------------------------------
  3926. void PhysCannonForceDrop( CBaseCombatWeapon *pActiveWeapon, CBaseEntity *pOnlyIfHoldingThis )
  3927. {
  3928. CWeaponPhysCannon *pCannon = dynamic_cast<CWeaponPhysCannon *>(pActiveWeapon);
  3929. if ( pCannon )
  3930. {
  3931. if ( pOnlyIfHoldingThis )
  3932. {
  3933. pCannon->DropIfEntityHeld( pOnlyIfHoldingThis );
  3934. }
  3935. else
  3936. {
  3937. pCannon->ForceDrop();
  3938. }
  3939. }
  3940. }
  3941. void PhysCannonBeginUpgrade( CBaseAnimating *pAnim )
  3942. {
  3943. CWeaponPhysCannon *pWeaponPhyscannon = assert_cast< CWeaponPhysCannon* >( pAnim );
  3944. pWeaponPhyscannon->BeginUpgrade();
  3945. }
  3946. bool PlayerPickupControllerIsHoldingEntity( CBaseEntity *pPickupControllerEntity, CBaseEntity *pHeldEntity )
  3947. {
  3948. CPlayerPickupController *pController = dynamic_cast<CPlayerPickupController *>(pPickupControllerEntity);
  3949. return pController ? pController->IsHoldingEntity( pHeldEntity ) : false;
  3950. }
  3951. void ShutdownPickupController( CBaseEntity *pPickupControllerEntity )
  3952. {
  3953. CPlayerPickupController *pController = dynamic_cast<CPlayerPickupController *>(pPickupControllerEntity);
  3954. pController->Shutdown( false );
  3955. }
  3956. float PhysCannonGetHeldObjectMass( CBaseCombatWeapon *pActiveWeapon, IPhysicsObject *pHeldObject )
  3957. {
  3958. float mass = 0.0f;
  3959. CWeaponPhysCannon *pCannon = dynamic_cast<CWeaponPhysCannon *>(pActiveWeapon);
  3960. if ( pCannon )
  3961. {
  3962. CGrabController &grab = pCannon->GetGrabController();
  3963. mass = grab.GetSavedMass( pHeldObject );
  3964. }
  3965. return mass;
  3966. }
  3967. CBaseEntity *PhysCannonGetHeldEntity( CBaseCombatWeapon *pActiveWeapon )
  3968. {
  3969. CWeaponPhysCannon *pCannon = dynamic_cast<CWeaponPhysCannon *>(pActiveWeapon);
  3970. if ( pCannon )
  3971. {
  3972. CGrabController &grab = pCannon->GetGrabController();
  3973. return grab.GetAttached();
  3974. }
  3975. return NULL;
  3976. }
  3977. CBaseEntity *GetPlayerHeldEntity( CBasePlayer *pPlayer )
  3978. {
  3979. CBaseEntity *pObject = NULL;
  3980. CPlayerPickupController *pPlayerPickupController = (CPlayerPickupController *)(pPlayer->GetUseEntity());
  3981. if ( pPlayerPickupController )
  3982. {
  3983. pObject = pPlayerPickupController->GetGrabController().GetAttached();
  3984. }
  3985. return pObject;
  3986. }
  3987. CBasePlayer *GetPlayerHoldingEntity( CBaseEntity *pEntity )
  3988. {
  3989. for( int i = 1; i <= gpGlobals->maxClients; ++i )
  3990. {
  3991. CBasePlayer *pPlayer = UTIL_PlayerByIndex( i );
  3992. if ( pPlayer )
  3993. {
  3994. if ( GetPlayerHeldEntity( pPlayer ) == pEntity || PhysCannonGetHeldEntity( pPlayer->GetActiveWeapon() ) == pEntity )
  3995. return pPlayer;
  3996. }
  3997. }
  3998. return NULL;
  3999. }
  4000. CGrabController *GetGrabControllerForPlayer( CBasePlayer *pPlayer )
  4001. {
  4002. CPlayerPickupController *pPlayerPickupController = (CPlayerPickupController *)(pPlayer->GetUseEntity());
  4003. if( pPlayerPickupController )
  4004. return &(pPlayerPickupController->GetGrabController());
  4005. return NULL;
  4006. }
  4007. CGrabController *GetGrabControllerForPhysCannon( CBaseCombatWeapon *pActiveWeapon )
  4008. {
  4009. CWeaponPhysCannon *pCannon = dynamic_cast<CWeaponPhysCannon *>(pActiveWeapon);
  4010. if ( pCannon )
  4011. {
  4012. return &(pCannon->GetGrabController());
  4013. }
  4014. return NULL;
  4015. }
  4016. void GetSavedParamsForCarriedPhysObject( CGrabController *pGrabController, IPhysicsObject *pObject, float *pSavedMassOut, float *pSavedRotationalDampingOut )
  4017. {
  4018. CBaseEntity *pHeld = pGrabController->m_attachedEntity;
  4019. if( pHeld )
  4020. {
  4021. if( pObject->GetGameData() == (void*)pHeld )
  4022. {
  4023. IPhysicsObject *pList[VPHYSICS_MAX_OBJECT_LIST_COUNT];
  4024. int count = pHeld->VPhysicsGetObjectList( pList, ARRAYSIZE(pList) );
  4025. for ( int i = 0; i < count; i++ )
  4026. {
  4027. if ( pList[i] == pObject )
  4028. {
  4029. if( pSavedMassOut )
  4030. *pSavedMassOut = pGrabController->m_savedMass[i];
  4031. if( pSavedRotationalDampingOut )
  4032. *pSavedRotationalDampingOut = pGrabController->m_savedRotDamping[i];
  4033. return;
  4034. }
  4035. }
  4036. }
  4037. }
  4038. if( pSavedMassOut )
  4039. *pSavedMassOut = 0.0f;
  4040. if( pSavedRotationalDampingOut )
  4041. *pSavedRotationalDampingOut = 0.0f;
  4042. return;
  4043. }
  4044. void UpdateGrabControllerTargetPosition( CBasePlayer *pPlayer, Vector *vPosition, QAngle *qAngles )
  4045. {
  4046. CGrabController *pGrabController = GetGrabControllerForPlayer( pPlayer );
  4047. if ( !pGrabController )
  4048. return;
  4049. pGrabController->UpdateObject( pPlayer, 12 );
  4050. pGrabController->GetTargetPosition( vPosition, qAngles );
  4051. }
  4052. bool PhysCannonAccountableForObject( CBaseCombatWeapon *pPhysCannon, CBaseEntity *pObject )
  4053. {
  4054. CWeaponPhysCannon *pCannon = dynamic_cast<CWeaponPhysCannon *>(pPhysCannon);
  4055. if ( pCannon )
  4056. {
  4057. return pCannon->IsAccountableForObject(pObject);
  4058. }
  4059. return false;
  4060. }
  4061. float PlayerPickupGetHeldObjectMass( CBaseEntity *pPickupControllerEntity, IPhysicsObject *pHeldObject )
  4062. {
  4063. float mass = 0.0f;
  4064. CPlayerPickupController *pController = dynamic_cast<CPlayerPickupController *>(pPickupControllerEntity);
  4065. if ( pController )
  4066. {
  4067. CGrabController &grab = pController->GetGrabController();
  4068. mass = grab.GetSavedMass( pHeldObject );
  4069. }
  4070. return mass;
  4071. }
  4072. void GrabController_SetPortalPenetratingEntity( CGrabController *pController, CBaseEntity *pPenetrated )
  4073. {
  4074. pController->SetPortalPenetratingEntity( pPenetrated );
  4075. }