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.

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