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.

3685 lines
100 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: Physics cannon
  4. //
  5. //=============================================================================//
  6. #include "cbase.h"
  7. #ifdef CLIENT_DLL
  8. #include "c_hl2mp_player.h"
  9. #include "vcollide_parse.h"
  10. #include "engine/ivdebugoverlay.h"
  11. #include "iviewrender_beams.h"
  12. #include "beamdraw.h"
  13. #include "c_te_effect_dispatch.h"
  14. #include "model_types.h"
  15. #include "clienteffectprecachesystem.h"
  16. #include "fx_interpvalue.h"
  17. #else
  18. #include "hl2mp_player.h"
  19. #include "soundent.h"
  20. #include "ndebugoverlay.h"
  21. #include "ai_basenpc.h"
  22. #include "player_pickup.h"
  23. #include "physics_prop_ragdoll.h"
  24. #include "globalstate.h"
  25. #include "props.h"
  26. #include "te_effect_dispatch.h"
  27. #include "util.h"
  28. #endif
  29. #include "gamerules.h"
  30. #include "soundenvelope.h"
  31. #include "engine/IEngineSound.h"
  32. #include "physics.h"
  33. #include "in_buttons.h"
  34. #include "IEffects.h"
  35. #include "shake.h"
  36. #include "beam_shared.h"
  37. #include "Sprite.h"
  38. #include "weapon_physcannon.h"
  39. #include "physics_saverestore.h"
  40. #include "movevars_shared.h"
  41. #include "weapon_hl2mpbasehlmpcombatweapon.h"
  42. #include "vphysics/friction.h"
  43. #include "debugoverlay_shared.h"
  44. // memdbgon must be the last include file in a .cpp file!!!
  45. #include "tier0/memdbgon.h"
  46. #define SPRITE_SCALE 128.0f
  47. static const char *s_pWaitForUpgradeContext = "WaitForUpgrade";
  48. ConVar g_debug_physcannon( "g_debug_physcannon", "0", FCVAR_REPLICATED | FCVAR_CHEAT );
  49. ConVar physcannon_minforce( "physcannon_minforce", "700", FCVAR_REPLICATED | FCVAR_CHEAT );
  50. ConVar physcannon_maxforce( "physcannon_maxforce", "1500", FCVAR_REPLICATED | FCVAR_CHEAT );
  51. ConVar physcannon_maxmass( "physcannon_maxmass", "250", FCVAR_REPLICATED | FCVAR_CHEAT );
  52. ConVar physcannon_tracelength( "physcannon_tracelength", "250", FCVAR_REPLICATED | FCVAR_CHEAT );
  53. ConVar physcannon_chargetime("physcannon_chargetime", "2", FCVAR_REPLICATED | FCVAR_CHEAT );
  54. ConVar physcannon_pullforce( "physcannon_pullforce", "4000", FCVAR_REPLICATED | FCVAR_CHEAT );
  55. ConVar physcannon_cone( "physcannon_cone", "0.97", FCVAR_REPLICATED | FCVAR_CHEAT );
  56. ConVar physcannon_ball_cone( "physcannon_ball_cone", "0.997", FCVAR_REPLICATED | FCVAR_CHEAT );
  57. ConVar player_throwforce( "player_throwforce", "1000", FCVAR_REPLICATED | FCVAR_CHEAT );
  58. #ifndef CLIENT_DLL
  59. extern ConVar hl2_normspeed;
  60. extern ConVar hl2_walkspeed;
  61. #endif
  62. #define PHYSCANNON_BEAM_SPRITE "sprites/orangelight1.vmt"
  63. #define PHYSCANNON_BEAM_SPRITE_NOZ "sprites/orangelight1_noz.vmt"
  64. #define PHYSCANNON_GLOW_SPRITE "sprites/glow04_noz"
  65. #define PHYSCANNON_ENDCAP_SPRITE "sprites/orangeflare1"
  66. #define PHYSCANNON_CENTER_GLOW "sprites/orangecore1"
  67. #define PHYSCANNON_BLAST_SPRITE "sprites/orangecore2"
  68. #ifdef CLIENT_DLL
  69. //Precahce the effects
  70. CLIENTEFFECT_REGISTER_BEGIN( PrecacheEffectPhysCannon )
  71. CLIENTEFFECT_MATERIAL( "sprites/orangelight1" )
  72. CLIENTEFFECT_MATERIAL( "sprites/orangelight1_noz" )
  73. CLIENTEFFECT_MATERIAL( PHYSCANNON_GLOW_SPRITE )
  74. CLIENTEFFECT_MATERIAL( PHYSCANNON_ENDCAP_SPRITE )
  75. CLIENTEFFECT_MATERIAL( PHYSCANNON_CENTER_GLOW )
  76. CLIENTEFFECT_MATERIAL( PHYSCANNON_BLAST_SPRITE )
  77. CLIENTEFFECT_REGISTER_END()
  78. #endif // CLIENT_DLL
  79. #ifndef CLIENT_DLL
  80. void PhysCannonBeginUpgrade( CBaseAnimating *pAnim )
  81. {
  82. }
  83. bool PlayerHasMegaPhysCannon( void )
  84. {
  85. return false;
  86. }
  87. bool PhysCannonAccountableForObject( CBaseCombatWeapon *pPhysCannon, CBaseEntity *pObject )
  88. {
  89. // BRJ: FIXME! This can't be implemented trivially, so I'm leaving it to Steve or Adrian
  90. Assert( 0 );
  91. return false;
  92. }
  93. #endif
  94. //-----------------------------------------------------------------------------
  95. //-----------------------------------------------------------------------------
  96. // this will hit skip the pass entity, but not anything it owns
  97. // (lets player grab own grenades)
  98. class CTraceFilterNoOwnerTest : public CTraceFilterSimple
  99. {
  100. public:
  101. DECLARE_CLASS( CTraceFilterNoOwnerTest, CTraceFilterSimple );
  102. CTraceFilterNoOwnerTest( const IHandleEntity *passentity, int collisionGroup )
  103. : CTraceFilterSimple( NULL, collisionGroup ), m_pPassNotOwner(passentity)
  104. {
  105. }
  106. virtual bool ShouldHitEntity( IHandleEntity *pHandleEntity, int contentsMask )
  107. {
  108. if ( pHandleEntity != m_pPassNotOwner )
  109. return BaseClass::ShouldHitEntity( pHandleEntity, contentsMask );
  110. return false;
  111. }
  112. protected:
  113. const IHandleEntity *m_pPassNotOwner;
  114. };
  115. static void MatrixOrthogonalize( matrix3x4_t &matrix, int column )
  116. {
  117. Vector columns[3];
  118. int i;
  119. for ( i = 0; i < 3; i++ )
  120. {
  121. MatrixGetColumn( matrix, i, columns[i] );
  122. }
  123. int index0 = column;
  124. int index1 = (column+1)%3;
  125. int index2 = (column+2)%3;
  126. columns[index2] = CrossProduct( columns[index0], columns[index1] );
  127. columns[index1] = CrossProduct( columns[index2], columns[index0] );
  128. VectorNormalize( columns[index2] );
  129. VectorNormalize( columns[index1] );
  130. MatrixSetColumn( columns[index1], index1, matrix );
  131. MatrixSetColumn( columns[index2], index2, matrix );
  132. }
  133. #define SIGN(x) ( (x) < 0 ? -1 : 1 )
  134. static QAngle AlignAngles( const QAngle &angles, float cosineAlignAngle )
  135. {
  136. matrix3x4_t alignMatrix;
  137. AngleMatrix( angles, alignMatrix );
  138. // NOTE: Must align z first
  139. for ( int j = 3; --j >= 0; )
  140. {
  141. Vector vec;
  142. MatrixGetColumn( alignMatrix, j, vec );
  143. for ( int i = 0; i < 3; i++ )
  144. {
  145. if ( fabs(vec[i]) > cosineAlignAngle )
  146. {
  147. vec[i] = SIGN(vec[i]);
  148. vec[(i+1)%3] = 0;
  149. vec[(i+2)%3] = 0;
  150. MatrixSetColumn( vec, j, alignMatrix );
  151. MatrixOrthogonalize( alignMatrix, j );
  152. break;
  153. }
  154. }
  155. }
  156. QAngle out;
  157. MatrixAngles( alignMatrix, out );
  158. return out;
  159. }
  160. 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 )
  161. {
  162. physcollision->TraceBox( boxOrigin, boxOrigin + (start-end), mins, maxs, pCollide, start, angles, ptr );
  163. if ( ptr->DidHit() )
  164. {
  165. ptr->endpos = start * (1-ptr->fraction) + end * ptr->fraction;
  166. ptr->startpos = start;
  167. ptr->plane.dist = -ptr->plane.dist;
  168. ptr->plane.normal *= -1;
  169. }
  170. }
  171. //-----------------------------------------------------------------------------
  172. // Purpose: Computes a local matrix for the player clamped to valid carry ranges
  173. //-----------------------------------------------------------------------------
  174. // when looking level, hold bottom of object 8 inches below eye level
  175. #define PLAYER_HOLD_LEVEL_EYES -8
  176. // when looking down, hold bottom of object 0 inches from feet
  177. #define PLAYER_HOLD_DOWN_FEET 2
  178. // when looking up, hold bottom of object 24 inches above eye level
  179. #define PLAYER_HOLD_UP_EYES 24
  180. // use a +/-30 degree range for the entire range of motion of pitch
  181. #define PLAYER_LOOK_PITCH_RANGE 30
  182. // player can reach down 2ft below his feet (otherwise he'll hold the object above the bottom)
  183. #define PLAYER_REACH_DOWN_DISTANCE 24
  184. static void ComputePlayerMatrix( CBasePlayer *pPlayer, matrix3x4_t &out )
  185. {
  186. if ( !pPlayer )
  187. return;
  188. QAngle angles = pPlayer->EyeAngles();
  189. Vector origin = pPlayer->EyePosition();
  190. // 0-360 / -180-180
  191. //angles.x = init ? 0 : AngleDistance( angles.x, 0 );
  192. //angles.x = clamp( angles.x, -PLAYER_LOOK_PITCH_RANGE, PLAYER_LOOK_PITCH_RANGE );
  193. angles.x = 0;
  194. float feet = pPlayer->GetAbsOrigin().z + pPlayer->WorldAlignMins().z;
  195. float eyes = origin.z;
  196. float zoffset = 0;
  197. // moving up (negative pitch is up)
  198. if ( angles.x < 0 )
  199. {
  200. zoffset = RemapVal( angles.x, 0, -PLAYER_LOOK_PITCH_RANGE, PLAYER_HOLD_LEVEL_EYES, PLAYER_HOLD_UP_EYES );
  201. }
  202. else
  203. {
  204. zoffset = RemapVal( angles.x, 0, PLAYER_LOOK_PITCH_RANGE, PLAYER_HOLD_LEVEL_EYES, PLAYER_HOLD_DOWN_FEET + (feet - eyes) );
  205. }
  206. origin.z += zoffset;
  207. angles.x = 0;
  208. AngleMatrix( angles, origin, out );
  209. }
  210. //-----------------------------------------------------------------------------
  211. // Purpose:
  212. //-----------------------------------------------------------------------------
  213. // derive from this so we can add save/load data to it
  214. struct game_shadowcontrol_params_t : public hlshadowcontrol_params_t
  215. {
  216. DECLARE_SIMPLE_DATADESC();
  217. };
  218. BEGIN_SIMPLE_DATADESC( game_shadowcontrol_params_t )
  219. DEFINE_FIELD( targetPosition, FIELD_POSITION_VECTOR ),
  220. DEFINE_FIELD( targetRotation, FIELD_VECTOR ),
  221. DEFINE_FIELD( maxAngular, FIELD_FLOAT ),
  222. DEFINE_FIELD( maxDampAngular, FIELD_FLOAT ),
  223. DEFINE_FIELD( maxSpeed, FIELD_FLOAT ),
  224. DEFINE_FIELD( maxDampSpeed, FIELD_FLOAT ),
  225. DEFINE_FIELD( dampFactor, FIELD_FLOAT ),
  226. DEFINE_FIELD( teleportDistance, FIELD_FLOAT ),
  227. END_DATADESC()
  228. //-----------------------------------------------------------------------------
  229. class CGrabController : public IMotionEvent
  230. {
  231. public:
  232. CGrabController( void );
  233. ~CGrabController( void );
  234. void AttachEntity( CBasePlayer *pPlayer, CBaseEntity *pEntity, IPhysicsObject *pPhys, bool bIsMegaPhysCannon, const Vector &vGrabPosition, bool bUseGrabPosition );
  235. void DetachEntity( bool bClearVelocity );
  236. void OnRestore();
  237. bool UpdateObject( CBasePlayer *pPlayer, float flError );
  238. void SetTargetPosition( const Vector &target, const QAngle &targetOrientation );
  239. float ComputeError();
  240. float GetLoadWeight( void ) const { return m_flLoadWeight; }
  241. void SetAngleAlignment( float alignAngleCosine ) { m_angleAlignment = alignAngleCosine; }
  242. void SetIgnorePitch( bool bIgnore ) { m_bIgnoreRelativePitch = bIgnore; }
  243. QAngle TransformAnglesToPlayerSpace( const QAngle &anglesIn, CBasePlayer *pPlayer );
  244. QAngle TransformAnglesFromPlayerSpace( const QAngle &anglesIn, CBasePlayer *pPlayer );
  245. CBaseEntity *GetAttached() { return (CBaseEntity *)m_attachedEntity; }
  246. IMotionEvent::simresult_e Simulate( IPhysicsMotionController *pController, IPhysicsObject *pObject, float deltaTime, Vector &linear, AngularImpulse &angular );
  247. float GetSavedMass( IPhysicsObject *pObject );
  248. QAngle m_attachedAnglesPlayerSpace;
  249. Vector m_attachedPositionObjectSpace;
  250. private:
  251. // Compute the max speed for an attached object
  252. void ComputeMaxSpeed( CBaseEntity *pEntity, IPhysicsObject *pPhysics );
  253. game_shadowcontrol_params_t m_shadow;
  254. float m_timeToArrive;
  255. float m_errorTime;
  256. float m_error;
  257. float m_contactAmount;
  258. float m_angleAlignment;
  259. bool m_bCarriedEntityBlocksLOS;
  260. bool m_bIgnoreRelativePitch;
  261. float m_flLoadWeight;
  262. float m_savedRotDamping[VPHYSICS_MAX_OBJECT_LIST_COUNT];
  263. float m_savedMass[VPHYSICS_MAX_OBJECT_LIST_COUNT];
  264. EHANDLE m_attachedEntity;
  265. QAngle m_vecPreferredCarryAngles;
  266. bool m_bHasPreferredCarryAngles;
  267. IPhysicsMotionController *m_controller;
  268. int m_frameCount;
  269. friend class CWeaponPhysCannon;
  270. };
  271. const float DEFAULT_MAX_ANGULAR = 360.0f * 10.0f;
  272. const float REDUCED_CARRY_MASS = 1.0f;
  273. CGrabController::CGrabController( void )
  274. {
  275. m_shadow.dampFactor = 1.0;
  276. m_shadow.teleportDistance = 0;
  277. m_errorTime = 0;
  278. m_error = 0;
  279. // make this controller really stiff!
  280. m_shadow.maxSpeed = 1000;
  281. m_shadow.maxAngular = DEFAULT_MAX_ANGULAR;
  282. m_shadow.maxDampSpeed = m_shadow.maxSpeed*2;
  283. m_shadow.maxDampAngular = m_shadow.maxAngular;
  284. m_attachedEntity = NULL;
  285. m_vecPreferredCarryAngles = vec3_angle;
  286. m_bHasPreferredCarryAngles = false;
  287. }
  288. CGrabController::~CGrabController( void )
  289. {
  290. DetachEntity( false );
  291. }
  292. void CGrabController::OnRestore()
  293. {
  294. if ( m_controller )
  295. {
  296. m_controller->SetEventHandler( this );
  297. }
  298. }
  299. void CGrabController::SetTargetPosition( const Vector &target, const QAngle &targetOrientation )
  300. {
  301. m_shadow.targetPosition = target;
  302. m_shadow.targetRotation = targetOrientation;
  303. m_timeToArrive = gpGlobals->frametime;
  304. CBaseEntity *pAttached = GetAttached();
  305. if ( pAttached )
  306. {
  307. IPhysicsObject *pObj = pAttached->VPhysicsGetObject();
  308. if ( pObj != NULL )
  309. {
  310. pObj->Wake();
  311. }
  312. else
  313. {
  314. DetachEntity( false );
  315. }
  316. }
  317. }
  318. //-----------------------------------------------------------------------------
  319. // Purpose:
  320. // Output : float
  321. //-----------------------------------------------------------------------------
  322. float CGrabController::ComputeError()
  323. {
  324. if ( m_errorTime <= 0 )
  325. return 0;
  326. CBaseEntity *pAttached = GetAttached();
  327. if ( pAttached )
  328. {
  329. Vector pos;
  330. IPhysicsObject *pObj = pAttached->VPhysicsGetObject();
  331. if ( pObj )
  332. {
  333. pObj->GetShadowPosition( &pos, NULL );
  334. float error = (m_shadow.targetPosition - pos).Length();
  335. if ( m_errorTime > 0 )
  336. {
  337. if ( m_errorTime > 1 )
  338. {
  339. m_errorTime = 1;
  340. }
  341. float speed = error / m_errorTime;
  342. if ( speed > m_shadow.maxSpeed )
  343. {
  344. error *= 0.5;
  345. }
  346. m_error = (1-m_errorTime) * m_error + error * m_errorTime;
  347. }
  348. }
  349. else
  350. {
  351. DevMsg( "Object attached to Physcannon has no physics object\n" );
  352. DetachEntity( false );
  353. return 9999; // force detach
  354. }
  355. }
  356. if ( pAttached->IsEFlagSet( EFL_IS_BEING_LIFTED_BY_BARNACLE ) )
  357. {
  358. m_error *= 3.0f;
  359. }
  360. m_errorTime = 0;
  361. return m_error;
  362. }
  363. #define MASS_SPEED_SCALE 60
  364. #define MAX_MASS 40
  365. void CGrabController::ComputeMaxSpeed( CBaseEntity *pEntity, IPhysicsObject *pPhysics )
  366. {
  367. #ifndef CLIENT_DLL
  368. m_shadow.maxSpeed = 1000;
  369. m_shadow.maxAngular = DEFAULT_MAX_ANGULAR;
  370. // Compute total mass...
  371. float flMass = PhysGetEntityMass( pEntity );
  372. float flMaxMass = physcannon_maxmass.GetFloat();
  373. if ( flMass <= flMaxMass )
  374. return;
  375. float flLerpFactor = clamp( flMass, flMaxMass, 500.0f );
  376. flLerpFactor = SimpleSplineRemapVal( flLerpFactor, flMaxMass, 500.0f, 0.0f, 1.0f );
  377. float invMass = pPhysics->GetInvMass();
  378. float invInertia = pPhysics->GetInvInertia().Length();
  379. float invMaxMass = 1.0f / MAX_MASS;
  380. float ratio = invMaxMass / invMass;
  381. invMass = invMaxMass;
  382. invInertia *= ratio;
  383. float maxSpeed = invMass * MASS_SPEED_SCALE * 200;
  384. float maxAngular = invInertia * MASS_SPEED_SCALE * 360;
  385. m_shadow.maxSpeed = Lerp( flLerpFactor, m_shadow.maxSpeed, maxSpeed );
  386. m_shadow.maxAngular = Lerp( flLerpFactor, m_shadow.maxAngular, maxAngular );
  387. #endif
  388. }
  389. QAngle CGrabController::TransformAnglesToPlayerSpace( const QAngle &anglesIn, CBasePlayer *pPlayer )
  390. {
  391. if ( m_bIgnoreRelativePitch )
  392. {
  393. matrix3x4_t test;
  394. QAngle angleTest = pPlayer->EyeAngles();
  395. angleTest.x = 0;
  396. AngleMatrix( angleTest, test );
  397. return TransformAnglesToLocalSpace( anglesIn, test );
  398. }
  399. return TransformAnglesToLocalSpace( anglesIn, pPlayer->EntityToWorldTransform() );
  400. }
  401. QAngle CGrabController::TransformAnglesFromPlayerSpace( const QAngle &anglesIn, CBasePlayer *pPlayer )
  402. {
  403. if ( m_bIgnoreRelativePitch )
  404. {
  405. matrix3x4_t test;
  406. QAngle angleTest = pPlayer->EyeAngles();
  407. angleTest.x = 0;
  408. AngleMatrix( angleTest, test );
  409. return TransformAnglesToWorldSpace( anglesIn, test );
  410. }
  411. return TransformAnglesToWorldSpace( anglesIn, pPlayer->EntityToWorldTransform() );
  412. }
  413. void CGrabController::AttachEntity( CBasePlayer *pPlayer, CBaseEntity *pEntity, IPhysicsObject *pPhys, bool bIsMegaPhysCannon, const Vector &vGrabPosition, bool bUseGrabPosition )
  414. {
  415. // play the impact sound of the object hitting the player
  416. // used as feedback to let the player know he picked up the object
  417. #ifndef CLIENT_DLL
  418. PhysicsImpactSound( pPlayer, pPhys, CHAN_STATIC, pPhys->GetMaterialIndex(), pPlayer->VPhysicsGetObject()->GetMaterialIndex(), 1.0, 64 );
  419. #endif
  420. Vector position;
  421. QAngle angles;
  422. pPhys->GetPosition( &position, &angles );
  423. // If it has a preferred orientation, use that instead.
  424. #ifndef CLIENT_DLL
  425. Pickup_GetPreferredCarryAngles( pEntity, pPlayer, pPlayer->EntityToWorldTransform(), angles );
  426. #endif
  427. // ComputeMaxSpeed( pEntity, pPhys );
  428. // Carried entities can never block LOS
  429. m_bCarriedEntityBlocksLOS = pEntity->BlocksLOS();
  430. pEntity->SetBlocksLOS( false );
  431. m_controller = physenv->CreateMotionController( this );
  432. m_controller->AttachObject( pPhys, true );
  433. // Don't do this, it's causing trouble with constraint solvers.
  434. //m_controller->SetPriority( IPhysicsMotionController::HIGH_PRIORITY );
  435. pPhys->Wake();
  436. PhysSetGameFlags( pPhys, FVPHYSICS_PLAYER_HELD );
  437. SetTargetPosition( position, angles );
  438. m_attachedEntity = pEntity;
  439. IPhysicsObject *pList[VPHYSICS_MAX_OBJECT_LIST_COUNT];
  440. int count = pEntity->VPhysicsGetObjectList( pList, ARRAYSIZE(pList) );
  441. m_flLoadWeight = 0;
  442. float damping = 10;
  443. float flFactor = count / 7.5f;
  444. if ( flFactor < 1.0f )
  445. {
  446. flFactor = 1.0f;
  447. }
  448. for ( int i = 0; i < count; i++ )
  449. {
  450. float mass = pList[i]->GetMass();
  451. pList[i]->GetDamping( NULL, &m_savedRotDamping[i] );
  452. m_flLoadWeight += mass;
  453. m_savedMass[i] = mass;
  454. // reduce the mass to prevent the player from adding crazy amounts of energy to the system
  455. pList[i]->SetMass( REDUCED_CARRY_MASS / flFactor );
  456. pList[i]->SetDamping( NULL, &damping );
  457. }
  458. // Give extra mass to the phys object we're actually picking up
  459. pPhys->SetMass( REDUCED_CARRY_MASS );
  460. pPhys->EnableDrag( false );
  461. m_errorTime = -1.0f; // 1 seconds until error starts accumulating
  462. m_error = 0;
  463. m_contactAmount = 0;
  464. m_attachedAnglesPlayerSpace = TransformAnglesToPlayerSpace( angles, pPlayer );
  465. if ( m_angleAlignment != 0 )
  466. {
  467. m_attachedAnglesPlayerSpace = AlignAngles( m_attachedAnglesPlayerSpace, m_angleAlignment );
  468. }
  469. VectorITransform( pEntity->WorldSpaceCenter(), pEntity->EntityToWorldTransform(), m_attachedPositionObjectSpace );
  470. #ifndef CLIENT_DLL
  471. // If it's a prop, see if it has desired carry angles
  472. CPhysicsProp *pProp = dynamic_cast<CPhysicsProp *>(pEntity);
  473. if ( pProp )
  474. {
  475. m_bHasPreferredCarryAngles = pProp->GetPropDataAngles( "preferred_carryangles", m_vecPreferredCarryAngles );
  476. }
  477. else
  478. {
  479. m_bHasPreferredCarryAngles = false;
  480. }
  481. #else
  482. m_bHasPreferredCarryAngles = false;
  483. #endif
  484. }
  485. static void ClampPhysicsVelocity( IPhysicsObject *pPhys, float linearLimit, float angularLimit )
  486. {
  487. Vector vel;
  488. AngularImpulse angVel;
  489. pPhys->GetVelocity( &vel, &angVel );
  490. float speed = VectorNormalize(vel) - linearLimit;
  491. float angSpeed = VectorNormalize(angVel) - angularLimit;
  492. speed = speed < 0 ? 0 : -speed;
  493. angSpeed = angSpeed < 0 ? 0 : -angSpeed;
  494. vel *= speed;
  495. angVel *= angSpeed;
  496. pPhys->AddVelocity( &vel, &angVel );
  497. }
  498. void CGrabController::DetachEntity( bool bClearVelocity )
  499. {
  500. CBaseEntity *pEntity = GetAttached();
  501. if ( pEntity )
  502. {
  503. // Restore the LS blocking state
  504. pEntity->SetBlocksLOS( m_bCarriedEntityBlocksLOS );
  505. IPhysicsObject *pList[VPHYSICS_MAX_OBJECT_LIST_COUNT];
  506. int count = pEntity->VPhysicsGetObjectList( pList, ARRAYSIZE(pList) );
  507. for ( int i = 0; i < count; i++ )
  508. {
  509. IPhysicsObject *pPhys = pList[i];
  510. if ( !pPhys )
  511. continue;
  512. // on the odd chance that it's gone to sleep while under anti-gravity
  513. pPhys->EnableDrag( true );
  514. pPhys->Wake();
  515. pPhys->SetMass( m_savedMass[i] );
  516. pPhys->SetDamping( NULL, &m_savedRotDamping[i] );
  517. PhysClearGameFlags( pPhys, FVPHYSICS_PLAYER_HELD );
  518. if ( bClearVelocity )
  519. {
  520. PhysForceClearVelocity( pPhys );
  521. }
  522. else
  523. {
  524. #ifndef CLIENT_DLL
  525. ClampPhysicsVelocity( pPhys, hl2_normspeed.GetFloat() * 1.5f, 2.0f * 360.0f );
  526. #endif
  527. }
  528. }
  529. }
  530. m_attachedEntity = NULL;
  531. if ( physenv )
  532. {
  533. physenv->DestroyMotionController( m_controller );
  534. }
  535. m_controller = NULL;
  536. }
  537. static bool InContactWithHeavyObject( IPhysicsObject *pObject, float heavyMass )
  538. {
  539. bool contact = false;
  540. IPhysicsFrictionSnapshot *pSnapshot = pObject->CreateFrictionSnapshot();
  541. while ( pSnapshot->IsValid() )
  542. {
  543. IPhysicsObject *pOther = pSnapshot->GetObject( 1 );
  544. if ( !pOther->IsMoveable() || pOther->GetMass() > heavyMass )
  545. {
  546. contact = true;
  547. break;
  548. }
  549. pSnapshot->NextFrictionData();
  550. }
  551. pObject->DestroyFrictionSnapshot( pSnapshot );
  552. return contact;
  553. }
  554. IMotionEvent::simresult_e CGrabController::Simulate( IPhysicsMotionController *pController, IPhysicsObject *pObject, float deltaTime, Vector &linear, AngularImpulse &angular )
  555. {
  556. game_shadowcontrol_params_t shadowParams = m_shadow;
  557. if ( InContactWithHeavyObject( pObject, GetLoadWeight() ) )
  558. {
  559. m_contactAmount = Approach( 0.1f, m_contactAmount, deltaTime*2.0f );
  560. }
  561. else
  562. {
  563. m_contactAmount = Approach( 1.0f, m_contactAmount, deltaTime*2.0f );
  564. }
  565. shadowParams.maxAngular = m_shadow.maxAngular * m_contactAmount * m_contactAmount * m_contactAmount;
  566. #ifndef CLIENT_DLL
  567. m_timeToArrive = pObject->ComputeShadowControl( shadowParams, m_timeToArrive, deltaTime );
  568. #else
  569. m_timeToArrive = pObject->ComputeShadowControl( shadowParams, (TICK_INTERVAL*2), deltaTime );
  570. #endif
  571. // Slide along the current contact points to fix bouncing problems
  572. Vector velocity;
  573. AngularImpulse angVel;
  574. pObject->GetVelocity( &velocity, &angVel );
  575. PhysComputeSlideDirection( pObject, velocity, angVel, &velocity, &angVel, GetLoadWeight() );
  576. pObject->SetVelocityInstantaneous( &velocity, NULL );
  577. linear.Init();
  578. angular.Init();
  579. m_errorTime += deltaTime;
  580. return SIM_LOCAL_ACCELERATION;
  581. }
  582. float CGrabController::GetSavedMass( IPhysicsObject *pObject )
  583. {
  584. CBaseEntity *pHeld = m_attachedEntity;
  585. if ( pHeld )
  586. {
  587. if ( pObject->GetGameData() == (void*)pHeld )
  588. {
  589. IPhysicsObject *pList[VPHYSICS_MAX_OBJECT_LIST_COUNT];
  590. int count = pHeld->VPhysicsGetObjectList( pList, ARRAYSIZE(pList) );
  591. for ( int i = 0; i < count; i++ )
  592. {
  593. if ( pList[i] == pObject )
  594. return m_savedMass[i];
  595. }
  596. }
  597. }
  598. return 0.0f;
  599. }
  600. //-----------------------------------------------------------------------------
  601. // Player pickup controller
  602. //-----------------------------------------------------------------------------
  603. class CPlayerPickupController : public CBaseEntity
  604. {
  605. DECLARE_CLASS( CPlayerPickupController, CBaseEntity );
  606. public:
  607. void Init( CBasePlayer *pPlayer, CBaseEntity *pObject );
  608. void Shutdown( bool bThrown = false );
  609. bool OnControls( CBaseEntity *pControls ) { return true; }
  610. void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
  611. void OnRestore()
  612. {
  613. m_grabController.OnRestore();
  614. }
  615. void VPhysicsUpdate( IPhysicsObject *pPhysics ){}
  616. void VPhysicsShadowUpdate( IPhysicsObject *pPhysics ) {}
  617. bool IsHoldingEntity( CBaseEntity *pEnt );
  618. CGrabController &GetGrabController() { return m_grabController; }
  619. private:
  620. CGrabController m_grabController;
  621. CBasePlayer *m_pPlayer;
  622. };
  623. LINK_ENTITY_TO_CLASS( player_pickup, CPlayerPickupController );
  624. //-----------------------------------------------------------------------------
  625. // Purpose:
  626. // Input : *pPlayer -
  627. // *pObject -
  628. //-----------------------------------------------------------------------------
  629. void CPlayerPickupController::Init( CBasePlayer *pPlayer, CBaseEntity *pObject )
  630. {
  631. #ifndef CLIENT_DLL
  632. // Holster player's weapon
  633. if ( pPlayer->GetActiveWeapon() )
  634. {
  635. if ( !pPlayer->GetActiveWeapon()->Holster() )
  636. {
  637. Shutdown();
  638. return;
  639. }
  640. }
  641. CHL2MP_Player *pOwner = (CHL2MP_Player *)ToBasePlayer( pPlayer );
  642. if ( pOwner )
  643. {
  644. pOwner->EnableSprint( false );
  645. }
  646. // If the target is debris, convert it to non-debris
  647. if ( pObject->GetCollisionGroup() == COLLISION_GROUP_DEBRIS )
  648. {
  649. // Interactive debris converts back to debris when it comes to rest
  650. pObject->SetCollisionGroup( COLLISION_GROUP_INTERACTIVE_DEBRIS );
  651. }
  652. // done so I'll go across level transitions with the player
  653. SetParent( pPlayer );
  654. m_grabController.SetIgnorePitch( true );
  655. m_grabController.SetAngleAlignment( DOT_30DEGREE );
  656. m_pPlayer = pPlayer;
  657. IPhysicsObject *pPhysics = pObject->VPhysicsGetObject();
  658. Pickup_OnPhysGunPickup( pObject, m_pPlayer );
  659. m_grabController.AttachEntity( pPlayer, pObject, pPhysics, false, vec3_origin, false );
  660. m_pPlayer->m_Local.m_iHideHUD |= HIDEHUD_WEAPONSELECTION;
  661. m_pPlayer->SetUseEntity( this );
  662. #endif
  663. }
  664. //-----------------------------------------------------------------------------
  665. // Purpose:
  666. // Input : bool -
  667. //-----------------------------------------------------------------------------
  668. void CPlayerPickupController::Shutdown( bool bThrown )
  669. {
  670. #ifndef CLIENT_DLL
  671. CBaseEntity *pObject = m_grabController.GetAttached();
  672. bool bClearVelocity = false;
  673. if ( !bThrown && pObject && pObject->VPhysicsGetObject() && pObject->VPhysicsGetObject()->GetContactPoint(NULL,NULL) )
  674. {
  675. bClearVelocity = true;
  676. }
  677. m_grabController.DetachEntity( bClearVelocity );
  678. if ( pObject != NULL )
  679. {
  680. Pickup_OnPhysGunDrop( pObject, m_pPlayer, bThrown ? THROWN_BY_PLAYER : DROPPED_BY_PLAYER );
  681. }
  682. if ( m_pPlayer )
  683. {
  684. CHL2MP_Player *pOwner = (CHL2MP_Player *)ToBasePlayer( m_pPlayer );
  685. if ( pOwner )
  686. {
  687. pOwner->EnableSprint( true );
  688. }
  689. m_pPlayer->SetUseEntity( NULL );
  690. if ( m_pPlayer->GetActiveWeapon() )
  691. {
  692. if ( !m_pPlayer->GetActiveWeapon()->Deploy() )
  693. {
  694. // We tried to restore the player's weapon, but we couldn't.
  695. // This usually happens when they're holding an empty weapon that doesn't
  696. // autoswitch away when out of ammo. Switch to next best weapon.
  697. m_pPlayer->SwitchToNextBestWeapon( NULL );
  698. }
  699. }
  700. m_pPlayer->m_Local.m_iHideHUD &= ~HIDEHUD_WEAPONSELECTION;
  701. }
  702. Remove();
  703. #endif
  704. }
  705. void CPlayerPickupController::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
  706. {
  707. if ( ToBasePlayer(pActivator) == m_pPlayer )
  708. {
  709. CBaseEntity *pAttached = m_grabController.GetAttached();
  710. // UNDONE: Use vphysics stress to decide to drop objects
  711. // UNDONE: Must fix case of forcing objects into the ground you're standing on (causes stress) before that will work
  712. if ( !pAttached || useType == USE_OFF || (m_pPlayer->m_nButtons & IN_ATTACK2) || m_grabController.ComputeError() > 12 )
  713. {
  714. Shutdown();
  715. return;
  716. }
  717. //Adrian: Oops, our object became motion disabled, let go!
  718. IPhysicsObject *pPhys = pAttached->VPhysicsGetObject();
  719. if ( pPhys && pPhys->IsMoveable() == false )
  720. {
  721. Shutdown();
  722. return;
  723. }
  724. #if STRESS_TEST
  725. vphysics_objectstress_t stress;
  726. CalculateObjectStress( pPhys, pAttached, &stress );
  727. if ( stress.exertedStress > 250 )
  728. {
  729. Shutdown();
  730. return;
  731. }
  732. #endif
  733. // +ATTACK will throw phys objects
  734. if ( m_pPlayer->m_nButtons & IN_ATTACK )
  735. {
  736. Shutdown( true );
  737. Vector vecLaunch;
  738. m_pPlayer->EyeVectors( &vecLaunch );
  739. // JAY: Scale this with mass because some small objects really go flying
  740. float massFactor = clamp( pPhys->GetMass(), 0.5, 15 );
  741. massFactor = RemapVal( massFactor, 0.5, 15, 0.5, 4 );
  742. vecLaunch *= player_throwforce.GetFloat() * massFactor;
  743. pPhys->ApplyForceCenter( vecLaunch );
  744. AngularImpulse aVel = RandomAngularImpulse( -10, 10 ) * massFactor;
  745. pPhys->ApplyTorqueCenter( aVel );
  746. return;
  747. }
  748. if ( useType == USE_SET )
  749. {
  750. // update position
  751. m_grabController.UpdateObject( m_pPlayer, 12 );
  752. }
  753. }
  754. }
  755. //-----------------------------------------------------------------------------
  756. // Purpose:
  757. // Input : *pEnt -
  758. // Output : Returns true on success, false on failure.
  759. //-----------------------------------------------------------------------------
  760. bool CPlayerPickupController::IsHoldingEntity( CBaseEntity *pEnt )
  761. {
  762. return ( m_grabController.GetAttached() == pEnt );
  763. }
  764. void PlayerPickupObject( CBasePlayer *pPlayer, CBaseEntity *pObject )
  765. {
  766. #ifndef CLIENT_DLL
  767. //Don't pick up if we don't have a phys object.
  768. if ( pObject->VPhysicsGetObject() == NULL )
  769. return;
  770. CPlayerPickupController *pController = (CPlayerPickupController *)CBaseEntity::Create( "player_pickup", pObject->GetAbsOrigin(), vec3_angle, pPlayer );
  771. if ( !pController )
  772. return;
  773. pController->Init( pPlayer, pObject );
  774. #endif
  775. }
  776. //----------------------------------------------------------------------------------------------------------------------------------------------------------
  777. // CInterpolatedValue class
  778. //----------------------------------------------------------------------------------------------------------------------------------------------------------
  779. #ifdef CLIENT_DLL
  780. //----------------------------------------------------------------------------------------------------------------------------------------------------------
  781. // CPhysCannonEffect class
  782. //----------------------------------------------------------------------------------------------------------------------------------------------------------
  783. class CPhysCannonEffect
  784. {
  785. public:
  786. CPhysCannonEffect( void ) : m_vecColor( 255, 255, 255 ), m_bVisible( true ), m_nAttachment( -1 ) {};
  787. void SetAttachment( int attachment ) { m_nAttachment = attachment; }
  788. int GetAttachment( void ) const { return m_nAttachment; }
  789. void SetVisible( bool visible = true ) { m_bVisible = visible; }
  790. int IsVisible( void ) const { return m_bVisible; }
  791. void SetColor( const Vector &color ) { m_vecColor = color; }
  792. const Vector &GetColor( void ) const { return m_vecColor; }
  793. bool SetMaterial( const char *materialName )
  794. {
  795. m_hMaterial.Init( materialName, TEXTURE_GROUP_CLIENT_EFFECTS );
  796. return ( m_hMaterial != NULL );
  797. }
  798. CMaterialReference &GetMaterial( void ) { return m_hMaterial; }
  799. CInterpolatedValue &GetAlpha( void ) { return m_Alpha; }
  800. CInterpolatedValue &GetScale( void ) { return m_Scale; }
  801. private:
  802. CInterpolatedValue m_Alpha;
  803. CInterpolatedValue m_Scale;
  804. Vector m_vecColor;
  805. bool m_bVisible;
  806. int m_nAttachment;
  807. CMaterialReference m_hMaterial;
  808. };
  809. //----------------------------------------------------------------------------------------------------------------------------------------------------------
  810. // CPhysCannonEffectBeam class
  811. //----------------------------------------------------------------------------------------------------------------------------------------------------------
  812. class CPhysCannonEffectBeam
  813. {
  814. public:
  815. CPhysCannonEffectBeam( void ) : m_pBeam( NULL ) {};
  816. ~CPhysCannonEffectBeam( void )
  817. {
  818. Release();
  819. }
  820. void Release( void )
  821. {
  822. if ( m_pBeam != NULL )
  823. {
  824. m_pBeam->flags = 0;
  825. m_pBeam->die = gpGlobals->curtime - 1;
  826. m_pBeam = NULL;
  827. }
  828. }
  829. void Init( int startAttachment, int endAttachment, CBaseEntity *pEntity, bool firstPerson )
  830. {
  831. if ( m_pBeam != NULL )
  832. return;
  833. BeamInfo_t beamInfo;
  834. beamInfo.m_pStartEnt = pEntity;
  835. beamInfo.m_nStartAttachment = startAttachment;
  836. beamInfo.m_pEndEnt = pEntity;
  837. beamInfo.m_nEndAttachment = endAttachment;
  838. beamInfo.m_nType = TE_BEAMPOINTS;
  839. beamInfo.m_vecStart = vec3_origin;
  840. beamInfo.m_vecEnd = vec3_origin;
  841. beamInfo.m_pszModelName = ( firstPerson ) ? PHYSCANNON_BEAM_SPRITE_NOZ : PHYSCANNON_BEAM_SPRITE;
  842. beamInfo.m_flHaloScale = 0.0f;
  843. beamInfo.m_flLife = 0.0f;
  844. if ( firstPerson )
  845. {
  846. beamInfo.m_flWidth = 0.0f;
  847. beamInfo.m_flEndWidth = 4.0f;
  848. }
  849. else
  850. {
  851. beamInfo.m_flWidth = 0.5f;
  852. beamInfo.m_flEndWidth = 2.0f;
  853. }
  854. beamInfo.m_flFadeLength = 0.0f;
  855. beamInfo.m_flAmplitude = 16;
  856. beamInfo.m_flBrightness = 255.0;
  857. beamInfo.m_flSpeed = 150.0f;
  858. beamInfo.m_nStartFrame = 0.0;
  859. beamInfo.m_flFrameRate = 30.0;
  860. beamInfo.m_flRed = 255.0;
  861. beamInfo.m_flGreen = 255.0;
  862. beamInfo.m_flBlue = 255.0;
  863. beamInfo.m_nSegments = 8;
  864. beamInfo.m_bRenderable = true;
  865. beamInfo.m_nFlags = FBEAM_FOREVER;
  866. m_pBeam = beams->CreateBeamEntPoint( beamInfo );
  867. }
  868. void SetVisible( bool state = true )
  869. {
  870. if ( m_pBeam == NULL )
  871. return;
  872. m_pBeam->brightness = ( state ) ? 255.0f : 0.0f;
  873. }
  874. private:
  875. Beam_t *m_pBeam;
  876. };
  877. #endif
  878. //----------------------------------------------------------------------------------------------------------------------------------------------------------
  879. // CWeaponPhysCannon class
  880. //----------------------------------------------------------------------------------------------------------------------------------------------------------
  881. #ifdef CLIENT_DLL
  882. #define CWeaponPhysCannon C_WeaponPhysCannon
  883. #endif
  884. class CWeaponPhysCannon : public CBaseHL2MPCombatWeapon
  885. {
  886. public:
  887. DECLARE_CLASS( CWeaponPhysCannon, CBaseHL2MPCombatWeapon );
  888. DECLARE_NETWORKCLASS();
  889. DECLARE_PREDICTABLE();
  890. CWeaponPhysCannon( void );
  891. void Drop( const Vector &vecVelocity );
  892. void Precache();
  893. virtual void OnRestore();
  894. virtual void StopLoopingSounds();
  895. virtual void UpdateOnRemove(void);
  896. void PrimaryAttack();
  897. void SecondaryAttack();
  898. void WeaponIdle();
  899. void ItemPreFrame();
  900. void ItemPostFrame();
  901. void ForceDrop( void );
  902. bool DropIfEntityHeld( CBaseEntity *pTarget ); // Drops its held entity if it matches the entity passed in
  903. CGrabController &GetGrabController() { return m_grabController; }
  904. bool CanHolster( void );
  905. bool Holster( CBaseCombatWeapon *pSwitchingTo = NULL );
  906. bool Deploy( void );
  907. bool HasAnyAmmo( void ) { return true; }
  908. virtual void SetViewModel( void );
  909. virtual const char *GetShootSound( int iIndex ) const;
  910. #ifndef CLIENT_DLL
  911. CNetworkQAngle ( m_attachedAnglesPlayerSpace );
  912. #else
  913. QAngle m_attachedAnglesPlayerSpace;
  914. #endif
  915. CNetworkVector ( m_attachedPositionObjectSpace );
  916. CNetworkHandle( CBaseEntity, m_hAttachedObject );
  917. EHANDLE m_hOldAttachedObject;
  918. protected:
  919. enum FindObjectResult_t
  920. {
  921. OBJECT_FOUND = 0,
  922. OBJECT_NOT_FOUND,
  923. OBJECT_BEING_DETACHED,
  924. };
  925. void DoEffect( int effectType, Vector *pos = NULL );
  926. void OpenElements( void );
  927. void CloseElements( void );
  928. // Pickup and throw objects.
  929. bool CanPickupObject( CBaseEntity *pTarget );
  930. void CheckForTarget( void );
  931. #ifndef CLIENT_DLL
  932. bool AttachObject( CBaseEntity *pObject, const Vector &vPosition );
  933. FindObjectResult_t FindObject( void );
  934. CBaseEntity *FindObjectInCone( const Vector &vecOrigin, const Vector &vecDir, float flCone );
  935. #endif // !CLIENT_DLL
  936. void UpdateObject( void );
  937. void DetachObject( bool playSound = true, bool wasLaunched = false );
  938. void LaunchObject( const Vector &vecDir, float flForce );
  939. void StartEffects( void ); // Initialize all sprites and beams
  940. void StopEffects( bool stopSound = true ); // Hide all effects temporarily
  941. void DestroyEffects( void ); // Destroy all sprites and beams
  942. // Punt objects - this is pointing at an object in the world and applying a force to it.
  943. void PuntNonVPhysics( CBaseEntity *pEntity, const Vector &forward, trace_t &tr );
  944. void PuntVPhysics( CBaseEntity *pEntity, const Vector &forward, trace_t &tr );
  945. // Velocity-based throw common to punt and launch code.
  946. void ApplyVelocityBasedForce( CBaseEntity *pEntity, const Vector &forward );
  947. // Physgun effects
  948. void DoEffectClosed( void );
  949. void DoEffectReady( void );
  950. void DoEffectHolding( void );
  951. void DoEffectLaunch( Vector *pos );
  952. void DoEffectNone( void );
  953. void DoEffectIdle( void );
  954. // Trace length
  955. float TraceLength();
  956. // Sprite scale factor
  957. float SpriteScaleFactor();
  958. float GetLoadPercentage();
  959. CSoundPatch *GetMotorSound( void );
  960. void DryFire( void );
  961. void PrimaryFireEffect( void );
  962. #ifndef CLIENT_DLL
  963. // What happens when the physgun picks up something
  964. void Physgun_OnPhysGunPickup( CBaseEntity *pEntity, CBasePlayer *pOwner, PhysGunPickup_t reason );
  965. #endif // !CLIENT_DLL
  966. #ifdef CLIENT_DLL
  967. enum EffectType_t
  968. {
  969. PHYSCANNON_CORE = 0,
  970. PHYSCANNON_BLAST,
  971. PHYSCANNON_GLOW1, // Must be in order!
  972. PHYSCANNON_GLOW2,
  973. PHYSCANNON_GLOW3,
  974. PHYSCANNON_GLOW4,
  975. PHYSCANNON_GLOW5,
  976. PHYSCANNON_GLOW6,
  977. PHYSCANNON_ENDCAP1, // Must be in order!
  978. PHYSCANNON_ENDCAP2,
  979. PHYSCANNON_ENDCAP3, // Only used in third-person!
  980. NUM_PHYSCANNON_PARAMETERS // Must be last!
  981. };
  982. #define NUM_GLOW_SPRITES ((CWeaponPhysCannon::PHYSCANNON_GLOW6-CWeaponPhysCannon::PHYSCANNON_GLOW1)+1)
  983. #define NUM_ENDCAP_SPRITES ((CWeaponPhysCannon::PHYSCANNON_ENDCAP3-CWeaponPhysCannon::PHYSCANNON_ENDCAP1)+1)
  984. #define NUM_PHYSCANNON_BEAMS 3
  985. virtual int DrawModel( int flags );
  986. virtual void ViewModelDrawn( C_BaseViewModel *pBaseViewModel );
  987. virtual bool IsTransparent( void );
  988. virtual void OnDataChanged( DataUpdateType_t type );
  989. virtual void ClientThink( void );
  990. void ManagePredictedObject( void );
  991. void DrawEffects( void );
  992. void GetEffectParameters( EffectType_t effectID, color32 &color, float &scale, IMaterial **pMaterial, Vector &vecAttachment );
  993. void DrawEffectSprite( EffectType_t effectID );
  994. inline bool IsEffectVisible( EffectType_t effectID );
  995. void UpdateElementPosition( void );
  996. // We need to render opaque and translucent pieces
  997. RenderGroup_t GetRenderGroup( void ) { return RENDER_GROUP_TWOPASS; }
  998. CInterpolatedValue m_ElementParameter; // Used to interpolate the position of the articulated elements
  999. CPhysCannonEffect m_Parameters[NUM_PHYSCANNON_PARAMETERS]; // Interpolated parameters for the effects
  1000. CPhysCannonEffectBeam m_Beams[NUM_PHYSCANNON_BEAMS]; // Beams
  1001. int m_nOldEffectState; // Used for parity checks
  1002. bool m_bOldOpen; // Used for parity checks
  1003. void NotifyShouldTransmit( ShouldTransmitState_t state );
  1004. #endif // CLIENT_DLL
  1005. int m_nChangeState; // For delayed state change of elements
  1006. float m_flCheckSuppressTime; // Amount of time to suppress the checking for targets
  1007. bool m_flLastDenySoundPlayed; // Debounce for deny sound
  1008. int m_nAttack2Debounce;
  1009. CNetworkVar( bool, m_bActive );
  1010. CNetworkVar( int, m_EffectState ); // Current state of the effects on the gun
  1011. CNetworkVar( bool, m_bOpen );
  1012. bool m_bResetOwnerEntity;
  1013. float m_flElementDebounce;
  1014. CSoundPatch *m_sndMotor; // Whirring sound for the gun
  1015. CGrabController m_grabController;
  1016. float m_flRepuntObjectTime;
  1017. EHANDLE m_hLastPuntedObject;
  1018. private:
  1019. CWeaponPhysCannon( const CWeaponPhysCannon & );
  1020. #ifndef CLIENT_DLL
  1021. DECLARE_ACTTABLE();
  1022. #endif
  1023. };
  1024. IMPLEMENT_NETWORKCLASS_ALIASED( WeaponPhysCannon, DT_WeaponPhysCannon )
  1025. BEGIN_NETWORK_TABLE( CWeaponPhysCannon, DT_WeaponPhysCannon )
  1026. #ifdef CLIENT_DLL
  1027. RecvPropBool( RECVINFO( m_bActive ) ),
  1028. RecvPropEHandle( RECVINFO( m_hAttachedObject ) ),
  1029. RecvPropVector( RECVINFO( m_attachedPositionObjectSpace ) ),
  1030. RecvPropFloat( RECVINFO( m_attachedAnglesPlayerSpace[0] ) ),
  1031. RecvPropFloat( RECVINFO( m_attachedAnglesPlayerSpace[1] ) ),
  1032. RecvPropFloat( RECVINFO( m_attachedAnglesPlayerSpace[2] ) ),
  1033. RecvPropInt( RECVINFO( m_EffectState ) ),
  1034. RecvPropBool( RECVINFO( m_bOpen ) ),
  1035. #else
  1036. SendPropBool( SENDINFO( m_bActive ) ),
  1037. SendPropEHandle( SENDINFO( m_hAttachedObject ) ),
  1038. SendPropVector(SENDINFO( m_attachedPositionObjectSpace ), -1, SPROP_COORD),
  1039. SendPropAngle( SENDINFO_VECTORELEM(m_attachedAnglesPlayerSpace, 0 ), 11 ),
  1040. SendPropAngle( SENDINFO_VECTORELEM(m_attachedAnglesPlayerSpace, 1 ), 11 ),
  1041. SendPropAngle( SENDINFO_VECTORELEM(m_attachedAnglesPlayerSpace, 2 ), 11 ),
  1042. SendPropInt( SENDINFO( m_EffectState ) ),
  1043. SendPropBool( SENDINFO( m_bOpen ) ),
  1044. #endif
  1045. END_NETWORK_TABLE()
  1046. #ifdef CLIENT_DLL
  1047. BEGIN_PREDICTION_DATA( CWeaponPhysCannon )
  1048. DEFINE_PRED_FIELD( m_EffectState, FIELD_INTEGER, FTYPEDESC_INSENDTABLE ),
  1049. DEFINE_PRED_FIELD( m_bOpen, FIELD_BOOLEAN, FTYPEDESC_INSENDTABLE ),
  1050. END_PREDICTION_DATA()
  1051. #endif
  1052. LINK_ENTITY_TO_CLASS( weapon_physcannon, CWeaponPhysCannon );
  1053. PRECACHE_WEAPON_REGISTER( weapon_physcannon );
  1054. #ifndef CLIENT_DLL
  1055. acttable_t CWeaponPhysCannon::m_acttable[] =
  1056. {
  1057. { ACT_HL2MP_IDLE, ACT_HL2MP_IDLE_PHYSGUN, false },
  1058. { ACT_HL2MP_RUN, ACT_HL2MP_RUN_PHYSGUN, false },
  1059. { ACT_HL2MP_IDLE_CROUCH, ACT_HL2MP_IDLE_CROUCH_PHYSGUN, false },
  1060. { ACT_HL2MP_WALK_CROUCH, ACT_HL2MP_WALK_CROUCH_PHYSGUN, false },
  1061. { ACT_HL2MP_GESTURE_RANGE_ATTACK, ACT_HL2MP_GESTURE_RANGE_ATTACK_PHYSGUN, false },
  1062. { ACT_HL2MP_GESTURE_RELOAD, ACT_HL2MP_GESTURE_RELOAD_PHYSGUN, false },
  1063. { ACT_HL2MP_JUMP, ACT_HL2MP_JUMP_PHYSGUN, false },
  1064. };
  1065. IMPLEMENT_ACTTABLE(CWeaponPhysCannon);
  1066. #endif
  1067. enum
  1068. {
  1069. ELEMENT_STATE_NONE = -1,
  1070. ELEMENT_STATE_OPEN,
  1071. ELEMENT_STATE_CLOSED,
  1072. };
  1073. enum
  1074. {
  1075. EFFECT_NONE,
  1076. EFFECT_CLOSED,
  1077. EFFECT_READY,
  1078. EFFECT_HOLDING,
  1079. EFFECT_LAUNCH,
  1080. };
  1081. //-----------------------------------------------------------------------------
  1082. // Constructor
  1083. //-----------------------------------------------------------------------------
  1084. CWeaponPhysCannon::CWeaponPhysCannon( void )
  1085. {
  1086. m_bOpen = false;
  1087. m_nChangeState = ELEMENT_STATE_NONE;
  1088. m_flCheckSuppressTime = 0.0f;
  1089. m_EffectState = (int)EFFECT_NONE;
  1090. m_flLastDenySoundPlayed = false;
  1091. #ifdef CLIENT_DLL
  1092. m_nOldEffectState = EFFECT_NONE;
  1093. m_bOldOpen = false;
  1094. #endif
  1095. }
  1096. //-----------------------------------------------------------------------------
  1097. // Purpose: Precache
  1098. //-----------------------------------------------------------------------------
  1099. void CWeaponPhysCannon::Precache( void )
  1100. {
  1101. PrecacheModel( PHYSCANNON_BEAM_SPRITE );
  1102. PrecacheModel( PHYSCANNON_BEAM_SPRITE_NOZ );
  1103. PrecacheScriptSound( "Weapon_PhysCannon.HoldSound" );
  1104. BaseClass::Precache();
  1105. }
  1106. //-----------------------------------------------------------------------------
  1107. // Purpose: Restore
  1108. //-----------------------------------------------------------------------------
  1109. void CWeaponPhysCannon::OnRestore()
  1110. {
  1111. BaseClass::OnRestore();
  1112. m_grabController.OnRestore();
  1113. // Tracker 8106: Physcannon effects disappear through level transition, so
  1114. // just recreate any effects here
  1115. if ( m_EffectState != EFFECT_NONE )
  1116. {
  1117. DoEffect( m_EffectState, NULL );
  1118. }
  1119. }
  1120. //-----------------------------------------------------------------------------
  1121. // On Remove
  1122. //-----------------------------------------------------------------------------
  1123. void CWeaponPhysCannon::UpdateOnRemove(void)
  1124. {
  1125. DestroyEffects( );
  1126. BaseClass::UpdateOnRemove();
  1127. }
  1128. #ifdef CLIENT_DLL
  1129. void CWeaponPhysCannon::OnDataChanged( DataUpdateType_t type )
  1130. {
  1131. BaseClass::OnDataChanged( type );
  1132. if ( type == DATA_UPDATE_CREATED )
  1133. {
  1134. SetNextClientThink( CLIENT_THINK_ALWAYS );
  1135. C_BaseAnimating::AutoAllowBoneAccess boneaccess( true, false );
  1136. StartEffects();
  1137. }
  1138. if ( GetOwner() == NULL )
  1139. {
  1140. if ( m_hAttachedObject )
  1141. {
  1142. m_hAttachedObject->VPhysicsDestroyObject();
  1143. }
  1144. if ( m_hOldAttachedObject )
  1145. {
  1146. m_hOldAttachedObject->VPhysicsDestroyObject();
  1147. }
  1148. }
  1149. // Update effect state when out of parity with the server
  1150. if ( m_nOldEffectState != m_EffectState )
  1151. {
  1152. DoEffect( m_EffectState );
  1153. m_nOldEffectState = m_EffectState;
  1154. }
  1155. // Update element state when out of parity
  1156. if ( m_bOldOpen != m_bOpen )
  1157. {
  1158. if ( m_bOpen )
  1159. {
  1160. m_ElementParameter.InitFromCurrent( 1.0f, 0.2f, INTERP_SPLINE );
  1161. }
  1162. else
  1163. {
  1164. m_ElementParameter.InitFromCurrent( 0.0f, 0.5f, INTERP_SPLINE );
  1165. }
  1166. m_bOldOpen = (bool) m_bOpen;
  1167. }
  1168. }
  1169. #endif
  1170. //-----------------------------------------------------------------------------
  1171. // Sprite scale factor
  1172. //-----------------------------------------------------------------------------
  1173. inline float CWeaponPhysCannon::SpriteScaleFactor()
  1174. {
  1175. return 1.0f;
  1176. }
  1177. //-----------------------------------------------------------------------------
  1178. // Purpose:
  1179. // Output : Returns true on success, false on failure.
  1180. //-----------------------------------------------------------------------------
  1181. bool CWeaponPhysCannon::Deploy( void )
  1182. {
  1183. CloseElements();
  1184. DoEffect( EFFECT_READY );
  1185. bool bReturn = BaseClass::Deploy();
  1186. m_flNextSecondaryAttack = m_flNextPrimaryAttack = gpGlobals->curtime;
  1187. CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
  1188. if ( pOwner )
  1189. {
  1190. pOwner->SetNextAttack( gpGlobals->curtime );
  1191. }
  1192. return bReturn;
  1193. }
  1194. //-----------------------------------------------------------------------------
  1195. // Purpose:
  1196. //-----------------------------------------------------------------------------
  1197. void CWeaponPhysCannon::SetViewModel( void )
  1198. {
  1199. BaseClass::SetViewModel();
  1200. }
  1201. //-----------------------------------------------------------------------------
  1202. // Purpose: Force the cannon to drop anything it's carrying
  1203. //-----------------------------------------------------------------------------
  1204. void CWeaponPhysCannon::ForceDrop( void )
  1205. {
  1206. CloseElements();
  1207. DetachObject();
  1208. StopEffects();
  1209. }
  1210. //-----------------------------------------------------------------------------
  1211. // Purpose: Drops its held entity if it matches the entity passed in
  1212. // Input : *pTarget -
  1213. // Output : Returns true on success, false on failure.
  1214. //-----------------------------------------------------------------------------
  1215. bool CWeaponPhysCannon::DropIfEntityHeld( CBaseEntity *pTarget )
  1216. {
  1217. if ( pTarget == NULL )
  1218. return false;
  1219. CBaseEntity *pHeld = m_grabController.GetAttached();
  1220. if ( pHeld == NULL )
  1221. return false;
  1222. if ( pHeld == pTarget )
  1223. {
  1224. ForceDrop();
  1225. return true;
  1226. }
  1227. return false;
  1228. }
  1229. //-----------------------------------------------------------------------------
  1230. // Purpose:
  1231. //-----------------------------------------------------------------------------
  1232. void CWeaponPhysCannon::Drop( const Vector &vecVelocity )
  1233. {
  1234. ForceDrop();
  1235. #ifndef CLIENT_DLL
  1236. UTIL_Remove( this );
  1237. #endif
  1238. }
  1239. //-----------------------------------------------------------------------------
  1240. // Purpose:
  1241. //-----------------------------------------------------------------------------
  1242. bool CWeaponPhysCannon::CanHolster( void )
  1243. {
  1244. //Don't holster this weapon if we're holding onto something
  1245. if ( m_bActive )
  1246. return false;
  1247. return BaseClass::CanHolster();
  1248. };
  1249. //-----------------------------------------------------------------------------
  1250. // Purpose:
  1251. // Output : Returns true on success, false on failure.
  1252. //-----------------------------------------------------------------------------
  1253. bool CWeaponPhysCannon::Holster( CBaseCombatWeapon *pSwitchingTo )
  1254. {
  1255. //Don't holster this weapon if we're holding onto something
  1256. if ( m_bActive )
  1257. return false;
  1258. ForceDrop();
  1259. DestroyEffects();
  1260. return BaseClass::Holster( pSwitchingTo );
  1261. }
  1262. //-----------------------------------------------------------------------------
  1263. // Purpose:
  1264. //-----------------------------------------------------------------------------
  1265. void CWeaponPhysCannon::DryFire( void )
  1266. {
  1267. SendWeaponAnim( ACT_VM_PRIMARYATTACK );
  1268. WeaponSound( EMPTY );
  1269. }
  1270. //-----------------------------------------------------------------------------
  1271. // Purpose:
  1272. //-----------------------------------------------------------------------------
  1273. void CWeaponPhysCannon::PrimaryFireEffect( void )
  1274. {
  1275. CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
  1276. if ( pOwner == NULL )
  1277. return;
  1278. pOwner->ViewPunch( QAngle(-6, SharedRandomInt( "physcannonfire", -2,2) ,0) );
  1279. #ifndef CLIENT_DLL
  1280. color32 white = { 245, 245, 255, 32 };
  1281. UTIL_ScreenFade( pOwner, white, 0.1f, 0.0f, FFADE_IN );
  1282. #endif
  1283. WeaponSound( SINGLE );
  1284. }
  1285. #define MAX_KNOCKBACK_FORCE 128
  1286. //-----------------------------------------------------------------------------
  1287. // Punt non-physics
  1288. //-----------------------------------------------------------------------------
  1289. void CWeaponPhysCannon::PuntNonVPhysics( CBaseEntity *pEntity, const Vector &forward, trace_t &tr )
  1290. {
  1291. if ( m_hLastPuntedObject == pEntity && gpGlobals->curtime < m_flRepuntObjectTime )
  1292. return;
  1293. #ifndef CLIENT_DLL
  1294. CTakeDamageInfo info;
  1295. info.SetAttacker( GetOwner() );
  1296. info.SetInflictor( this );
  1297. info.SetDamage( 1.0f );
  1298. info.SetDamageType( DMG_CRUSH | DMG_PHYSGUN );
  1299. info.SetDamageForce( forward ); // Scale?
  1300. info.SetDamagePosition( tr.endpos );
  1301. m_hLastPuntedObject = pEntity;
  1302. m_flRepuntObjectTime = gpGlobals->curtime + 0.5f;
  1303. pEntity->DispatchTraceAttack( info, forward, &tr );
  1304. ApplyMultiDamage();
  1305. //Explosion effect
  1306. DoEffect( EFFECT_LAUNCH, &tr.endpos );
  1307. #endif
  1308. PrimaryFireEffect();
  1309. SendWeaponAnim( ACT_VM_SECONDARYATTACK );
  1310. m_nChangeState = ELEMENT_STATE_CLOSED;
  1311. m_flElementDebounce = gpGlobals->curtime + 0.5f;
  1312. m_flCheckSuppressTime = gpGlobals->curtime + 0.25f;
  1313. }
  1314. #ifndef CLIENT_DLL
  1315. //-----------------------------------------------------------------------------
  1316. // What happens when the physgun picks up something
  1317. //-----------------------------------------------------------------------------
  1318. void CWeaponPhysCannon::Physgun_OnPhysGunPickup( CBaseEntity *pEntity, CBasePlayer *pOwner, PhysGunPickup_t reason )
  1319. {
  1320. // If the target is debris, convert it to non-debris
  1321. if ( pEntity->GetCollisionGroup() == COLLISION_GROUP_DEBRIS )
  1322. {
  1323. // Interactive debris converts back to debris when it comes to rest
  1324. pEntity->SetCollisionGroup( COLLISION_GROUP_INTERACTIVE_DEBRIS );
  1325. }
  1326. Pickup_OnPhysGunPickup( pEntity, pOwner, reason );
  1327. }
  1328. #endif
  1329. //-----------------------------------------------------------------------------
  1330. // Punt vphysics
  1331. //-----------------------------------------------------------------------------
  1332. void CWeaponPhysCannon::PuntVPhysics( CBaseEntity *pEntity, const Vector &vecForward, trace_t &tr )
  1333. {
  1334. CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
  1335. if ( m_hLastPuntedObject == pEntity && gpGlobals->curtime < m_flRepuntObjectTime )
  1336. return;
  1337. m_hLastPuntedObject = pEntity;
  1338. m_flRepuntObjectTime = gpGlobals->curtime + 0.5f;
  1339. #ifndef CLIENT_DLL
  1340. CTakeDamageInfo info;
  1341. Vector forward = vecForward;
  1342. info.SetAttacker( GetOwner() );
  1343. info.SetInflictor( this );
  1344. info.SetDamage( 0.0f );
  1345. info.SetDamageType( DMG_PHYSGUN );
  1346. pEntity->DispatchTraceAttack( info, forward, &tr );
  1347. ApplyMultiDamage();
  1348. if ( Pickup_OnAttemptPhysGunPickup( pEntity, pOwner, PUNTED_BY_CANNON ) )
  1349. {
  1350. IPhysicsObject *pList[VPHYSICS_MAX_OBJECT_LIST_COUNT];
  1351. int listCount = pEntity->VPhysicsGetObjectList( pList, ARRAYSIZE(pList) );
  1352. if ( !listCount )
  1353. {
  1354. //FIXME: Do we want to do this if there's no physics object?
  1355. Physgun_OnPhysGunPickup( pEntity, pOwner, PUNTED_BY_CANNON );
  1356. DryFire();
  1357. return;
  1358. }
  1359. if( forward.z < 0 )
  1360. {
  1361. //reflect, but flatten the trajectory out a bit so it's easier to hit standing targets
  1362. forward.z *= -0.65f;
  1363. }
  1364. // NOTE: Do this first to enable motion (if disabled) - so forces will work
  1365. // Tell the object it's been punted
  1366. Physgun_OnPhysGunPickup( pEntity, pOwner, PUNTED_BY_CANNON );
  1367. // don't push vehicles that are attached to the world via fixed constraints
  1368. // they will just wiggle...
  1369. if ( (pList[0]->GetGameFlags() & FVPHYSICS_CONSTRAINT_STATIC) && pEntity->GetServerVehicle() )
  1370. {
  1371. forward.Init();
  1372. }
  1373. if ( !Pickup_ShouldPuntUseLaunchForces( pEntity, PHYSGUN_FORCE_PUNTED ) )
  1374. {
  1375. int i;
  1376. // limit mass to avoid punting REALLY huge things
  1377. float totalMass = 0;
  1378. for ( i = 0; i < listCount; i++ )
  1379. {
  1380. totalMass += pList[i]->GetMass();
  1381. }
  1382. float maxMass = 250;
  1383. IServerVehicle *pVehicle = pEntity->GetServerVehicle();
  1384. if ( pVehicle )
  1385. {
  1386. maxMass *= 2.5; // 625 for vehicles
  1387. }
  1388. float mass = MIN(totalMass, maxMass); // max 250kg of additional force
  1389. // Put some spin on the object
  1390. for ( i = 0; i < listCount; i++ )
  1391. {
  1392. const float hitObjectFactor = 0.5f;
  1393. const float otherObjectFactor = 1.0f - hitObjectFactor;
  1394. // Must be light enough
  1395. float ratio = pList[i]->GetMass() / totalMass;
  1396. if ( pList[i] == pEntity->VPhysicsGetObject() )
  1397. {
  1398. ratio += hitObjectFactor;
  1399. ratio = MIN(ratio,1.0f);
  1400. }
  1401. else
  1402. {
  1403. ratio *= otherObjectFactor;
  1404. }
  1405. pList[i]->ApplyForceCenter( forward * 15000.0f * ratio );
  1406. pList[i]->ApplyForceOffset( forward * mass * 600.0f * ratio, tr.endpos );
  1407. }
  1408. }
  1409. else
  1410. {
  1411. ApplyVelocityBasedForce( pEntity, vecForward );
  1412. }
  1413. }
  1414. #endif
  1415. // Add recoil
  1416. QAngle recoil = QAngle( random->RandomFloat( 1.0f, 2.0f ), random->RandomFloat( -1.0f, 1.0f ), 0 );
  1417. pOwner->ViewPunch( recoil );
  1418. //Explosion effect
  1419. DoEffect( EFFECT_LAUNCH, &tr.endpos );
  1420. PrimaryFireEffect();
  1421. SendWeaponAnim( ACT_VM_SECONDARYATTACK );
  1422. m_nChangeState = ELEMENT_STATE_CLOSED;
  1423. m_flElementDebounce = gpGlobals->curtime + 0.5f;
  1424. m_flCheckSuppressTime = gpGlobals->curtime + 0.25f;
  1425. // Don't allow the gun to regrab a thrown object!!
  1426. m_flNextSecondaryAttack = m_flNextPrimaryAttack = gpGlobals->curtime + 0.5f;
  1427. }
  1428. //-----------------------------------------------------------------------------
  1429. // Purpose: Applies velocity-based forces to throw the entity. This code is
  1430. // called from both punt and launch carried code.
  1431. // ASSUMES: that pEntity is a vphysics entity.
  1432. // Input : -
  1433. //-----------------------------------------------------------------------------
  1434. void CWeaponPhysCannon::ApplyVelocityBasedForce( CBaseEntity *pEntity, const Vector &forward )
  1435. {
  1436. #ifndef CLIENT_DLL
  1437. IPhysicsObject *pPhysicsObject = pEntity->VPhysicsGetObject();
  1438. Assert(pPhysicsObject); // Shouldn't ever get here with a non-vphysics object.
  1439. if (!pPhysicsObject)
  1440. return;
  1441. float flForceMax = physcannon_maxforce.GetFloat();
  1442. float flForce = flForceMax;
  1443. float mass = pPhysicsObject->GetMass();
  1444. if (mass > 100)
  1445. {
  1446. mass = MIN(mass, 1000);
  1447. float flForceMin = physcannon_minforce.GetFloat();
  1448. flForce = SimpleSplineRemapVal(mass, 100, 600, flForceMax, flForceMin);
  1449. }
  1450. Vector vVel = forward * flForce;
  1451. // FIXME: Josh needs to put a real value in for PHYSGUN_FORCE_PUNTED
  1452. AngularImpulse aVel = Pickup_PhysGunLaunchAngularImpulse( pEntity, PHYSGUN_FORCE_PUNTED );
  1453. pPhysicsObject->AddVelocity( &vVel, &aVel );
  1454. #endif
  1455. }
  1456. //-----------------------------------------------------------------------------
  1457. // Trace length
  1458. //-----------------------------------------------------------------------------
  1459. float CWeaponPhysCannon::TraceLength()
  1460. {
  1461. return physcannon_tracelength.GetFloat();
  1462. }
  1463. //-----------------------------------------------------------------------------
  1464. // Purpose:
  1465. //
  1466. // This mode is a toggle. Primary fire one time to pick up a physics object.
  1467. // With an object held, click primary fire again to drop object.
  1468. //-----------------------------------------------------------------------------
  1469. void CWeaponPhysCannon::PrimaryAttack( void )
  1470. {
  1471. if( m_flNextPrimaryAttack > gpGlobals->curtime )
  1472. return;
  1473. CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
  1474. if ( pOwner == NULL )
  1475. return;
  1476. if( m_bActive )
  1477. {
  1478. // Punch the object being held!!
  1479. Vector forward;
  1480. pOwner->EyeVectors( &forward );
  1481. // Validate the item is within punt range
  1482. CBaseEntity *pHeld = m_grabController.GetAttached();
  1483. Assert( pHeld != NULL );
  1484. if ( pHeld != NULL )
  1485. {
  1486. float heldDist = ( pHeld->WorldSpaceCenter() - pOwner->WorldSpaceCenter() ).Length();
  1487. if ( heldDist > physcannon_tracelength.GetFloat() )
  1488. {
  1489. // We can't punt this yet
  1490. DryFire();
  1491. return;
  1492. }
  1493. }
  1494. LaunchObject( forward, physcannon_maxforce.GetFloat() );
  1495. PrimaryFireEffect();
  1496. SendWeaponAnim( ACT_VM_SECONDARYATTACK );
  1497. return;
  1498. }
  1499. // If not active, just issue a physics punch in the world.
  1500. m_flNextPrimaryAttack = gpGlobals->curtime + 0.5f;
  1501. Vector forward;
  1502. pOwner->EyeVectors( &forward );
  1503. // NOTE: Notice we're *not* using the mega tracelength here
  1504. // when you have the mega cannon. Punting has shorter range.
  1505. Vector start, end;
  1506. start = pOwner->Weapon_ShootPosition();
  1507. float flPuntDistance = physcannon_tracelength.GetFloat();
  1508. VectorMA( start, flPuntDistance, forward, end );
  1509. CTraceFilterNoOwnerTest filter( pOwner, COLLISION_GROUP_NONE );
  1510. trace_t tr;
  1511. UTIL_TraceHull( start, end, -Vector(8,8,8), Vector(8,8,8), MASK_SHOT|CONTENTS_GRATE, &filter, &tr );
  1512. bool bValid = true;
  1513. CBaseEntity *pEntity = tr.m_pEnt;
  1514. if ( tr.fraction == 1 || !tr.m_pEnt || tr.m_pEnt->IsEFlagSet( EFL_NO_PHYSCANNON_INTERACTION ) )
  1515. {
  1516. bValid = false;
  1517. }
  1518. else if ( (pEntity->GetMoveType() != MOVETYPE_VPHYSICS) && ( pEntity->m_takedamage == DAMAGE_NO ) )
  1519. {
  1520. bValid = false;
  1521. }
  1522. // If the entity we've hit is invalid, try a traceline instead
  1523. if ( !bValid )
  1524. {
  1525. UTIL_TraceLine( start, end, MASK_SHOT|CONTENTS_GRATE, &filter, &tr );
  1526. if ( tr.fraction == 1 || !tr.m_pEnt || tr.m_pEnt->IsEFlagSet( EFL_NO_PHYSCANNON_INTERACTION ) )
  1527. {
  1528. // Play dry-fire sequence
  1529. DryFire();
  1530. return;
  1531. }
  1532. pEntity = tr.m_pEnt;
  1533. }
  1534. // See if we hit something
  1535. if ( pEntity->GetMoveType() != MOVETYPE_VPHYSICS )
  1536. {
  1537. if ( pEntity->m_takedamage == DAMAGE_NO )
  1538. {
  1539. DryFire();
  1540. return;
  1541. }
  1542. if( GetOwner()->IsPlayer() )
  1543. {
  1544. // Don't let the player zap any NPC's except regular antlions and headcrabs.
  1545. if( pEntity->IsPlayer() )
  1546. {
  1547. DryFire();
  1548. return;
  1549. }
  1550. }
  1551. PuntNonVPhysics( pEntity, forward, tr );
  1552. }
  1553. else
  1554. {
  1555. if ( pEntity->VPhysicsIsFlesh( ) )
  1556. {
  1557. DryFire();
  1558. return;
  1559. }
  1560. PuntVPhysics( pEntity, forward, tr );
  1561. }
  1562. }
  1563. //-----------------------------------------------------------------------------
  1564. // Purpose: Click secondary attack whilst holding an object to hurl it.
  1565. //-----------------------------------------------------------------------------
  1566. void CWeaponPhysCannon::SecondaryAttack( void )
  1567. {
  1568. #ifndef CLIENT_DLL
  1569. if ( m_flNextSecondaryAttack > gpGlobals->curtime )
  1570. return;
  1571. CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
  1572. if ( pOwner == NULL )
  1573. return;
  1574. // See if we should drop a held item
  1575. if ( ( m_bActive ) && ( pOwner->m_afButtonPressed & IN_ATTACK2 ) )
  1576. {
  1577. // Drop the held object
  1578. m_flNextPrimaryAttack = gpGlobals->curtime + 0.5;
  1579. m_flNextSecondaryAttack = gpGlobals->curtime + 0.5;
  1580. DetachObject();
  1581. DoEffect( EFFECT_READY );
  1582. SendWeaponAnim( ACT_VM_PRIMARYATTACK );
  1583. }
  1584. else
  1585. {
  1586. // Otherwise pick it up
  1587. FindObjectResult_t result = FindObject();
  1588. switch ( result )
  1589. {
  1590. case OBJECT_FOUND:
  1591. WeaponSound( SPECIAL1 );
  1592. SendWeaponAnim( ACT_VM_PRIMARYATTACK );
  1593. m_flNextSecondaryAttack = gpGlobals->curtime + 0.5f;
  1594. // We found an object. Debounce the button
  1595. m_nAttack2Debounce |= pOwner->m_nButtons;
  1596. break;
  1597. case OBJECT_NOT_FOUND:
  1598. m_flNextSecondaryAttack = gpGlobals->curtime + 0.1f;
  1599. CloseElements();
  1600. break;
  1601. case OBJECT_BEING_DETACHED:
  1602. m_flNextSecondaryAttack = gpGlobals->curtime + 0.01f;
  1603. break;
  1604. }
  1605. DoEffect( EFFECT_HOLDING );
  1606. }
  1607. #endif
  1608. }
  1609. //-----------------------------------------------------------------------------
  1610. // Purpose:
  1611. //-----------------------------------------------------------------------------
  1612. void CWeaponPhysCannon::WeaponIdle( void )
  1613. {
  1614. if ( HasWeaponIdleTimeElapsed() )
  1615. {
  1616. if ( m_bActive )
  1617. {
  1618. //Shake when holding an item
  1619. SendWeaponAnim( ACT_VM_RELOAD );
  1620. }
  1621. else
  1622. {
  1623. //Otherwise idle simply
  1624. SendWeaponAnim( ACT_VM_IDLE );
  1625. }
  1626. }
  1627. }
  1628. #ifndef CLIENT_DLL
  1629. //-----------------------------------------------------------------------------
  1630. // Purpose:
  1631. // Input : *pObject -
  1632. //-----------------------------------------------------------------------------
  1633. bool CWeaponPhysCannon::AttachObject( CBaseEntity *pObject, const Vector &vPosition )
  1634. {
  1635. if ( m_bActive )
  1636. return false;
  1637. if ( CanPickupObject( pObject ) == false )
  1638. return false;
  1639. m_grabController.SetIgnorePitch( false );
  1640. m_grabController.SetAngleAlignment( 0 );
  1641. IPhysicsObject *pPhysics = pObject->VPhysicsGetObject();
  1642. // Must be valid
  1643. if ( !pPhysics )
  1644. return false;
  1645. CHL2MP_Player *pOwner = (CHL2MP_Player *)ToBasePlayer( GetOwner() );
  1646. m_bActive = true;
  1647. if( pOwner )
  1648. {
  1649. // NOTE: This can change the mass; so it must be done before max speed setting
  1650. Physgun_OnPhysGunPickup( pObject, pOwner, PICKED_UP_BY_CANNON );
  1651. }
  1652. // NOTE :This must happen after OnPhysGunPickup because that can change the mass
  1653. m_grabController.AttachEntity( pOwner, pObject, pPhysics, false, vPosition, false );
  1654. m_hAttachedObject = pObject;
  1655. m_attachedPositionObjectSpace = m_grabController.m_attachedPositionObjectSpace;
  1656. m_attachedAnglesPlayerSpace = m_grabController.m_attachedAnglesPlayerSpace;
  1657. m_bResetOwnerEntity = false;
  1658. if ( m_hAttachedObject->GetOwnerEntity() == NULL )
  1659. {
  1660. m_hAttachedObject->SetOwnerEntity( pOwner );
  1661. m_bResetOwnerEntity = true;
  1662. }
  1663. /* if( pOwner )
  1664. {
  1665. pOwner->EnableSprint( false );
  1666. float loadWeight = ( 1.0f - GetLoadPercentage() );
  1667. float maxSpeed = hl2_walkspeed.GetFloat() + ( ( hl2_normspeed.GetFloat() - hl2_walkspeed.GetFloat() ) * loadWeight );
  1668. //Msg( "Load perc: %f -- Movement speed: %f/%f\n", loadWeight, maxSpeed, hl2_normspeed.GetFloat() );
  1669. pOwner->SetMaxSpeed( maxSpeed );
  1670. }*/
  1671. // Don't drop again for a slight delay, in case they were pulling objects near them
  1672. m_flNextSecondaryAttack = gpGlobals->curtime + 0.4f;
  1673. DoEffect( EFFECT_HOLDING );
  1674. OpenElements();
  1675. if ( GetMotorSound() )
  1676. {
  1677. (CSoundEnvelopeController::GetController()).Play( GetMotorSound(), 0.0f, 50 );
  1678. (CSoundEnvelopeController::GetController()).SoundChangePitch( GetMotorSound(), 100, 0.5f );
  1679. (CSoundEnvelopeController::GetController()).SoundChangeVolume( GetMotorSound(), 0.8f, 0.5f );
  1680. }
  1681. return true;
  1682. }
  1683. CWeaponPhysCannon::FindObjectResult_t CWeaponPhysCannon::FindObject( void )
  1684. {
  1685. CBasePlayer *pPlayer = ToBasePlayer( GetOwner() );
  1686. Assert( pPlayer );
  1687. if ( pPlayer == NULL )
  1688. return OBJECT_NOT_FOUND;
  1689. Vector forward;
  1690. pPlayer->EyeVectors( &forward );
  1691. // Setup our positions
  1692. Vector start = pPlayer->Weapon_ShootPosition();
  1693. float testLength = TraceLength() * 4.0f;
  1694. Vector end = start + forward * testLength;
  1695. // Try to find an object by looking straight ahead
  1696. trace_t tr;
  1697. CTraceFilterNoOwnerTest filter( pPlayer, COLLISION_GROUP_NONE );
  1698. UTIL_TraceLine( start, end, MASK_SHOT|CONTENTS_GRATE, &filter, &tr );
  1699. // Try again with a hull trace
  1700. if ( ( tr.fraction == 1.0 ) || ( tr.m_pEnt == NULL ) || ( tr.m_pEnt->IsWorld() ) )
  1701. {
  1702. UTIL_TraceHull( start, end, -Vector(4,4,4), Vector(4,4,4), MASK_SHOT|CONTENTS_GRATE, &filter, &tr );
  1703. }
  1704. CBaseEntity *pEntity = tr.m_pEnt ? tr.m_pEnt->GetRootMoveParent() : NULL;
  1705. bool bAttach = false;
  1706. bool bPull = false;
  1707. // If we hit something, pick it up or pull it
  1708. if ( ( tr.fraction != 1.0f ) && ( tr.m_pEnt ) && ( tr.m_pEnt->IsWorld() == false ) )
  1709. {
  1710. // Attempt to attach if within range
  1711. if ( tr.fraction <= 0.25f )
  1712. {
  1713. bAttach = true;
  1714. }
  1715. else if ( tr.fraction > 0.25f )
  1716. {
  1717. bPull = true;
  1718. }
  1719. }
  1720. // Find anything within a general cone in front
  1721. CBaseEntity *pConeEntity = NULL;
  1722. if (!bAttach && !bPull)
  1723. {
  1724. pConeEntity = FindObjectInCone( start, forward, physcannon_cone.GetFloat() );
  1725. }
  1726. if ( pConeEntity )
  1727. {
  1728. pEntity = pConeEntity;
  1729. // If the object is near, grab it. Else, pull it a bit.
  1730. if ( pEntity->WorldSpaceCenter().DistToSqr( start ) <= (testLength * testLength) )
  1731. {
  1732. bAttach = true;
  1733. }
  1734. else
  1735. {
  1736. bPull = true;
  1737. }
  1738. }
  1739. if ( CanPickupObject( pEntity ) == false )
  1740. {
  1741. // Make a noise to signify we can't pick this up
  1742. if ( !m_flLastDenySoundPlayed )
  1743. {
  1744. m_flLastDenySoundPlayed = true;
  1745. WeaponSound( SPECIAL3 );
  1746. }
  1747. return OBJECT_NOT_FOUND;
  1748. }
  1749. // Check to see if the object is constrained + needs to be ripped off...
  1750. CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
  1751. if ( !Pickup_OnAttemptPhysGunPickup( pEntity, pOwner, PICKED_UP_BY_CANNON ) )
  1752. return OBJECT_BEING_DETACHED;
  1753. if ( bAttach )
  1754. {
  1755. return AttachObject( pEntity, tr.endpos ) ? OBJECT_FOUND : OBJECT_NOT_FOUND;
  1756. }
  1757. if ( !bPull )
  1758. return OBJECT_NOT_FOUND;
  1759. // FIXME: This needs to be run through the CanPickupObject logic
  1760. IPhysicsObject *pObj = pEntity->VPhysicsGetObject();
  1761. if ( !pObj )
  1762. return OBJECT_NOT_FOUND;
  1763. // If we're too far, simply start to pull the object towards us
  1764. Vector pullDir = start - pEntity->WorldSpaceCenter();
  1765. VectorNormalize( pullDir );
  1766. pullDir *= physcannon_pullforce.GetFloat();
  1767. float mass = PhysGetEntityMass( pEntity );
  1768. if ( mass < 50.0f )
  1769. {
  1770. pullDir *= (mass + 0.5) * (1/50.0f);
  1771. }
  1772. // Nudge it towards us
  1773. pObj->ApplyForceCenter( pullDir );
  1774. return OBJECT_NOT_FOUND;
  1775. }
  1776. //-----------------------------------------------------------------------------
  1777. //-----------------------------------------------------------------------------
  1778. CBaseEntity *CWeaponPhysCannon::FindObjectInCone( const Vector &vecOrigin, const Vector &vecDir, float flCone )
  1779. {
  1780. // Find the nearest physics-based item in a cone in front of me.
  1781. CBaseEntity *list[256];
  1782. float flNearestDist = TraceLength() + 1.0;
  1783. Vector mins = vecOrigin - Vector( flNearestDist, flNearestDist, flNearestDist );
  1784. Vector maxs = vecOrigin + Vector( flNearestDist, flNearestDist, flNearestDist );
  1785. CBaseEntity *pNearest = NULL;
  1786. int count = UTIL_EntitiesInBox( list, 256, mins, maxs, 0 );
  1787. for( int i = 0 ; i < count ; i++ )
  1788. {
  1789. if ( !list[ i ]->VPhysicsGetObject() )
  1790. continue;
  1791. // Closer than other objects
  1792. Vector los = ( list[ i ]->WorldSpaceCenter() - vecOrigin );
  1793. float flDist = VectorNormalize( los );
  1794. if( flDist >= flNearestDist )
  1795. continue;
  1796. // Cull to the cone
  1797. if ( DotProduct( los, vecDir ) <= flCone )
  1798. continue;
  1799. // Make sure it isn't occluded!
  1800. trace_t tr;
  1801. CTraceFilterNoOwnerTest filter( GetOwner(), COLLISION_GROUP_NONE );
  1802. UTIL_TraceLine( vecOrigin, list[ i ]->WorldSpaceCenter(), MASK_SHOT|CONTENTS_GRATE, &filter, &tr );
  1803. if( tr.m_pEnt == list[ i ] )
  1804. {
  1805. flNearestDist = flDist;
  1806. pNearest = list[ i ];
  1807. }
  1808. }
  1809. return pNearest;
  1810. }
  1811. #endif
  1812. //-----------------------------------------------------------------------------
  1813. //-----------------------------------------------------------------------------
  1814. bool CGrabController::UpdateObject( CBasePlayer *pPlayer, float flError )
  1815. {
  1816. CBaseEntity *pEntity = GetAttached();
  1817. if ( !pEntity )
  1818. return false;
  1819. if ( ComputeError() > flError )
  1820. return false;
  1821. if ( pPlayer->GetGroundEntity() == pEntity )
  1822. return false;
  1823. if (!pEntity->VPhysicsGetObject() )
  1824. return false;
  1825. //Adrian: Oops, our object became motion disabled, let go!
  1826. IPhysicsObject *pPhys = pEntity->VPhysicsGetObject();
  1827. if ( pPhys && pPhys->IsMoveable() == false )
  1828. {
  1829. return false;
  1830. }
  1831. if ( m_frameCount == gpGlobals->framecount )
  1832. {
  1833. return true;
  1834. }
  1835. m_frameCount = gpGlobals->framecount;
  1836. Vector forward, right, up;
  1837. QAngle playerAngles = pPlayer->EyeAngles();
  1838. float pitch = AngleDistance(playerAngles.x,0);
  1839. playerAngles.x = clamp( pitch, -75, 75 );
  1840. AngleVectors( playerAngles, &forward, &right, &up );
  1841. // Now clamp a sphere of object radius at end to the player's bbox
  1842. Vector radial = physcollision->CollideGetExtent( pPhys->GetCollide(), vec3_origin, pEntity->GetAbsAngles(), -forward );
  1843. Vector player2d = pPlayer->CollisionProp()->OBBMaxs();
  1844. float playerRadius = player2d.Length2D();
  1845. float flDot = DotProduct( forward, radial );
  1846. float radius = playerRadius + fabs( flDot );
  1847. float distance = 24 + ( radius * 2.0f );
  1848. Vector start = pPlayer->Weapon_ShootPosition();
  1849. Vector end = start + ( forward * distance );
  1850. trace_t tr;
  1851. CTraceFilterSkipTwoEntities traceFilter( pPlayer, pEntity, COLLISION_GROUP_NONE );
  1852. Ray_t ray;
  1853. ray.Init( start, end );
  1854. enginetrace->TraceRay( ray, MASK_SOLID_BRUSHONLY, &traceFilter, &tr );
  1855. if ( tr.fraction < 0.5 )
  1856. {
  1857. end = start + forward * (radius*0.5f);
  1858. }
  1859. else if ( tr.fraction <= 1.0f )
  1860. {
  1861. end = start + forward * ( distance - radius );
  1862. }
  1863. Vector playerMins, playerMaxs, nearest;
  1864. pPlayer->CollisionProp()->WorldSpaceAABB( &playerMins, &playerMaxs );
  1865. Vector playerLine = pPlayer->CollisionProp()->WorldSpaceCenter();
  1866. CalcClosestPointOnLine( end, playerLine+Vector(0,0,playerMins.z), playerLine+Vector(0,0,playerMaxs.z), nearest, NULL );
  1867. Vector delta = end - nearest;
  1868. float len = VectorNormalize(delta);
  1869. if ( len < radius )
  1870. {
  1871. end = nearest + radius * delta;
  1872. }
  1873. QAngle angles = TransformAnglesFromPlayerSpace( m_attachedAnglesPlayerSpace, pPlayer );
  1874. //Show overlays of radius
  1875. if ( g_debug_physcannon.GetBool() )
  1876. {
  1877. #ifdef CLIENT_DLL
  1878. debugoverlay->AddBoxOverlay( end, -Vector( 2,2,2 ), Vector(2,2,2), angles, 0, 255, 255, true, 0 );
  1879. debugoverlay->AddBoxOverlay( GetAttached()->WorldSpaceCenter(),
  1880. -Vector( radius, radius, radius),
  1881. Vector( radius, radius, radius ),
  1882. angles,
  1883. 255, 255, 0,
  1884. true,
  1885. 0.0f );
  1886. #else
  1887. NDebugOverlay::Box( end, -Vector( 2,2,2 ), Vector(2,2,2), 0, 255, 0, true, 0 );
  1888. NDebugOverlay::Box( GetAttached()->WorldSpaceCenter(),
  1889. -Vector( radius+5, radius+5, radius+5),
  1890. Vector( radius+5, radius+5, radius+5 ),
  1891. 255, 0, 0,
  1892. true,
  1893. 0.0f );
  1894. #endif
  1895. }
  1896. #ifndef CLIENT_DLL
  1897. // If it has a preferred orientation, update to ensure we're still oriented correctly.
  1898. Pickup_GetPreferredCarryAngles( pEntity, pPlayer, pPlayer->EntityToWorldTransform(), angles );
  1899. // We may be holding a prop that has preferred carry angles
  1900. if ( m_bHasPreferredCarryAngles )
  1901. {
  1902. matrix3x4_t tmp;
  1903. ComputePlayerMatrix( pPlayer, tmp );
  1904. angles = TransformAnglesToWorldSpace( m_vecPreferredCarryAngles, tmp );
  1905. }
  1906. #endif
  1907. matrix3x4_t attachedToWorld;
  1908. Vector offset;
  1909. AngleMatrix( angles, attachedToWorld );
  1910. VectorRotate( m_attachedPositionObjectSpace, attachedToWorld, offset );
  1911. SetTargetPosition( end - offset, angles );
  1912. return true;
  1913. }
  1914. void CWeaponPhysCannon::UpdateObject( void )
  1915. {
  1916. CBasePlayer *pPlayer = ToBasePlayer( GetOwner() );
  1917. Assert( pPlayer );
  1918. float flError = 12;
  1919. if ( !m_grabController.UpdateObject( pPlayer, flError ) )
  1920. {
  1921. DetachObject();
  1922. return;
  1923. }
  1924. }
  1925. //-----------------------------------------------------------------------------
  1926. //-----------------------------------------------------------------------------
  1927. void CWeaponPhysCannon::DetachObject( bool playSound, bool wasLaunched )
  1928. {
  1929. #ifndef CLIENT_DLL
  1930. if ( m_bActive == false )
  1931. return;
  1932. CHL2MP_Player *pOwner = (CHL2MP_Player *)ToBasePlayer( GetOwner() );
  1933. if( pOwner != NULL )
  1934. {
  1935. pOwner->EnableSprint( true );
  1936. pOwner->SetMaxSpeed( hl2_normspeed.GetFloat() );
  1937. }
  1938. CBaseEntity *pObject = m_grabController.GetAttached();
  1939. m_grabController.DetachEntity( wasLaunched );
  1940. if ( pObject != NULL )
  1941. {
  1942. Pickup_OnPhysGunDrop( pObject, pOwner, wasLaunched ? LAUNCHED_BY_CANNON : DROPPED_BY_CANNON );
  1943. }
  1944. // Stop our looping sound
  1945. if ( GetMotorSound() )
  1946. {
  1947. (CSoundEnvelopeController::GetController()).SoundChangeVolume( GetMotorSound(), 0.0f, 1.0f );
  1948. (CSoundEnvelopeController::GetController()).SoundChangePitch( GetMotorSound(), 50, 1.0f );
  1949. }
  1950. if ( pObject && m_bResetOwnerEntity == true )
  1951. {
  1952. pObject->SetOwnerEntity( NULL );
  1953. }
  1954. m_bActive = false;
  1955. m_hAttachedObject = NULL;
  1956. if ( playSound )
  1957. {
  1958. //Play the detach sound
  1959. WeaponSound( MELEE_MISS );
  1960. }
  1961. #else
  1962. m_grabController.DetachEntity( wasLaunched );
  1963. if ( m_hAttachedObject )
  1964. {
  1965. m_hAttachedObject->VPhysicsDestroyObject();
  1966. }
  1967. #endif
  1968. }
  1969. #ifdef CLIENT_DLL
  1970. void CWeaponPhysCannon::ManagePredictedObject( void )
  1971. {
  1972. CBaseEntity *pAttachedObject = m_hAttachedObject.Get();
  1973. if ( m_hAttachedObject )
  1974. {
  1975. // NOTE :This must happen after OnPhysGunPickup because that can change the mass
  1976. if ( pAttachedObject != GetGrabController().GetAttached() )
  1977. {
  1978. IPhysicsObject *pPhysics = pAttachedObject->VPhysicsGetObject();
  1979. if ( pPhysics == NULL )
  1980. {
  1981. solid_t tmpSolid;
  1982. PhysModelParseSolid( tmpSolid, m_hAttachedObject, pAttachedObject->GetModelIndex() );
  1983. pAttachedObject->VPhysicsInitNormal( SOLID_VPHYSICS, 0, false, &tmpSolid );
  1984. }
  1985. pPhysics = pAttachedObject->VPhysicsGetObject();
  1986. if ( pPhysics )
  1987. {
  1988. m_grabController.SetIgnorePitch( false );
  1989. m_grabController.SetAngleAlignment( 0 );
  1990. GetGrabController().AttachEntity( ToBasePlayer( GetOwner() ), pAttachedObject, pPhysics, false, vec3_origin, false );
  1991. GetGrabController().m_attachedPositionObjectSpace = m_attachedPositionObjectSpace;
  1992. GetGrabController().m_attachedAnglesPlayerSpace = m_attachedAnglesPlayerSpace;
  1993. }
  1994. }
  1995. }
  1996. else
  1997. {
  1998. if ( m_hOldAttachedObject && m_hOldAttachedObject->VPhysicsGetObject() )
  1999. {
  2000. GetGrabController().DetachEntity( false );
  2001. m_hOldAttachedObject->VPhysicsDestroyObject();
  2002. }
  2003. }
  2004. m_hOldAttachedObject = m_hAttachedObject;
  2005. }
  2006. #endif
  2007. #ifdef CLIENT_DLL
  2008. //-----------------------------------------------------------------------------
  2009. // Purpose: Update the pose parameter for the gun
  2010. //-----------------------------------------------------------------------------
  2011. void CWeaponPhysCannon::UpdateElementPosition( void )
  2012. {
  2013. CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
  2014. float flElementPosition = m_ElementParameter.Interp( gpGlobals->curtime );
  2015. if ( ShouldDrawUsingViewModel() )
  2016. {
  2017. if ( pOwner != NULL )
  2018. {
  2019. CBaseViewModel *vm = pOwner->GetViewModel();
  2020. if ( vm != NULL )
  2021. {
  2022. vm->SetPoseParameter( "active", flElementPosition );
  2023. }
  2024. }
  2025. }
  2026. else
  2027. {
  2028. SetPoseParameter( "active", flElementPosition );
  2029. }
  2030. }
  2031. //-----------------------------------------------------------------------------
  2032. // Purpose: Think function for the client
  2033. //-----------------------------------------------------------------------------
  2034. void CWeaponPhysCannon::ClientThink( void )
  2035. {
  2036. // Update our elements visually
  2037. UpdateElementPosition();
  2038. // Update our effects
  2039. DoEffectIdle();
  2040. }
  2041. #endif // CLIENT_DLL
  2042. //-----------------------------------------------------------------------------
  2043. //-----------------------------------------------------------------------------
  2044. void CWeaponPhysCannon::ItemPreFrame()
  2045. {
  2046. BaseClass::ItemPreFrame();
  2047. #ifdef CLIENT_DLL
  2048. C_BasePlayer *localplayer = C_BasePlayer::GetLocalPlayer();
  2049. if ( localplayer && !localplayer->IsObserver() )
  2050. ManagePredictedObject();
  2051. #endif
  2052. // Update the object if the weapon is switched on.
  2053. if( m_bActive )
  2054. {
  2055. UpdateObject();
  2056. }
  2057. }
  2058. //-----------------------------------------------------------------------------
  2059. // Purpose:
  2060. //-----------------------------------------------------------------------------
  2061. void CWeaponPhysCannon::CheckForTarget( void )
  2062. {
  2063. #ifndef CLIENT_DLL
  2064. //See if we're suppressing this
  2065. if ( m_flCheckSuppressTime > gpGlobals->curtime )
  2066. return;
  2067. // holstered
  2068. if ( IsEffectActive( EF_NODRAW ) )
  2069. return;
  2070. CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
  2071. if ( pOwner == NULL )
  2072. return;
  2073. if ( m_bActive )
  2074. return;
  2075. Vector aimDir;
  2076. pOwner->EyeVectors( &aimDir );
  2077. Vector startPos = pOwner->Weapon_ShootPosition();
  2078. Vector endPos;
  2079. VectorMA( startPos, TraceLength(), aimDir, endPos );
  2080. trace_t tr;
  2081. UTIL_TraceHull( startPos, endPos, -Vector(4,4,4), Vector(4,4,4), MASK_SHOT|CONTENTS_GRATE, pOwner, COLLISION_GROUP_NONE, &tr );
  2082. if ( ( tr.fraction != 1.0f ) && ( tr.m_pEnt != NULL ) )
  2083. {
  2084. // FIXME: Try just having the elements always open when pointed at a physics object
  2085. if ( CanPickupObject( tr.m_pEnt ) || Pickup_ForcePhysGunOpen( tr.m_pEnt, pOwner ) )
  2086. // if ( ( tr.m_pEnt->VPhysicsGetObject() != NULL ) && ( tr.m_pEnt->GetMoveType() == MOVETYPE_VPHYSICS ) )
  2087. {
  2088. m_nChangeState = ELEMENT_STATE_NONE;
  2089. OpenElements();
  2090. return;
  2091. }
  2092. }
  2093. // Close the elements after a delay to prevent overact state switching
  2094. if ( ( m_flElementDebounce < gpGlobals->curtime ) && ( m_nChangeState == ELEMENT_STATE_NONE ) )
  2095. {
  2096. m_nChangeState = ELEMENT_STATE_CLOSED;
  2097. m_flElementDebounce = gpGlobals->curtime + 0.5f;
  2098. }
  2099. #endif
  2100. }
  2101. //-----------------------------------------------------------------------------
  2102. // Purpose: Idle effect (pulsing)
  2103. //-----------------------------------------------------------------------------
  2104. void CWeaponPhysCannon::DoEffectIdle( void )
  2105. {
  2106. #ifdef CLIENT_DLL
  2107. StartEffects();
  2108. //if ( ShouldDrawUsingViewModel() )
  2109. {
  2110. // Turn on the glow sprites
  2111. for ( int i = PHYSCANNON_GLOW1; i < (PHYSCANNON_GLOW1+NUM_GLOW_SPRITES); i++ )
  2112. {
  2113. m_Parameters[i].GetScale().SetAbsolute( random->RandomFloat( 0.075f, 0.05f ) * SPRITE_SCALE );
  2114. m_Parameters[i].GetAlpha().SetAbsolute( random->RandomInt( 24, 32 ) );
  2115. }
  2116. // Turn on the glow sprites
  2117. for ( int i = PHYSCANNON_ENDCAP1; i < (PHYSCANNON_ENDCAP1+NUM_ENDCAP_SPRITES); i++ )
  2118. {
  2119. m_Parameters[i].GetScale().SetAbsolute( random->RandomFloat( 3, 5 ) );
  2120. m_Parameters[i].GetAlpha().SetAbsolute( random->RandomInt( 200, 255 ) );
  2121. }
  2122. if ( m_EffectState != EFFECT_HOLDING )
  2123. {
  2124. // Turn beams off
  2125. m_Beams[0].SetVisible( false );
  2126. m_Beams[1].SetVisible( false );
  2127. m_Beams[2].SetVisible( false );
  2128. }
  2129. }
  2130. /*
  2131. else
  2132. {
  2133. // Turn on the glow sprites
  2134. for ( int i = PHYSCANNON_GLOW1; i < (PHYSCANNON_GLOW1+NUM_GLOW_SPRITES); i++ )
  2135. {
  2136. m_Parameters[i].GetScale().SetAbsolute( random->RandomFloat( 0.075f, 0.05f ) * SPRITE_SCALE );
  2137. m_Parameters[i].GetAlpha().SetAbsolute( random->RandomInt( 24, 32 ) );
  2138. }
  2139. // Turn on the glow sprites
  2140. for ( i = PHYSCANNON_ENDCAP1; i < (PHYSCANNON_ENDCAP1+NUM_ENDCAP_SPRITES); i++ )
  2141. {
  2142. m_Parameters[i].GetScale().SetAbsolute( random->RandomFloat( 3, 5 ) );
  2143. m_Parameters[i].GetAlpha().SetAbsolute( random->RandomInt( 200, 255 ) );
  2144. }
  2145. if ( m_EffectState != EFFECT_HOLDING )
  2146. {
  2147. // Turn beams off
  2148. m_Beams[0].SetVisible( false );
  2149. m_Beams[1].SetVisible( false );
  2150. m_Beams[2].SetVisible( false );
  2151. }
  2152. }
  2153. */
  2154. #endif
  2155. }
  2156. //-----------------------------------------------------------------------------
  2157. //-----------------------------------------------------------------------------
  2158. void CWeaponPhysCannon::ItemPostFrame()
  2159. {
  2160. CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
  2161. if ( pOwner == NULL )
  2162. {
  2163. // We found an object. Debounce the button
  2164. m_nAttack2Debounce = 0;
  2165. return;
  2166. }
  2167. //Check for object in pickup range
  2168. if ( m_bActive == false )
  2169. {
  2170. CheckForTarget();
  2171. if ( ( m_flElementDebounce < gpGlobals->curtime ) && ( m_nChangeState != ELEMENT_STATE_NONE ) )
  2172. {
  2173. if ( m_nChangeState == ELEMENT_STATE_OPEN )
  2174. {
  2175. OpenElements();
  2176. }
  2177. else if ( m_nChangeState == ELEMENT_STATE_CLOSED )
  2178. {
  2179. CloseElements();
  2180. }
  2181. m_nChangeState = ELEMENT_STATE_NONE;
  2182. }
  2183. }
  2184. // NOTE: Attack2 will be considered to be pressed until the first item is picked up.
  2185. int nAttack2Mask = pOwner->m_nButtons & (~m_nAttack2Debounce);
  2186. if ( nAttack2Mask & IN_ATTACK2 )
  2187. {
  2188. SecondaryAttack();
  2189. }
  2190. else
  2191. {
  2192. // Reset our debouncer
  2193. m_flLastDenySoundPlayed = false;
  2194. if ( m_bActive == false )
  2195. {
  2196. DoEffect( EFFECT_READY );
  2197. }
  2198. }
  2199. if (( pOwner->m_nButtons & IN_ATTACK2 ) == 0 )
  2200. {
  2201. m_nAttack2Debounce = 0;
  2202. }
  2203. if ( pOwner->m_nButtons & IN_ATTACK )
  2204. {
  2205. PrimaryAttack();
  2206. }
  2207. else
  2208. {
  2209. WeaponIdle();
  2210. }
  2211. }
  2212. //-----------------------------------------------------------------------------
  2213. //-----------------------------------------------------------------------------
  2214. #define PHYSCANNON_DANGER_SOUND_RADIUS 128
  2215. void CWeaponPhysCannon::LaunchObject( const Vector &vecDir, float flForce )
  2216. {
  2217. CBaseEntity *pObject = m_grabController.GetAttached();
  2218. if ( !(m_hLastPuntedObject == pObject && gpGlobals->curtime < m_flRepuntObjectTime) )
  2219. {
  2220. // FIRE!!!
  2221. if( pObject != NULL )
  2222. {
  2223. DetachObject( false, true );
  2224. m_hLastPuntedObject = pObject;
  2225. m_flRepuntObjectTime = gpGlobals->curtime + 0.5f;
  2226. // Launch
  2227. ApplyVelocityBasedForce( pObject, vecDir );
  2228. // Don't allow the gun to regrab a thrown object!!
  2229. m_flNextSecondaryAttack = m_flNextPrimaryAttack = gpGlobals->curtime + 0.5;
  2230. Vector center = pObject->WorldSpaceCenter();
  2231. //Do repulse effect
  2232. DoEffect( EFFECT_LAUNCH, &center );
  2233. m_hAttachedObject = NULL;
  2234. m_bActive = false;
  2235. }
  2236. }
  2237. // Stop our looping sound
  2238. if ( GetMotorSound() )
  2239. {
  2240. (CSoundEnvelopeController::GetController()).SoundChangeVolume( GetMotorSound(), 0.0f, 1.0f );
  2241. (CSoundEnvelopeController::GetController()).SoundChangePitch( GetMotorSound(), 50, 1.0f );
  2242. }
  2243. //Close the elements and suppress checking for a bit
  2244. m_nChangeState = ELEMENT_STATE_CLOSED;
  2245. m_flElementDebounce = gpGlobals->curtime + 0.1f;
  2246. m_flCheckSuppressTime = gpGlobals->curtime + 0.25f;
  2247. }
  2248. bool UTIL_IsCombineBall( CBaseEntity *pEntity );
  2249. //-----------------------------------------------------------------------------
  2250. // Purpose:
  2251. // Input : *pTarget -
  2252. // Output : Returns true on success, false on failure.
  2253. //-----------------------------------------------------------------------------
  2254. bool CWeaponPhysCannon::CanPickupObject( CBaseEntity *pTarget )
  2255. {
  2256. #ifndef CLIENT_DLL
  2257. if ( pTarget == NULL )
  2258. return false;
  2259. if ( pTarget->GetBaseAnimating() && pTarget->GetBaseAnimating()->IsDissolving() )
  2260. return false;
  2261. if ( pTarget->IsEFlagSet( EFL_NO_PHYSCANNON_INTERACTION ) )
  2262. return false;
  2263. CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
  2264. if ( pOwner && pOwner->GetGroundEntity() == pTarget )
  2265. return false;
  2266. if ( pTarget->VPhysicsIsFlesh( ) )
  2267. return false;
  2268. IPhysicsObject *pObj = pTarget->VPhysicsGetObject();
  2269. if ( pObj && pObj->GetGameFlags() & FVPHYSICS_PLAYER_HELD )
  2270. return false;
  2271. if ( UTIL_IsCombineBall( pTarget ) )
  2272. {
  2273. return CBasePlayer::CanPickupObject( pTarget, 0, 0 );
  2274. }
  2275. return CBasePlayer::CanPickupObject( pTarget, physcannon_maxmass.GetFloat(), 0 );
  2276. #else
  2277. return false;
  2278. #endif
  2279. }
  2280. //-----------------------------------------------------------------------------
  2281. // Purpose:
  2282. //-----------------------------------------------------------------------------
  2283. void CWeaponPhysCannon::OpenElements( void )
  2284. {
  2285. if ( m_bOpen )
  2286. return;
  2287. WeaponSound( SPECIAL2 );
  2288. CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
  2289. if ( pOwner == NULL )
  2290. return;
  2291. SendWeaponAnim( ACT_VM_IDLE );
  2292. m_bOpen = true;
  2293. DoEffect( EFFECT_READY );
  2294. #ifdef CLIENT
  2295. // Element prediction
  2296. m_ElementParameter.InitFromCurrent( 1.0f, 0.2f, INTERP_SPLINE );
  2297. m_bOldOpen = true;
  2298. #endif
  2299. }
  2300. //-----------------------------------------------------------------------------
  2301. // Purpose:
  2302. //-----------------------------------------------------------------------------
  2303. void CWeaponPhysCannon::CloseElements( void )
  2304. {
  2305. if ( m_bOpen == false )
  2306. return;
  2307. WeaponSound( MELEE_HIT );
  2308. CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
  2309. if ( pOwner == NULL )
  2310. return;
  2311. SendWeaponAnim( ACT_VM_IDLE );
  2312. m_bOpen = false;
  2313. if ( GetMotorSound() )
  2314. {
  2315. (CSoundEnvelopeController::GetController()).SoundChangeVolume( GetMotorSound(), 0.0f, 1.0f );
  2316. (CSoundEnvelopeController::GetController()).SoundChangePitch( GetMotorSound(), 50, 1.0f );
  2317. }
  2318. DoEffect( EFFECT_CLOSED );
  2319. #ifdef CLIENT
  2320. // Element prediction
  2321. m_ElementParameter.InitFromCurrent( 0.0f, 0.5f, INTERP_SPLINE );
  2322. m_bOldOpen = false;
  2323. #endif
  2324. }
  2325. #define PHYSCANNON_MAX_MASS 500
  2326. //-----------------------------------------------------------------------------
  2327. // Purpose:
  2328. // Output : float
  2329. //-----------------------------------------------------------------------------
  2330. float CWeaponPhysCannon::GetLoadPercentage( void )
  2331. {
  2332. float loadWeight = m_grabController.GetLoadWeight();
  2333. loadWeight /= physcannon_maxmass.GetFloat();
  2334. loadWeight = clamp( loadWeight, 0.0f, 1.0f );
  2335. return loadWeight;
  2336. }
  2337. //-----------------------------------------------------------------------------
  2338. // Purpose:
  2339. // Output : CSoundPatch
  2340. //-----------------------------------------------------------------------------
  2341. CSoundPatch *CWeaponPhysCannon::GetMotorSound( void )
  2342. {
  2343. if ( m_sndMotor == NULL )
  2344. {
  2345. CPASAttenuationFilter filter( this );
  2346. m_sndMotor = (CSoundEnvelopeController::GetController()).SoundCreate( filter, entindex(), CHAN_STATIC, "Weapon_PhysCannon.HoldSound", ATTN_NORM );
  2347. }
  2348. return m_sndMotor;
  2349. }
  2350. //-----------------------------------------------------------------------------
  2351. // Shuts down sounds
  2352. //-----------------------------------------------------------------------------
  2353. void CWeaponPhysCannon::StopLoopingSounds()
  2354. {
  2355. if ( m_sndMotor != NULL )
  2356. {
  2357. (CSoundEnvelopeController::GetController()).SoundDestroy( m_sndMotor );
  2358. m_sndMotor = NULL;
  2359. }
  2360. #ifndef CLIENT_DLL
  2361. BaseClass::StopLoopingSounds();
  2362. #endif
  2363. }
  2364. //-----------------------------------------------------------------------------
  2365. // Purpose:
  2366. //-----------------------------------------------------------------------------
  2367. void CWeaponPhysCannon::DestroyEffects( void )
  2368. {
  2369. #ifdef CLIENT_DLL
  2370. // Free our beams
  2371. m_Beams[0].Release();
  2372. m_Beams[1].Release();
  2373. m_Beams[2].Release();
  2374. #endif
  2375. // Stop everything
  2376. StopEffects();
  2377. }
  2378. //-----------------------------------------------------------------------------
  2379. // Purpose:
  2380. //-----------------------------------------------------------------------------
  2381. void CWeaponPhysCannon::StopEffects( bool stopSound )
  2382. {
  2383. // Turn off our effect state
  2384. DoEffect( EFFECT_NONE );
  2385. #ifndef CLIENT_DLL
  2386. //Shut off sounds
  2387. if ( stopSound && GetMotorSound() != NULL )
  2388. {
  2389. (CSoundEnvelopeController::GetController()).SoundFadeOut( GetMotorSound(), 0.1f );
  2390. }
  2391. #endif // !CLIENT_DLL
  2392. }
  2393. //-----------------------------------------------------------------------------
  2394. // Purpose:
  2395. //-----------------------------------------------------------------------------
  2396. void CWeaponPhysCannon::StartEffects( void )
  2397. {
  2398. #ifdef CLIENT_DLL
  2399. // ------------------------------------------
  2400. // Core
  2401. // ------------------------------------------
  2402. if ( m_Parameters[PHYSCANNON_CORE].GetMaterial() == NULL )
  2403. {
  2404. m_Parameters[PHYSCANNON_CORE].GetScale().Init( 0.0f, 1.0f, 0.1f );
  2405. m_Parameters[PHYSCANNON_CORE].GetAlpha().Init( 255.0f, 255.0f, 0.1f );
  2406. m_Parameters[PHYSCANNON_CORE].SetAttachment( 1 );
  2407. if ( m_Parameters[PHYSCANNON_CORE].SetMaterial( PHYSCANNON_CENTER_GLOW ) == false )
  2408. {
  2409. // This means the texture was not found
  2410. Assert( 0 );
  2411. }
  2412. }
  2413. // ------------------------------------------
  2414. // Blast
  2415. // ------------------------------------------
  2416. if ( m_Parameters[PHYSCANNON_BLAST].GetMaterial() == NULL )
  2417. {
  2418. m_Parameters[PHYSCANNON_BLAST].GetScale().Init( 0.0f, 1.0f, 0.1f );
  2419. m_Parameters[PHYSCANNON_BLAST].GetAlpha().Init( 255.0f, 255.0f, 0.1f );
  2420. m_Parameters[PHYSCANNON_BLAST].SetAttachment( 1 );
  2421. m_Parameters[PHYSCANNON_BLAST].SetVisible( false );
  2422. if ( m_Parameters[PHYSCANNON_BLAST].SetMaterial( PHYSCANNON_BLAST_SPRITE ) == false )
  2423. {
  2424. // This means the texture was not found
  2425. Assert( 0 );
  2426. }
  2427. }
  2428. // ------------------------------------------
  2429. // Glows
  2430. // ------------------------------------------
  2431. const char *attachNamesGlowThirdPerson[NUM_GLOW_SPRITES] =
  2432. {
  2433. "fork1m",
  2434. "fork1t",
  2435. "fork2m",
  2436. "fork2t",
  2437. "fork3m",
  2438. "fork3t",
  2439. };
  2440. const char *attachNamesGlow[NUM_GLOW_SPRITES] =
  2441. {
  2442. "fork1b",
  2443. "fork1m",
  2444. "fork1t",
  2445. "fork2b",
  2446. "fork2m",
  2447. "fork2t"
  2448. };
  2449. //Create the glow sprites
  2450. for ( int i = PHYSCANNON_GLOW1; i < (PHYSCANNON_GLOW1+NUM_GLOW_SPRITES); i++ )
  2451. {
  2452. if ( m_Parameters[i].GetMaterial() != NULL )
  2453. continue;
  2454. m_Parameters[i].GetScale().SetAbsolute( 0.05f * SPRITE_SCALE );
  2455. m_Parameters[i].GetAlpha().SetAbsolute( 64.0f );
  2456. // Different for different views
  2457. if ( ShouldDrawUsingViewModel() )
  2458. {
  2459. m_Parameters[i].SetAttachment( LookupAttachment( attachNamesGlow[i-PHYSCANNON_GLOW1] ) );
  2460. }
  2461. else
  2462. {
  2463. m_Parameters[i].SetAttachment( LookupAttachment( attachNamesGlowThirdPerson[i-PHYSCANNON_GLOW1] ) );
  2464. }
  2465. m_Parameters[i].SetColor( Vector( 255, 128, 0 ) );
  2466. if ( m_Parameters[i].SetMaterial( PHYSCANNON_GLOW_SPRITE ) == false )
  2467. {
  2468. // This means the texture was not found
  2469. Assert( 0 );
  2470. }
  2471. }
  2472. // ------------------------------------------
  2473. // End caps
  2474. // ------------------------------------------
  2475. const char *attachNamesEndCap[NUM_ENDCAP_SPRITES] =
  2476. {
  2477. "fork1t",
  2478. "fork2t",
  2479. "fork3t"
  2480. };
  2481. //Create the glow sprites
  2482. for ( int i = PHYSCANNON_ENDCAP1; i < (PHYSCANNON_ENDCAP1+NUM_ENDCAP_SPRITES); i++ )
  2483. {
  2484. if ( m_Parameters[i].GetMaterial() != NULL )
  2485. continue;
  2486. m_Parameters[i].GetScale().SetAbsolute( 0.05f * SPRITE_SCALE );
  2487. m_Parameters[i].GetAlpha().SetAbsolute( 255.0f );
  2488. m_Parameters[i].SetAttachment( LookupAttachment( attachNamesEndCap[i-PHYSCANNON_ENDCAP1] ) );
  2489. m_Parameters[i].SetVisible( false );
  2490. if ( m_Parameters[i].SetMaterial( PHYSCANNON_ENDCAP_SPRITE ) == false )
  2491. {
  2492. // This means the texture was not found
  2493. Assert( 0 );
  2494. }
  2495. }
  2496. #endif
  2497. }
  2498. //-----------------------------------------------------------------------------
  2499. // Purpose: Closing effects
  2500. //-----------------------------------------------------------------------------
  2501. void CWeaponPhysCannon::DoEffectClosed( void )
  2502. {
  2503. #ifdef CLIENT_DLL
  2504. // Turn off the end-caps
  2505. for ( int i = PHYSCANNON_ENDCAP1; i < (PHYSCANNON_ENDCAP1+NUM_ENDCAP_SPRITES); i++ )
  2506. {
  2507. m_Parameters[i].SetVisible( false );
  2508. }
  2509. #endif
  2510. }
  2511. //-----------------------------------------------------------------------------
  2512. // Purpose: Ready effects
  2513. //-----------------------------------------------------------------------------
  2514. void CWeaponPhysCannon::DoEffectReady( void )
  2515. {
  2516. #ifdef CLIENT_DLL
  2517. // Special POV case
  2518. if ( ShouldDrawUsingViewModel() )
  2519. {
  2520. //Turn on the center sprite
  2521. m_Parameters[PHYSCANNON_CORE].GetScale().InitFromCurrent( 14.0f, 0.2f );
  2522. m_Parameters[PHYSCANNON_CORE].GetAlpha().InitFromCurrent( 128.0f, 0.2f );
  2523. m_Parameters[PHYSCANNON_CORE].SetVisible();
  2524. }
  2525. else
  2526. {
  2527. //Turn off the center sprite
  2528. m_Parameters[PHYSCANNON_CORE].GetScale().InitFromCurrent( 8.0f, 0.2f );
  2529. m_Parameters[PHYSCANNON_CORE].GetAlpha().InitFromCurrent( 0.0f, 0.2f );
  2530. m_Parameters[PHYSCANNON_CORE].SetVisible();
  2531. }
  2532. // Turn on the glow sprites
  2533. for ( int i = PHYSCANNON_GLOW1; i < (PHYSCANNON_GLOW1+NUM_GLOW_SPRITES); i++ )
  2534. {
  2535. m_Parameters[i].GetScale().InitFromCurrent( 0.4f * SPRITE_SCALE, 0.2f );
  2536. m_Parameters[i].GetAlpha().InitFromCurrent( 64.0f, 0.2f );
  2537. m_Parameters[i].SetVisible();
  2538. }
  2539. // Turn on the glow sprites
  2540. for ( int i = PHYSCANNON_ENDCAP1; i < (PHYSCANNON_ENDCAP1+NUM_ENDCAP_SPRITES); i++ )
  2541. {
  2542. m_Parameters[i].SetVisible( false );
  2543. }
  2544. #endif
  2545. }
  2546. //-----------------------------------------------------------------------------
  2547. // Holding effects
  2548. //-----------------------------------------------------------------------------
  2549. void CWeaponPhysCannon::DoEffectHolding( void )
  2550. {
  2551. #ifdef CLIENT_DLL
  2552. if ( ShouldDrawUsingViewModel() )
  2553. {
  2554. // Scale up the center sprite
  2555. m_Parameters[PHYSCANNON_CORE].GetScale().InitFromCurrent( 16.0f, 0.2f );
  2556. m_Parameters[PHYSCANNON_CORE].GetAlpha().InitFromCurrent( 255.0f, 0.1f );
  2557. m_Parameters[PHYSCANNON_CORE].SetVisible();
  2558. // Prepare for scale up
  2559. m_Parameters[PHYSCANNON_BLAST].SetVisible( false );
  2560. // Turn on the glow sprites
  2561. for ( int i = PHYSCANNON_GLOW1; i < (PHYSCANNON_GLOW1+NUM_GLOW_SPRITES); i++ )
  2562. {
  2563. m_Parameters[i].GetScale().InitFromCurrent( 0.5f * SPRITE_SCALE, 0.2f );
  2564. m_Parameters[i].GetAlpha().InitFromCurrent( 64.0f, 0.2f );
  2565. m_Parameters[i].SetVisible();
  2566. }
  2567. // Turn on the glow sprites
  2568. // NOTE: The last glow is left off for first-person
  2569. for ( int i = PHYSCANNON_ENDCAP1; i < (PHYSCANNON_ENDCAP1+NUM_ENDCAP_SPRITES-1); i++ )
  2570. {
  2571. m_Parameters[i].SetVisible();
  2572. }
  2573. // Create our beams
  2574. CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
  2575. CBaseEntity *pBeamEnt = pOwner->GetViewModel();
  2576. // Setup the beams
  2577. m_Beams[0].Init( LookupAttachment( "fork1t" ), 1, pBeamEnt, true );
  2578. m_Beams[1].Init( LookupAttachment( "fork2t" ), 1, pBeamEnt, true );
  2579. // Set them visible
  2580. m_Beams[0].SetVisible();
  2581. m_Beams[1].SetVisible();
  2582. }
  2583. else
  2584. {
  2585. // Scale up the center sprite
  2586. m_Parameters[PHYSCANNON_CORE].GetScale().InitFromCurrent( 14.0f, 0.2f );
  2587. m_Parameters[PHYSCANNON_CORE].GetAlpha().InitFromCurrent( 255.0f, 0.1f );
  2588. m_Parameters[PHYSCANNON_CORE].SetVisible();
  2589. // Prepare for scale up
  2590. m_Parameters[PHYSCANNON_BLAST].SetVisible( false );
  2591. // Turn on the glow sprites
  2592. for ( int i = PHYSCANNON_GLOW1; i < (PHYSCANNON_GLOW1+NUM_GLOW_SPRITES); i++ )
  2593. {
  2594. m_Parameters[i].GetScale().InitFromCurrent( 0.5f * SPRITE_SCALE, 0.2f );
  2595. m_Parameters[i].GetAlpha().InitFromCurrent( 64.0f, 0.2f );
  2596. m_Parameters[i].SetVisible();
  2597. }
  2598. // Turn on the glow sprites
  2599. for ( int i = PHYSCANNON_ENDCAP1; i < (PHYSCANNON_ENDCAP1+NUM_ENDCAP_SPRITES); i++ )
  2600. {
  2601. m_Parameters[i].SetVisible();
  2602. }
  2603. // Setup the beams
  2604. m_Beams[0].Init( LookupAttachment( "fork1t" ), 1, this, false );
  2605. m_Beams[1].Init( LookupAttachment( "fork2t" ), 1, this, false );
  2606. m_Beams[2].Init( LookupAttachment( "fork3t" ), 1, this, false );
  2607. // Set them visible
  2608. m_Beams[0].SetVisible();
  2609. m_Beams[1].SetVisible();
  2610. m_Beams[2].SetVisible();
  2611. }
  2612. #endif
  2613. }
  2614. //-----------------------------------------------------------------------------
  2615. // Launch effects
  2616. //-----------------------------------------------------------------------------
  2617. void CWeaponPhysCannon::DoEffectLaunch( Vector *pos )
  2618. {
  2619. CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
  2620. if ( pOwner == NULL )
  2621. return;
  2622. Vector endPos;
  2623. Vector shotDir;
  2624. // See if we need to predict this position
  2625. if ( pos == NULL )
  2626. {
  2627. // Hit an entity if we're holding one
  2628. if ( m_hAttachedObject )
  2629. {
  2630. endPos = m_hAttachedObject->WorldSpaceCenter();
  2631. shotDir = endPos - pOwner->Weapon_ShootPosition();
  2632. VectorNormalize( shotDir );
  2633. }
  2634. else
  2635. {
  2636. // Otherwise try and find the right spot
  2637. endPos = pOwner->Weapon_ShootPosition();
  2638. pOwner->EyeVectors( &shotDir );
  2639. trace_t tr;
  2640. UTIL_TraceLine( endPos, endPos + ( shotDir * MAX_TRACE_LENGTH ), MASK_SHOT, pOwner, COLLISION_GROUP_NONE, &tr );
  2641. endPos = tr.endpos;
  2642. shotDir = endPos - pOwner->Weapon_ShootPosition();
  2643. VectorNormalize( shotDir );
  2644. }
  2645. }
  2646. else
  2647. {
  2648. // Use what is supplied
  2649. endPos = *pos;
  2650. shotDir = ( endPos - pOwner->Weapon_ShootPosition() );
  2651. VectorNormalize( shotDir );
  2652. }
  2653. // End hit
  2654. CPVSFilter filter( endPos );
  2655. // Don't send this to the owning player, they already had it predicted
  2656. if ( IsPredicted() )
  2657. {
  2658. filter.UsePredictionRules();
  2659. }
  2660. // Do an impact hit
  2661. CEffectData data;
  2662. data.m_vOrigin = endPos;
  2663. #ifdef CLIENT_DLL
  2664. data.m_hEntity = GetRefEHandle();
  2665. #else
  2666. data.m_nEntIndex = entindex();
  2667. #endif
  2668. te->DispatchEffect( filter, 0.0, data.m_vOrigin, "PhyscannonImpact", data );
  2669. #ifdef CLIENT_DLL
  2670. //Turn on the blast sprite and scale
  2671. m_Parameters[PHYSCANNON_BLAST].GetScale().Init( 8.0f, 64.0f, 0.1f );
  2672. m_Parameters[PHYSCANNON_BLAST].GetAlpha().Init( 255.0f, 0.0f, 0.2f );
  2673. m_Parameters[PHYSCANNON_BLAST].SetVisible();
  2674. #endif
  2675. }
  2676. //-----------------------------------------------------------------------------
  2677. // Purpose: Shutdown for the weapon when it's holstered
  2678. //-----------------------------------------------------------------------------
  2679. void CWeaponPhysCannon::DoEffectNone( void )
  2680. {
  2681. #ifdef CLIENT_DLL
  2682. //Turn off main glows
  2683. m_Parameters[PHYSCANNON_CORE].SetVisible( false );
  2684. m_Parameters[PHYSCANNON_BLAST].SetVisible( false );
  2685. for ( int i = PHYSCANNON_GLOW1; i < (PHYSCANNON_GLOW1+NUM_GLOW_SPRITES); i++ )
  2686. {
  2687. m_Parameters[i].SetVisible( false );
  2688. }
  2689. // Turn on the glow sprites
  2690. for ( int i = PHYSCANNON_ENDCAP1; i < (PHYSCANNON_ENDCAP1+NUM_ENDCAP_SPRITES); i++ )
  2691. {
  2692. m_Parameters[i].SetVisible( false );
  2693. }
  2694. m_Beams[0].SetVisible( false );
  2695. m_Beams[1].SetVisible( false );
  2696. m_Beams[2].SetVisible( false );
  2697. #endif
  2698. }
  2699. //-----------------------------------------------------------------------------
  2700. // Purpose:
  2701. // Input : effectType -
  2702. //-----------------------------------------------------------------------------
  2703. void CWeaponPhysCannon::DoEffect( int effectType, Vector *pos )
  2704. {
  2705. m_EffectState = effectType;
  2706. #ifdef CLIENT_DLL
  2707. // Save predicted state
  2708. m_nOldEffectState = m_EffectState;
  2709. #endif
  2710. switch( effectType )
  2711. {
  2712. case EFFECT_CLOSED:
  2713. DoEffectClosed( );
  2714. break;
  2715. case EFFECT_READY:
  2716. DoEffectReady( );
  2717. break;
  2718. case EFFECT_HOLDING:
  2719. DoEffectHolding();
  2720. break;
  2721. case EFFECT_LAUNCH:
  2722. DoEffectLaunch( pos );
  2723. break;
  2724. default:
  2725. case EFFECT_NONE:
  2726. DoEffectNone();
  2727. break;
  2728. }
  2729. }
  2730. //-----------------------------------------------------------------------------
  2731. // Purpose:
  2732. // Input : iIndex -
  2733. // Output : const char
  2734. //-----------------------------------------------------------------------------
  2735. const char *CWeaponPhysCannon::GetShootSound( int iIndex ) const
  2736. {
  2737. return BaseClass::GetShootSound( iIndex );
  2738. }
  2739. #ifdef CLIENT_DLL
  2740. extern void FormatViewModelAttachment( Vector &vOrigin, bool bInverse );
  2741. //-----------------------------------------------------------------------------
  2742. // Purpose: Gets the complete list of values needed to render an effect from an
  2743. // effect parameter
  2744. //-----------------------------------------------------------------------------
  2745. void CWeaponPhysCannon::GetEffectParameters( EffectType_t effectID, color32 &color, float &scale, IMaterial **pMaterial, Vector &vecAttachment )
  2746. {
  2747. const float dt = gpGlobals->curtime;
  2748. // Get alpha
  2749. float alpha = m_Parameters[effectID].GetAlpha().Interp( dt );
  2750. // Get scale
  2751. scale = m_Parameters[effectID].GetScale().Interp( dt );
  2752. // Get material
  2753. *pMaterial = (IMaterial *) m_Parameters[effectID].GetMaterial();
  2754. // Setup the color
  2755. color.r = (int) m_Parameters[effectID].GetColor().x;
  2756. color.g = (int) m_Parameters[effectID].GetColor().y;
  2757. color.b = (int) m_Parameters[effectID].GetColor().z;
  2758. color.a = (int) alpha;
  2759. // Setup the attachment
  2760. int attachment = m_Parameters[effectID].GetAttachment();
  2761. QAngle angles;
  2762. // Format for first-person
  2763. if ( ShouldDrawUsingViewModel() )
  2764. {
  2765. CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
  2766. if ( pOwner != NULL )
  2767. {
  2768. pOwner->GetViewModel()->GetAttachment( attachment, vecAttachment, angles );
  2769. ::FormatViewModelAttachment( vecAttachment, true );
  2770. }
  2771. }
  2772. else
  2773. {
  2774. GetAttachment( attachment, vecAttachment, angles );
  2775. }
  2776. }
  2777. //-----------------------------------------------------------------------------
  2778. // Purpose: Whether or not an effect is set to display
  2779. //-----------------------------------------------------------------------------
  2780. bool CWeaponPhysCannon::IsEffectVisible( EffectType_t effectID )
  2781. {
  2782. return m_Parameters[effectID].IsVisible();
  2783. }
  2784. //-----------------------------------------------------------------------------
  2785. // Purpose: Draws the effect sprite, given an effect parameter ID
  2786. //-----------------------------------------------------------------------------
  2787. void CWeaponPhysCannon::DrawEffectSprite( EffectType_t effectID )
  2788. {
  2789. color32 color;
  2790. float scale;
  2791. IMaterial *pMaterial;
  2792. Vector vecAttachment;
  2793. // Don't draw invisible effects
  2794. if ( IsEffectVisible( effectID ) == false )
  2795. return;
  2796. // Get all of our parameters
  2797. GetEffectParameters( effectID, color, scale, &pMaterial, vecAttachment );
  2798. // Msg( "Scale: %.2f\tAlpha: %.2f\n", scale, alpha );
  2799. // Don't render fully translucent objects
  2800. if ( color.a <= 0.0f )
  2801. return;
  2802. // Draw the sprite
  2803. CMatRenderContextPtr pRenderContext( materials );
  2804. pRenderContext->Bind( pMaterial, this );
  2805. DrawSprite( vecAttachment, scale, scale, color );
  2806. }
  2807. //-----------------------------------------------------------------------------
  2808. // Purpose: Render our third-person effects
  2809. //-----------------------------------------------------------------------------
  2810. void CWeaponPhysCannon::DrawEffects( void )
  2811. {
  2812. // Draw the core effects
  2813. DrawEffectSprite( PHYSCANNON_CORE );
  2814. DrawEffectSprite( PHYSCANNON_BLAST );
  2815. // Draw the glows
  2816. for ( int i = PHYSCANNON_GLOW1; i < (PHYSCANNON_GLOW1+NUM_GLOW_SPRITES); i++ )
  2817. {
  2818. DrawEffectSprite( (EffectType_t) i );
  2819. }
  2820. // Draw the endcaps
  2821. for ( int i = PHYSCANNON_ENDCAP1; i < (PHYSCANNON_ENDCAP1+NUM_ENDCAP_SPRITES); i++ )
  2822. {
  2823. DrawEffectSprite( (EffectType_t) i );
  2824. }
  2825. }
  2826. //-----------------------------------------------------------------------------
  2827. // Purpose: Third-person function call to render world model
  2828. //-----------------------------------------------------------------------------
  2829. int CWeaponPhysCannon::DrawModel( int flags )
  2830. {
  2831. // Only render these on the transparent pass
  2832. if ( flags & STUDIO_TRANSPARENCY )
  2833. {
  2834. DrawEffects();
  2835. return 1;
  2836. }
  2837. // Only do this on the opaque pass
  2838. return BaseClass::DrawModel( flags );
  2839. }
  2840. //-----------------------------------------------------------------------------
  2841. // Purpose: First-person function call after viewmodel has been drawn
  2842. //-----------------------------------------------------------------------------
  2843. void CWeaponPhysCannon::ViewModelDrawn( C_BaseViewModel *pBaseViewModel )
  2844. {
  2845. // Render our effects
  2846. DrawEffects();
  2847. // Pass this back up
  2848. BaseClass::ViewModelDrawn( pBaseViewModel );
  2849. }
  2850. //-----------------------------------------------------------------------------
  2851. // Purpose: We are always considered transparent
  2852. //-----------------------------------------------------------------------------
  2853. bool CWeaponPhysCannon::IsTransparent( void )
  2854. {
  2855. return true;
  2856. }
  2857. //-----------------------------------------------------------------------------
  2858. // Purpose:
  2859. //-----------------------------------------------------------------------------
  2860. void CWeaponPhysCannon::NotifyShouldTransmit( ShouldTransmitState_t state )
  2861. {
  2862. BaseClass::NotifyShouldTransmit(state);
  2863. if ( state == SHOULDTRANSMIT_END )
  2864. {
  2865. DoEffect( EFFECT_NONE );
  2866. }
  2867. }
  2868. #endif
  2869. //-----------------------------------------------------------------------------
  2870. // EXTERNAL API
  2871. //-----------------------------------------------------------------------------
  2872. void PhysCannonForceDrop( CBaseCombatWeapon *pActiveWeapon, CBaseEntity *pOnlyIfHoldingThis )
  2873. {
  2874. CWeaponPhysCannon *pCannon = dynamic_cast<CWeaponPhysCannon *>(pActiveWeapon);
  2875. if ( pCannon )
  2876. {
  2877. if ( pOnlyIfHoldingThis )
  2878. {
  2879. pCannon->DropIfEntityHeld( pOnlyIfHoldingThis );
  2880. }
  2881. else
  2882. {
  2883. pCannon->ForceDrop();
  2884. }
  2885. }
  2886. }
  2887. bool PlayerPickupControllerIsHoldingEntity( CBaseEntity *pPickupControllerEntity, CBaseEntity *pHeldEntity )
  2888. {
  2889. CPlayerPickupController *pController = dynamic_cast<CPlayerPickupController *>(pPickupControllerEntity);
  2890. return pController ? pController->IsHoldingEntity( pHeldEntity ) : false;
  2891. }
  2892. float PhysCannonGetHeldObjectMass( CBaseCombatWeapon *pActiveWeapon, IPhysicsObject *pHeldObject )
  2893. {
  2894. float mass = 0.0f;
  2895. CWeaponPhysCannon *pCannon = dynamic_cast<CWeaponPhysCannon *>(pActiveWeapon);
  2896. if ( pCannon )
  2897. {
  2898. CGrabController &grab = pCannon->GetGrabController();
  2899. mass = grab.GetSavedMass( pHeldObject );
  2900. }
  2901. return mass;
  2902. }
  2903. CBaseEntity *PhysCannonGetHeldEntity( CBaseCombatWeapon *pActiveWeapon )
  2904. {
  2905. CWeaponPhysCannon *pCannon = dynamic_cast<CWeaponPhysCannon *>(pActiveWeapon);
  2906. if ( pCannon )
  2907. {
  2908. CGrabController &grab = pCannon->GetGrabController();
  2909. return grab.GetAttached();
  2910. }
  2911. return NULL;
  2912. }
  2913. float PlayerPickupGetHeldObjectMass( CBaseEntity *pPickupControllerEntity, IPhysicsObject *pHeldObject )
  2914. {
  2915. float mass = 0.0f;
  2916. CPlayerPickupController *pController = dynamic_cast<CPlayerPickupController *>(pPickupControllerEntity);
  2917. if ( pController )
  2918. {
  2919. CGrabController &grab = pController->GetGrabController();
  2920. mass = grab.GetSavedMass( pHeldObject );
  2921. }
  2922. return mass;
  2923. }
  2924. #ifdef CLIENT_DLL
  2925. extern void FX_GaussExplosion( const Vector &pos, const Vector &dir, int type );
  2926. void CallbackPhyscannonImpact( const CEffectData &data )
  2927. {
  2928. C_BaseEntity *pEnt = data.GetEntity();
  2929. if ( pEnt == NULL )
  2930. return;
  2931. Vector vecAttachment;
  2932. QAngle vecAngles;
  2933. C_BaseCombatWeapon *pWeapon = dynamic_cast<C_BaseCombatWeapon *>(pEnt);
  2934. if ( pWeapon == NULL )
  2935. return;
  2936. pWeapon->GetAttachment( 1, vecAttachment, vecAngles );
  2937. Vector dir = ( data.m_vOrigin - vecAttachment );
  2938. VectorNormalize( dir );
  2939. // Do special first-person fix-up
  2940. if ( pWeapon->GetOwner() == CBasePlayer::GetLocalPlayer() )
  2941. {
  2942. // Translate the attachment entity to the viewmodel
  2943. C_BasePlayer *pPlayer = dynamic_cast<C_BasePlayer *>(pWeapon->GetOwner());
  2944. if ( pPlayer )
  2945. {
  2946. pEnt = pPlayer->GetViewModel();
  2947. }
  2948. // Format attachment for first-person view!
  2949. ::FormatViewModelAttachment( vecAttachment, true );
  2950. // Explosions at the impact point
  2951. FX_GaussExplosion( data.m_vOrigin, -dir, 0 );
  2952. // Draw a beam
  2953. BeamInfo_t beamInfo;
  2954. beamInfo.m_pStartEnt = pEnt;
  2955. beamInfo.m_nStartAttachment = 1;
  2956. beamInfo.m_pEndEnt = NULL;
  2957. beamInfo.m_nEndAttachment = -1;
  2958. beamInfo.m_vecStart = vec3_origin;
  2959. beamInfo.m_vecEnd = data.m_vOrigin;
  2960. beamInfo.m_pszModelName = PHYSCANNON_BEAM_SPRITE;
  2961. beamInfo.m_flHaloScale = 0.0f;
  2962. beamInfo.m_flLife = 0.1f;
  2963. beamInfo.m_flWidth = 12.0f;
  2964. beamInfo.m_flEndWidth = 4.0f;
  2965. beamInfo.m_flFadeLength = 0.0f;
  2966. beamInfo.m_flAmplitude = 0;
  2967. beamInfo.m_flBrightness = 255.0;
  2968. beamInfo.m_flSpeed = 0.0f;
  2969. beamInfo.m_nStartFrame = 0.0;
  2970. beamInfo.m_flFrameRate = 30.0;
  2971. beamInfo.m_flRed = 255.0;
  2972. beamInfo.m_flGreen = 255.0;
  2973. beamInfo.m_flBlue = 255.0;
  2974. beamInfo.m_nSegments = 16;
  2975. beamInfo.m_bRenderable = true;
  2976. beamInfo.m_nFlags = FBEAM_ONLYNOISEONCE;
  2977. beams->CreateBeamEntPoint( beamInfo );
  2978. }
  2979. else
  2980. {
  2981. // Explosion at the starting point
  2982. FX_GaussExplosion( vecAttachment, dir, 0 );
  2983. }
  2984. }
  2985. DECLARE_CLIENT_EFFECT( "PhyscannonImpact", CallbackPhyscannonImpact );
  2986. #endif