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.

2110 lines
57 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: Player for HL1.
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================//
  7. #include "cbase.h"
  8. #include "hl1_player.h"
  9. #include "gamerules.h"
  10. #include "trains.h"
  11. #include "hl1_basecombatweapon_shared.h"
  12. #include "vcollide_parse.h"
  13. #include "in_buttons.h"
  14. #include "igamemovement.h"
  15. #include "ai_hull.h"
  16. #include "hl2_shareddefs.h"
  17. #include "info_camera_link.h"
  18. #include "point_camera.h"
  19. #include "ndebugoverlay.h"
  20. #include "globals.h"
  21. #include "ai_interactions.h"
  22. #include "engine/IEngineSound.h"
  23. #include "vphysics/player_controller.h"
  24. #include "vphysics/constraints.h"
  25. #include "predicted_viewmodel.h"
  26. #include "physics_saverestore.h"
  27. #include "gamestats.h"
  28. #define DMG_FREEZE DMG_VEHICLE
  29. #define DMG_SLOWFREEZE DMG_DISSOLVE
  30. // HL1_DMG_SHOWNHUD: Add DMG_VEHICLE because HL2 hijacked those bits from DMG_FREEZE, which is what they are in HL1
  31. // HL1_DMG_SHOWNHUD: Add DMG_DISSOLVE because HL2 hijacked those bits from DMG_SLOWFREEZE, which is what they are in HL1
  32. // See Halflife1 GameRules - Damage_GetShowOnHud()
  33. //#define HL1_DMG_SHOWNHUD (DMG_POISON | DMG_ACID | DMG_FREEZE | DMG_SLOWFREEZE | DMG_DROWN | DMG_BURN | DMG_SLOWBURN | DMG_NERVEGAS | DMG_RADIATION | DMG_SHOCK)
  34. // TIME BASED DAMAGE AMOUNT
  35. // tweak these values based on gameplay feedback:
  36. #define PARALYZE_DURATION 2 // number of 2 second intervals to take damage
  37. #define PARALYZE_DAMAGE 1.0 // damage to take each 2 second interval
  38. #define NERVEGAS_DURATION 2
  39. #define NERVEGAS_DAMAGE 5.0
  40. #define POISON_DURATION 5
  41. #define POISON_DAMAGE 2.0
  42. #define RADIATION_DURATION 2
  43. #define RADIATION_DAMAGE 1.0
  44. #define ACID_DURATION 2
  45. #define ACID_DAMAGE 5.0
  46. #define SLOWBURN_DURATION 2
  47. #define SLOWBURN_DAMAGE 1.0
  48. #define SLOWFREEZE_DURATION 2
  49. #define SLOWFREEZE_DAMAGE 1.0
  50. #define FLASH_DRAIN_TIME 1.2 //100 units/3 minutes
  51. #define FLASH_CHARGE_TIME 0.2 // 100 units/20 seconds (seconds per unit)
  52. ConVar player_showpredictedposition( "player_showpredictedposition", "0" );
  53. ConVar player_showpredictedposition_timestep( "player_showpredictedposition_timestep", "1.0" );
  54. ConVar sv_hl1_allowpickup( "sv_hl1_allowpickup", "0" );
  55. LINK_ENTITY_TO_CLASS( player, CHL1_Player );
  56. PRECACHE_REGISTER(player);
  57. BEGIN_DATADESC( CHL1_Player )
  58. DEFINE_FIELD( m_nControlClass, FIELD_INTEGER ),
  59. DEFINE_AUTO_ARRAY( m_vecMissPositions, FIELD_POSITION_VECTOR ),
  60. DEFINE_FIELD( m_nNumMissPositions, FIELD_INTEGER ),
  61. DEFINE_FIELD( m_flIdleTime, FIELD_TIME ),
  62. DEFINE_FIELD( m_flMoveTime, FIELD_TIME ),
  63. DEFINE_FIELD( m_flLastDamageTime, FIELD_TIME ),
  64. DEFINE_FIELD( m_flTargetFindTime, FIELD_TIME ),
  65. DEFINE_FIELD( m_bHasLongJump, FIELD_BOOLEAN ),
  66. DEFINE_FIELD( m_nFlashBattery, FIELD_INTEGER ),
  67. DEFINE_FIELD( m_flFlashLightTime, FIELD_TIME ),
  68. DEFINE_FIELD( m_flStartCharge, FIELD_FLOAT ),
  69. DEFINE_FIELD( m_flAmmoStartCharge, FIELD_FLOAT ),
  70. DEFINE_FIELD( m_flPlayAftershock, FIELD_FLOAT ),
  71. DEFINE_FIELD( m_flNextAmmoBurn, FIELD_FLOAT ),
  72. DEFINE_PHYSPTR( m_pPullConstraint ),
  73. DEFINE_FIELD( m_hPullObject, FIELD_EHANDLE ),
  74. DEFINE_FIELD( m_bIsPullingObject, FIELD_BOOLEAN ),
  75. END_DATADESC()
  76. IMPLEMENT_SERVERCLASS_ST( CHL1_Player, DT_HL1Player )
  77. SendPropInt( SENDINFO( m_bHasLongJump ), 1, SPROP_UNSIGNED ),
  78. SendPropInt( SENDINFO( m_nFlashBattery ), 8, SPROP_UNSIGNED ),
  79. SendPropBool( SENDINFO( m_bIsPullingObject ) ),
  80. SendPropFloat( SENDINFO( m_flStartCharge ) ),
  81. SendPropFloat( SENDINFO( m_flAmmoStartCharge ) ),
  82. SendPropFloat( SENDINFO( m_flPlayAftershock ) ),
  83. SendPropFloat( SENDINFO( m_flNextAmmoBurn ) )
  84. END_SEND_TABLE()
  85. CHL1_Player::CHL1_Player()
  86. {
  87. m_nNumMissPositions = 0;
  88. }
  89. void CHL1_Player::Precache( void )
  90. {
  91. BaseClass::Precache();
  92. PrecacheScriptSound( "Player.FlashlightOn" );
  93. PrecacheScriptSound( "Player.FlashlightOff" );
  94. PrecacheScriptSound( "Player.UseTrain" );
  95. }
  96. //-----------------------------------------------------------------------------
  97. // Purpose: Allow pre-frame adjustments on the player
  98. //-----------------------------------------------------------------------------
  99. void CHL1_Player::PreThink(void)
  100. {
  101. if ( player_showpredictedposition.GetBool() )
  102. {
  103. Vector predPos;
  104. UTIL_PredictedPosition( this, player_showpredictedposition_timestep.GetFloat(), &predPos );
  105. NDebugOverlay::Box( predPos, NAI_Hull::Mins( GetHullType() ), NAI_Hull::Maxs( GetHullType() ), 0, 255, 0, 0, 0.01f );
  106. NDebugOverlay::Line( GetAbsOrigin(), predPos, 0, 255, 0, 0, 0.01f );
  107. }
  108. int buttonsChanged;
  109. buttonsChanged = m_afButtonPressed | m_afButtonReleased;
  110. UpdatePullingObject();
  111. g_pGameRules->PlayerThink( this );
  112. if ( g_fGameOver || IsPlayerLockedInPlace() )
  113. return; // intermission or finale
  114. ItemPreFrame( );
  115. WaterMove();
  116. if ( g_pGameRules && g_pGameRules->FAllowFlashlight() )
  117. m_Local.m_iHideHUD &= ~HIDEHUD_FLASHLIGHT;
  118. else
  119. m_Local.m_iHideHUD |= HIDEHUD_FLASHLIGHT;
  120. // checks if new client data (for HUD and view control) needs to be sent to the client
  121. UpdateClientData();
  122. CheckTimeBasedDamage();
  123. CheckSuitUpdate();
  124. if (m_lifeState >= LIFE_DYING)
  125. {
  126. PlayerDeathThink();
  127. return;
  128. }
  129. // So the correct flags get sent to client asap.
  130. //
  131. if ( m_afPhysicsFlags & PFLAG_DIROVERRIDE )
  132. AddFlag( FL_ONTRAIN );
  133. else
  134. RemoveFlag( FL_ONTRAIN );
  135. // Train speed control
  136. if ( m_afPhysicsFlags & PFLAG_DIROVERRIDE )
  137. {
  138. CBaseEntity *pTrain = GetGroundEntity();
  139. float vel;
  140. if ( pTrain )
  141. {
  142. if ( !(pTrain->ObjectCaps() & FCAP_DIRECTIONAL_USE) )
  143. pTrain = NULL;
  144. }
  145. if ( !pTrain )
  146. {
  147. if ( GetActiveWeapon() && (GetActiveWeapon()->ObjectCaps() & FCAP_DIRECTIONAL_USE) )
  148. {
  149. m_iTrain = TRAIN_ACTIVE | TRAIN_NEW;
  150. if ( m_nButtons & IN_FORWARD )
  151. {
  152. m_iTrain |= TRAIN_FAST;
  153. }
  154. else if ( m_nButtons & IN_BACK )
  155. {
  156. m_iTrain |= TRAIN_BACK;
  157. }
  158. else
  159. {
  160. m_iTrain |= TRAIN_NEUTRAL;
  161. }
  162. return;
  163. }
  164. else
  165. {
  166. trace_t trainTrace;
  167. // Maybe this is on the other side of a level transition
  168. UTIL_TraceLine( GetAbsOrigin(), GetAbsOrigin() + Vector(0,0,-38),
  169. MASK_PLAYERSOLID_BRUSHONLY, this, COLLISION_GROUP_NONE, &trainTrace );
  170. if ( trainTrace.fraction != 1.0 && trainTrace.m_pEnt )
  171. pTrain = trainTrace.m_pEnt;
  172. if ( !pTrain || !(pTrain->ObjectCaps() & FCAP_DIRECTIONAL_USE) || !pTrain->OnControls(this) )
  173. {
  174. // Warning( "In train mode with no train!\n" );
  175. m_afPhysicsFlags &= ~PFLAG_DIROVERRIDE;
  176. m_iTrain = TRAIN_NEW|TRAIN_OFF;
  177. return;
  178. }
  179. }
  180. }
  181. else if ( !( GetFlags() & FL_ONGROUND ) || pTrain->HasSpawnFlags( SF_TRACKTRAIN_NOCONTROL ) || (m_nButtons & (IN_MOVELEFT|IN_MOVERIGHT) ) )
  182. {
  183. // Turn off the train if you jump, strafe, or the train controls go dead
  184. m_afPhysicsFlags &= ~PFLAG_DIROVERRIDE;
  185. m_iTrain = TRAIN_NEW|TRAIN_OFF;
  186. return;
  187. }
  188. SetAbsVelocity( vec3_origin );
  189. vel = 0;
  190. if ( m_afButtonPressed & IN_FORWARD )
  191. {
  192. vel = 1;
  193. pTrain->Use( this, this, USE_SET, (float)vel );
  194. }
  195. else if ( m_afButtonPressed & IN_BACK )
  196. {
  197. vel = -1;
  198. pTrain->Use( this, this, USE_SET, (float)vel );
  199. }
  200. else if ( m_afButtonPressed & IN_JUMP )
  201. {
  202. m_afPhysicsFlags &= ~PFLAG_DIROVERRIDE;
  203. m_iTrain = TRAIN_NEW|TRAIN_OFF;
  204. }
  205. if (vel)
  206. {
  207. m_iTrain = TrainSpeed(pTrain->m_flSpeed, ((CFuncTrackTrain*)pTrain)->GetMaxSpeed());
  208. m_iTrain |= TRAIN_ACTIVE|TRAIN_NEW;
  209. }
  210. }
  211. else if (m_iTrain & TRAIN_ACTIVE)
  212. {
  213. m_iTrain = TRAIN_NEW; // turn off train
  214. }
  215. // THIS CODE DOESN'T SEEM TO DO ANYTHING!!!
  216. // WHY IS IT STILL HERE? (sjb)
  217. if (m_nButtons & IN_JUMP)
  218. {
  219. // If on a ladder, jump off the ladder
  220. // else Jump
  221. if( IsPullingObject() )
  222. {
  223. StopPullingObject();
  224. }
  225. Jump();
  226. }
  227. // If trying to duck, already ducked, or in the process of ducking
  228. if ((m_nButtons & IN_DUCK) || (GetFlags() & FL_DUCKING) || (m_afPhysicsFlags & PFLAG_DUCKING) )
  229. Duck();
  230. //
  231. // If we're not on the ground, we're falling. Update our falling velocity.
  232. //
  233. if ( !( GetFlags() & FL_ONGROUND ) )
  234. {
  235. m_Local.m_flFallVelocity = -GetAbsVelocity().z;
  236. }
  237. if ( m_afPhysicsFlags & PFLAG_ONBARNACLE )
  238. {
  239. SetAbsVelocity( vec3_origin );
  240. }
  241. // StudioFrameAdvance( );//!!!HACKHACK!!! Can't be hit by traceline when not animating?
  242. //Find targets for NPC to shoot if they decide to miss us
  243. FindMissTargets();
  244. }
  245. //------------------------------------------------------------------------------
  246. // Purpose :
  247. // Input :
  248. // Output :
  249. //------------------------------------------------------------------------------
  250. Class_T CHL1_Player::Classify ( void )
  251. {
  252. // If player controlling another entity? If so, return this class
  253. if (m_nControlClass != CLASS_NONE)
  254. {
  255. return m_nControlClass;
  256. }
  257. else
  258. {
  259. return CLASS_PLAYER;
  260. }
  261. }
  262. //-----------------------------------------------------------------------------
  263. // Purpose: This is a generic function (to be implemented by sub-classes) to
  264. // handle specific interactions between different types of characters
  265. // (For example the barnacle grabbing an NPC)
  266. // Input : Constant for the type of interaction
  267. // Output : true - if sub-class has a response for the interaction
  268. // false - if sub-class has no response
  269. //-----------------------------------------------------------------------------
  270. bool CHL1_Player::HandleInteraction(int interactionType, void *data, CBaseCombatCharacter* sourceEnt)
  271. {
  272. if ( interactionType == g_interactionBarnacleVictimDangle )
  273. {
  274. TakeDamage ( CTakeDamageInfo( sourceEnt, sourceEnt, m_iHealth + ArmorValue(), DMG_SLASH | DMG_ALWAYSGIB ) );
  275. return true;
  276. }
  277. if (interactionType == g_interactionBarnacleVictimReleased)
  278. {
  279. m_afPhysicsFlags &= ~PFLAG_ONBARNACLE;
  280. SetMoveType( MOVETYPE_WALK );
  281. return true;
  282. }
  283. else if (interactionType == g_interactionBarnacleVictimGrab)
  284. {
  285. m_afPhysicsFlags |= PFLAG_ONBARNACLE;
  286. ClearUseEntity();
  287. return true;
  288. }
  289. return false;
  290. }
  291. void CHL1_Player::PlayerRunCommand(CUserCmd *ucmd, IMoveHelper *moveHelper)
  292. {
  293. // Handle FL_FROZEN.
  294. if ( m_afPhysicsFlags & PFLAG_ONBARNACLE )
  295. {
  296. ucmd->forwardmove = 0;
  297. ucmd->sidemove = 0;
  298. ucmd->upmove = 0;
  299. }
  300. //Update our movement information
  301. if ( ( ucmd->forwardmove != 0 ) || ( ucmd->sidemove != 0 ) || ( ucmd->upmove != 0 ) )
  302. {
  303. m_flIdleTime -= TICK_INTERVAL * 2.0f;
  304. if ( m_flIdleTime < 0.0f )
  305. {
  306. m_flIdleTime = 0.0f;
  307. }
  308. m_flMoveTime += TICK_INTERVAL;
  309. if ( m_flMoveTime > 4.0f )
  310. {
  311. m_flMoveTime = 4.0f;
  312. }
  313. }
  314. else
  315. {
  316. m_flIdleTime += TICK_INTERVAL;
  317. if ( m_flIdleTime > 4.0f )
  318. {
  319. m_flIdleTime = 4.0f;
  320. }
  321. m_flMoveTime -= TICK_INTERVAL * 2.0f;
  322. if ( m_flMoveTime < 0.0f )
  323. {
  324. m_flMoveTime = 0.0f;
  325. }
  326. }
  327. //Msg("Player time: [ACTIVE: %f]\t[IDLE: %f]\n", m_flMoveTime, m_flIdleTime );
  328. BaseClass::PlayerRunCommand( ucmd, moveHelper );
  329. }
  330. //-----------------------------------------------------------------------------
  331. // Purpose: Create and give the named item to the player. Then return it.
  332. //-----------------------------------------------------------------------------
  333. CBaseEntity *CHL1_Player::GiveNamedItem( const char *pszName, int iSubType )
  334. {
  335. // If I already own this type don't create one
  336. if ( Weapon_OwnsThisType(pszName, iSubType) )
  337. return NULL;
  338. // Msg( "giving %s\n", pszName );
  339. EHANDLE pent;
  340. pent = CreateEntityByName(pszName);
  341. if ( pent == NULL )
  342. {
  343. Msg( "NULL Ent in GiveNamedItem!\n" );
  344. return NULL;
  345. }
  346. pent->SetLocalOrigin( GetLocalOrigin() );
  347. pent->AddSpawnFlags( SF_NORESPAWN );
  348. if ( iSubType )
  349. {
  350. CBaseCombatWeapon *pWeapon = dynamic_cast<CBaseCombatWeapon*>( (CBaseEntity*)pent );
  351. if ( pWeapon )
  352. {
  353. pWeapon->SetSubType( iSubType );
  354. }
  355. }
  356. DispatchSpawn( pent );
  357. if ( pent != NULL && !(pent->IsMarkedForDeletion()) )
  358. {
  359. pent->Touch( this );
  360. }
  361. return pent;
  362. }
  363. //-----------------------------------------------------------------------------
  364. //-----------------------------------------------------------------------------
  365. void CHL1_Player::StartPullingObject( CBaseEntity *pObject )
  366. {
  367. if ( pObject->VPhysicsGetObject() == NULL || VPhysicsGetObject() == NULL )
  368. {
  369. return;
  370. }
  371. if( !(GetFlags()&FL_ONGROUND) )
  372. {
  373. //Msg("Can't grab in air!\n");
  374. return;
  375. }
  376. if( GetGroundEntity() == pObject )
  377. {
  378. //Msg("Can't grab something you're standing on!\n");
  379. return;
  380. }
  381. constraint_ballsocketparams_t ballsocket;
  382. ballsocket.Defaults();
  383. ballsocket.constraint.Defaults();
  384. ballsocket.constraint.forceLimit = lbs2kg(1000);
  385. ballsocket.constraint.torqueLimit = lbs2kg(1000);
  386. ballsocket.InitWithCurrentObjectState( VPhysicsGetObject(), pObject->VPhysicsGetObject(), WorldSpaceCenter() );
  387. m_pPullConstraint = physenv->CreateBallsocketConstraint( VPhysicsGetObject(), pObject->VPhysicsGetObject(), NULL, ballsocket );
  388. m_hPullObject.Set(pObject);
  389. m_bIsPullingObject = true;
  390. }
  391. //-----------------------------------------------------------------------------
  392. //-----------------------------------------------------------------------------
  393. void CHL1_Player::StopPullingObject()
  394. {
  395. if( m_pPullConstraint )
  396. {
  397. physenv->DestroyConstraint( m_pPullConstraint );
  398. }
  399. m_hPullObject.Set(NULL);
  400. m_pPullConstraint = NULL;
  401. m_bIsPullingObject = false;
  402. }
  403. //-----------------------------------------------------------------------------
  404. //-----------------------------------------------------------------------------
  405. void CHL1_Player::UpdatePullingObject()
  406. {
  407. if( !IsPullingObject() )
  408. return;
  409. CBaseEntity *pObject= m_hPullObject.Get();
  410. if( !pObject || !pObject->VPhysicsGetObject() )
  411. {
  412. // Object broke or otherwise vanished.
  413. StopPullingObject();
  414. return;
  415. }
  416. if( m_afButtonReleased & IN_USE )
  417. {
  418. // Player released +USE
  419. StopPullingObject();
  420. return;
  421. }
  422. float flMaxDistSqr = Square(PLAYER_USE_RADIUS + 1.0f);
  423. Vector objectPos;
  424. QAngle angle;
  425. pObject->VPhysicsGetObject()->GetPosition( &objectPos, &angle );
  426. if( !FInViewCone(objectPos) )
  427. {
  428. // Player turned away.
  429. StopPullingObject();
  430. }
  431. else if( objectPos.DistToSqr(WorldSpaceCenter()) > flMaxDistSqr )
  432. {
  433. // Object got caught up on something and left behind
  434. StopPullingObject();
  435. }
  436. }
  437. //-----------------------------------------------------------------------------
  438. // Purpose: Sets HL1specific defaults.
  439. //-----------------------------------------------------------------------------
  440. void CHL1_Player::Spawn(void)
  441. {
  442. // In multiplayer ,this is handled in the super class
  443. if ( !g_pGameRules->IsMultiplayer () )
  444. SetModel( "models/player.mdl" );
  445. BaseClass::Spawn();
  446. //
  447. // Our player movement speed is set once here. This will override the cl_xxxx
  448. // cvars unless they are set to be lower than this.
  449. //
  450. SetMaxSpeed( 1000 );
  451. SetDefaultFOV( 0 );
  452. m_nFlashBattery = 99;
  453. m_flFlashLightTime = 1;
  454. m_flFieldOfView = 0.5;
  455. StopPullingObject();
  456. m_Local.m_iHideHUD = 0;
  457. }
  458. //-----------------------------------------------------------------------------
  459. //-----------------------------------------------------------------------------
  460. void CHL1_Player::Event_Killed( const CTakeDamageInfo &info )
  461. {
  462. StopPullingObject();
  463. BaseClass::Event_Killed(info);
  464. }
  465. void CHL1_Player::CheckTimeBasedDamage( void )
  466. {
  467. int i;
  468. byte bDuration = 0;
  469. static float gtbdPrev = 0.0;
  470. // If we don't have any time based damage return.
  471. if ( !g_pGameRules->Damage_IsTimeBased( m_bitsDamageType ) )
  472. return;
  473. // only check for time based damage approx. every 2 seconds
  474. if (abs(gpGlobals->curtime - m_tbdPrev) < 2.0)
  475. return;
  476. m_tbdPrev = gpGlobals->curtime;
  477. for (i = 0; i < CDMG_TIMEBASED; i++)
  478. {
  479. // Make sure the damage type is really time-based.
  480. // This is kind of hacky but necessary until we setup DamageType as an enum.
  481. int iDamage = ( DMG_PARALYZE << i );
  482. if ( !g_pGameRules->Damage_IsTimeBased( iDamage ) )
  483. continue;
  484. // make sure bit is set for damage type
  485. if ( m_bitsDamageType & iDamage )
  486. {
  487. switch (i)
  488. {
  489. case itbd_Paralyze:
  490. // UNDONE - flag movement as half-speed
  491. bDuration = PARALYZE_DURATION;
  492. break;
  493. case itbd_NerveGas:
  494. // OnTakeDamage(pev, pev, NERVEGAS_DAMAGE, DMG_GENERIC);
  495. bDuration = NERVEGAS_DURATION;
  496. break;
  497. case itbd_PoisonRecover:
  498. OnTakeDamage( CTakeDamageInfo( this, this, POISON_DAMAGE, DMG_GENERIC ) );
  499. bDuration = POISON_DURATION;
  500. break;
  501. case itbd_Radiation:
  502. // OnTakeDamage(pev, pev, RADIATION_DAMAGE, DMG_GENERIC);
  503. bDuration = RADIATION_DURATION;
  504. break;
  505. case itbd_DrownRecover:
  506. // NOTE: this hack is actually used to RESTORE health
  507. // after the player has been drowning and finally takes a breath
  508. if (m_idrowndmg > m_idrownrestored)
  509. {
  510. int idif = MIN(m_idrowndmg - m_idrownrestored, 10);
  511. TakeHealth(idif, DMG_GENERIC);
  512. m_idrownrestored += idif;
  513. }
  514. bDuration = 4; // get up to 5*10 = 50 points back
  515. break;
  516. case itbd_Acid:
  517. // OnTakeDamage(pev, pev, ACID_DAMAGE, DMG_GENERIC);
  518. bDuration = ACID_DURATION;
  519. break;
  520. case itbd_SlowBurn:
  521. // OnTakeDamage(pev, pev, SLOWBURN_DAMAGE, DMG_GENERIC);
  522. bDuration = SLOWBURN_DURATION;
  523. break;
  524. case itbd_SlowFreeze:
  525. // OnTakeDamage(pev, pev, SLOWFREEZE_DAMAGE, DMG_GENERIC);
  526. bDuration = SLOWFREEZE_DURATION;
  527. break;
  528. default:
  529. bDuration = 0;
  530. }
  531. if (m_rgbTimeBasedDamage[i])
  532. {
  533. // decrement damage duration, detect when done.
  534. if (!m_rgbTimeBasedDamage[i] || --m_rgbTimeBasedDamage[i] == 0)
  535. {
  536. m_rgbTimeBasedDamage[i] = 0;
  537. // if we're done, clear damage bits
  538. m_bitsDamageType &= ~(DMG_PARALYZE << i);
  539. }
  540. }
  541. else
  542. // first time taking this damage type - init damage duration
  543. m_rgbTimeBasedDamage[i] = bDuration;
  544. }
  545. }
  546. }
  547. class CPhysicsPlayerCallback : public IPhysicsPlayerControllerEvent
  548. {
  549. public:
  550. int ShouldMoveTo( IPhysicsObject *pObject, const Vector &position )
  551. {
  552. CHL1_Player *pPlayer = (CHL1_Player *)pObject->GetGameData();
  553. if ( pPlayer )
  554. {
  555. if ( pPlayer->TouchedPhysics() )
  556. {
  557. return 0;
  558. }
  559. }
  560. return 1;
  561. }
  562. };
  563. static CPhysicsPlayerCallback playerCallback;
  564. //-----------------------------------------------------------------------------
  565. // Purpose:
  566. //-----------------------------------------------------------------------------
  567. void CHL1_Player::InitVCollision( const Vector &vecAbsOrigin, const Vector &vecAbsVelocity )
  568. {
  569. BaseClass::InitVCollision( vecAbsOrigin, vecAbsVelocity );
  570. // Setup the HL2 specific callback.
  571. GetPhysicsController()->SetEventHandler( &playerCallback );
  572. }
  573. CHL1_Player::~CHL1_Player( void )
  574. {
  575. }
  576. extern int gEvilImpulse101;
  577. void CHL1_Player::CheatImpulseCommands( int iImpulse )
  578. {
  579. if ( !sv_cheats->GetBool() )
  580. {
  581. return;
  582. }
  583. switch( iImpulse )
  584. {
  585. case 101:
  586. gEvilImpulse101 = true;
  587. GiveNamedItem( "item_suit" );
  588. GiveNamedItem( "item_battery" );
  589. GiveNamedItem( "weapon_crowbar" );
  590. GiveNamedItem( "weapon_glock" );
  591. GiveNamedItem( "ammo_9mmclip" );
  592. GiveNamedItem( "weapon_shotgun" );
  593. GiveNamedItem( "ammo_buckshot" );
  594. GiveNamedItem( "weapon_mp5" );
  595. GiveNamedItem( "ammo_9mmar" );
  596. GiveNamedItem( "ammo_argrenades" );
  597. GiveNamedItem( "weapon_handgrenade" );
  598. GiveNamedItem( "weapon_tripmine" );
  599. GiveNamedItem( "weapon_357" );
  600. GiveNamedItem( "ammo_357" );
  601. GiveNamedItem( "weapon_crossbow" );
  602. GiveNamedItem( "ammo_crossbow" );
  603. GiveNamedItem( "weapon_egon" );
  604. GiveNamedItem( "weapon_gauss" );
  605. GiveNamedItem( "ammo_gaussclip" );
  606. GiveNamedItem( "weapon_rpg" );
  607. GiveNamedItem( "ammo_rpgclip" );
  608. GiveNamedItem( "weapon_satchel" );
  609. GiveNamedItem( "weapon_snark" );
  610. GiveNamedItem( "weapon_hornetgun" );
  611. gEvilImpulse101 = false;
  612. break;
  613. case 0:
  614. default:
  615. BaseClass::CheatImpulseCommands( iImpulse );
  616. }
  617. }
  618. void CHL1_Player::SetupVisibility( CBaseEntity *pViewEntity, unsigned char *pvs, int pvssize )
  619. {
  620. int area = pViewEntity ? pViewEntity->NetworkProp()->AreaNum() : NetworkProp()->AreaNum();
  621. BaseClass::SetupVisibility( pViewEntity, pvs, pvssize );
  622. PointCameraSetupVisibility( this, area, pvs, pvssize );
  623. }
  624. #define ARMOR_RATIO 0.2 // Armor Takes 80% of the damage
  625. #define ARMOR_BONUS 0.5 // Each Point of Armor is work 1/x points of health
  626. int CHL1_Player::OnTakeDamage( const CTakeDamageInfo &inputInfo )
  627. {
  628. // have suit diagnose the problem - ie: report damage type
  629. int bitsDamage = inputInfo.GetDamageType();
  630. int ffound = true;
  631. int fmajor;
  632. int fcritical;
  633. int fTookDamage;
  634. int ftrivial;
  635. float flRatio;
  636. float flBonus;
  637. float flHealthPrev = m_iHealth;
  638. CTakeDamageInfo info = inputInfo;
  639. if ( info.GetDamage() > 0.0f )
  640. {
  641. m_flLastDamageTime = gpGlobals->curtime;
  642. }
  643. flBonus = ARMOR_BONUS;
  644. flRatio = ARMOR_RATIO;
  645. if ( ( info.GetDamageType() & DMG_BLAST ) && g_pGameRules->IsMultiplayer() )
  646. {
  647. // blasts damage armor more.
  648. flBonus *= 2;
  649. }
  650. // Already dead
  651. if ( !IsAlive() )
  652. return 0;
  653. // go take the damage first
  654. if ( !g_pGameRules->FPlayerCanTakeDamage( this, info.GetAttacker(), info ) )
  655. {
  656. // Refuse the damage
  657. return 0;
  658. }
  659. // keep track of amount of damage last sustained
  660. m_lastDamageAmount = info.GetDamage();
  661. // Armor.
  662. if ( ArmorValue() &&
  663. !(info.GetDamageType() & (DMG_FALL | DMG_DROWN | DMG_POISON)) && // armor doesn't protect against fall or drown damage!
  664. !(GetFlags() & FL_GODMODE) )
  665. {
  666. float flNew = info.GetDamage() * flRatio;
  667. float flArmor;
  668. flArmor = (info.GetDamage() - flNew) * flBonus;
  669. // Does this use more armor than we have?
  670. if ( flArmor > ArmorValue() )
  671. {
  672. flArmor = ArmorValue();
  673. flArmor *= (1/flBonus);
  674. flNew = info.GetDamage() - flArmor;
  675. SetArmorValue( 0 );
  676. }
  677. else
  678. SetArmorValue( ArmorValue() - flArmor );
  679. info.SetDamage( flNew );
  680. }
  681. // this cast to INT is critical!!! If a player ends up with 0.5 health, the engine will get that
  682. // as an int (zero) and think the player is dead! (this will incite a clientside screentilt, etc)
  683. info.SetDamage( (int)info.GetDamage() );
  684. fTookDamage = CBaseCombatCharacter::OnTakeDamage( info ); // Bypass CBasePlayer's
  685. if ( fTookDamage )
  686. {
  687. // add to the damage total for clients, which will be sent as a single
  688. // message at the end of the frame
  689. // todo: remove after combining shotgun blasts?
  690. if ( info.GetInflictor() && info.GetInflictor()->edict() )
  691. m_DmgOrigin = info.GetInflictor()->GetAbsOrigin();
  692. m_DmgTake += (int)info.GetDamage();
  693. }
  694. // Reset damage time countdown for each type of time based damage player just sustained
  695. for (int i = 0; i < CDMG_TIMEBASED; i++)
  696. {
  697. // Make sure the damage type is really time-based.
  698. // This is kind of hacky but necessary until we setup DamageType as an enum.
  699. int iDamage = ( DMG_PARALYZE << i );
  700. if ( ( info.GetDamageType() & iDamage ) && g_pGameRules->Damage_IsTimeBased( iDamage ) )
  701. {
  702. m_rgbTimeBasedDamage[i] = 0;
  703. }
  704. }
  705. // Display any effect associate with this damage type
  706. DamageEffect(info.GetDamage(),bitsDamage);
  707. // how bad is it, doc?
  708. ftrivial = (m_iHealth > 75 || m_lastDamageAmount < 5);
  709. fmajor = (m_lastDamageAmount > 25);
  710. fcritical = (m_iHealth < 30);
  711. // handle all bits set in this damage message,
  712. // let the suit give player the diagnosis
  713. // UNDONE: add sounds for types of damage sustained (ie: burn, shock, slash )
  714. // UNDONE: still need to record damage and heal messages for the following types
  715. // DMG_BURN
  716. // DMG_FREEZE
  717. // DMG_BLAST
  718. // DMG_SHOCK
  719. m_bitsDamageType |= bitsDamage; // Save this so we can report it to the client
  720. m_bitsHUDDamage = -1; // make sure the damage bits get resent
  721. bool bTimeBasedDamage = g_pGameRules->Damage_IsTimeBased( bitsDamage );
  722. while (fTookDamage && (!ftrivial || bTimeBasedDamage) && ffound && bitsDamage)
  723. {
  724. ffound = false;
  725. if (bitsDamage & DMG_CLUB)
  726. {
  727. if (fmajor)
  728. SetSuitUpdate("!HEV_DMG4", false, SUIT_NEXT_IN_30SEC); // minor fracture
  729. bitsDamage &= ~DMG_CLUB;
  730. ffound = true;
  731. }
  732. if (bitsDamage & (DMG_FALL | DMG_CRUSH))
  733. {
  734. if (fmajor)
  735. SetSuitUpdate("!HEV_DMG5", false, SUIT_NEXT_IN_30SEC); // major fracture
  736. else
  737. SetSuitUpdate("!HEV_DMG4", false, SUIT_NEXT_IN_30SEC); // minor fracture
  738. bitsDamage &= ~(DMG_FALL | DMG_CRUSH);
  739. ffound = true;
  740. }
  741. if (bitsDamage & DMG_BULLET)
  742. {
  743. if (m_lastDamageAmount > 5)
  744. SetSuitUpdate("!HEV_DMG6", false, SUIT_NEXT_IN_30SEC); // blood loss detected
  745. //else
  746. // SetSuitUpdate("!HEV_DMG0", false, SUIT_NEXT_IN_30SEC); // minor laceration
  747. bitsDamage &= ~DMG_BULLET;
  748. ffound = true;
  749. }
  750. if (bitsDamage & DMG_SLASH)
  751. {
  752. if (fmajor)
  753. SetSuitUpdate("!HEV_DMG1", false, SUIT_NEXT_IN_30SEC); // major laceration
  754. else
  755. SetSuitUpdate("!HEV_DMG0", false, SUIT_NEXT_IN_30SEC); // minor laceration
  756. bitsDamage &= ~DMG_SLASH;
  757. ffound = true;
  758. }
  759. if (bitsDamage & DMG_SONIC)
  760. {
  761. if (fmajor)
  762. SetSuitUpdate("!HEV_DMG2", false, SUIT_NEXT_IN_1MIN); // internal bleeding
  763. bitsDamage &= ~DMG_SONIC;
  764. ffound = true;
  765. }
  766. if (bitsDamage & (DMG_POISON | DMG_PARALYZE))
  767. {
  768. if (bitsDamage & DMG_POISON)
  769. {
  770. m_nPoisonDmg += info.GetDamage();
  771. m_tbdPrev = gpGlobals->curtime;
  772. m_rgbTimeBasedDamage[itbd_PoisonRecover] = 0;
  773. }
  774. SetSuitUpdate("!HEV_DMG3", false, SUIT_NEXT_IN_1MIN); // blood toxins detected
  775. bitsDamage &= ~( DMG_POISON | DMG_PARALYZE );
  776. ffound = true;
  777. }
  778. if (bitsDamage & DMG_ACID)
  779. {
  780. SetSuitUpdate("!HEV_DET1", false, SUIT_NEXT_IN_1MIN); // hazardous chemicals detected
  781. bitsDamage &= ~DMG_ACID;
  782. ffound = true;
  783. }
  784. if (bitsDamage & DMG_NERVEGAS)
  785. {
  786. SetSuitUpdate("!HEV_DET0", false, SUIT_NEXT_IN_1MIN); // biohazard detected
  787. bitsDamage &= ~DMG_NERVEGAS;
  788. ffound = true;
  789. }
  790. if (bitsDamage & DMG_RADIATION)
  791. {
  792. SetSuitUpdate("!HEV_DET2", false, SUIT_NEXT_IN_1MIN); // radiation detected
  793. bitsDamage &= ~DMG_RADIATION;
  794. ffound = true;
  795. }
  796. if (bitsDamage & DMG_SHOCK)
  797. {
  798. bitsDamage &= ~DMG_SHOCK;
  799. ffound = true;
  800. }
  801. }
  802. m_Local.m_vecPunchAngle.SetX( -2 );
  803. if (fTookDamage && !ftrivial && fmajor && flHealthPrev >= 75)
  804. {
  805. // first time we take major damage...
  806. // turn automedic on if not on
  807. SetSuitUpdate("!HEV_MED1", false, SUIT_NEXT_IN_30MIN); // automedic on
  808. // give morphine shot if not given recently
  809. SetSuitUpdate("!HEV_HEAL7", false, SUIT_NEXT_IN_30MIN); // morphine shot
  810. }
  811. if (fTookDamage && !ftrivial && fcritical && flHealthPrev < 75)
  812. {
  813. // already took major damage, now it's critical...
  814. if (m_iHealth < 6)
  815. SetSuitUpdate("!HEV_HLTH3", false, SUIT_NEXT_IN_10MIN); // near death
  816. else if (m_iHealth < 20)
  817. SetSuitUpdate("!HEV_HLTH2", false, SUIT_NEXT_IN_10MIN); // health critical
  818. // give critical health warnings
  819. if (!random->RandomInt(0,3) && flHealthPrev < 50)
  820. SetSuitUpdate("!HEV_DMG7", false, SUIT_NEXT_IN_5MIN); //seek medical attention
  821. }
  822. // if we're taking time based damage, warn about its continuing effects
  823. if (fTookDamage && g_pGameRules->Damage_IsTimeBased( info.GetDamageType() ) && flHealthPrev < 75)
  824. {
  825. if (flHealthPrev < 50)
  826. {
  827. if (!random->RandomInt(0,3))
  828. SetSuitUpdate("!HEV_DMG7", false, SUIT_NEXT_IN_5MIN); //seek medical attention
  829. }
  830. else
  831. SetSuitUpdate("!HEV_HLTH1", false, SUIT_NEXT_IN_10MIN); // health dropping
  832. }
  833. // Do special explosion damage effect
  834. if ( bitsDamage & DMG_BLAST )
  835. {
  836. OnDamagedByExplosion( info );
  837. }
  838. gamestats->Event_PlayerDamage( this, info );
  839. return fTookDamage;
  840. }
  841. int CHL1_Player::OnTakeDamage_Alive( const CTakeDamageInfo &info )
  842. {
  843. int nRet;
  844. int nSavedHealth = m_iHealth;
  845. // Drown
  846. if( info.GetDamageType() & DMG_DROWN )
  847. {
  848. if( m_idrowndmg == m_idrownrestored )
  849. {
  850. EmitSound( "Player.DrownStart" );
  851. }
  852. else
  853. {
  854. EmitSound( "Player.DrownContinue" );
  855. }
  856. }
  857. nRet = BaseClass::OnTakeDamage_Alive( info );
  858. if ( GetFlags() & FL_GODMODE )
  859. {
  860. m_iHealth = nSavedHealth;
  861. }
  862. else if (m_debugOverlays & OVERLAY_BUDDHA_MODE)
  863. {
  864. if ( m_iHealth <= 0 )
  865. {
  866. m_iHealth = 1;
  867. }
  868. }
  869. return nRet;
  870. }
  871. //-----------------------------------------------------------------------------
  872. // Purpose:
  873. //-----------------------------------------------------------------------------
  874. void CHL1_Player::FindMissTargets( void )
  875. {
  876. if ( m_flTargetFindTime > gpGlobals->curtime )
  877. return;
  878. m_flTargetFindTime = gpGlobals->curtime + 1.0f;
  879. m_nNumMissPositions = 0;
  880. CBaseEntity *pEnts[256];
  881. Vector radius( 80, 80, 80);
  882. int numEnts = UTIL_EntitiesInBox( pEnts, 256, GetAbsOrigin()-radius, GetAbsOrigin()+radius, 0 );
  883. for ( int i = 0; i < numEnts; i++ )
  884. {
  885. if ( pEnts[i] == NULL )
  886. continue;
  887. if ( m_nNumMissPositions >= 16 )
  888. return;
  889. //See if it's a good target candidate
  890. if ( FClassnameIs( pEnts[i], "prop_dynamic" ) ||
  891. FClassnameIs( pEnts[i], "dynamic_prop" ) ||
  892. FClassnameIs( pEnts[i], "prop_physics" ) ||
  893. FClassnameIs( pEnts[i], "physics_prop" ) )
  894. {
  895. //NDebugOverlay::Cross3D( pEnts[i]->WorldSpaceCenter(), -Vector(4,4,4), Vector(4,4,4), 0, 255, 0, true, 1.0f );
  896. m_vecMissPositions[m_nNumMissPositions++] = pEnts[i]->WorldSpaceCenter();
  897. continue;
  898. }
  899. }
  900. }
  901. //-----------------------------------------------------------------------------
  902. // Purpose:
  903. // Output : Good position to shoot at
  904. //-----------------------------------------------------------------------------
  905. bool CHL1_Player::GetMissPosition( Vector *position )
  906. {
  907. if ( m_nNumMissPositions == 0 )
  908. return false;
  909. (*position) = m_vecMissPositions[ random->RandomInt( 0, m_nNumMissPositions-1 ) ];
  910. return true;
  911. }
  912. //-----------------------------------------------------------------------------
  913. //-----------------------------------------------------------------------------
  914. int CHL1_Player::FlashlightIsOn( void )
  915. {
  916. return IsEffectActive( EF_DIMLIGHT);
  917. }
  918. //-----------------------------------------------------------------------------
  919. //-----------------------------------------------------------------------------
  920. void CHL1_Player::FlashlightTurnOn( void )
  921. {
  922. if ( IsSuitEquipped() )
  923. {
  924. AddEffects( EF_DIMLIGHT );
  925. CPASAttenuationFilter filter( this );
  926. EmitSound( filter, entindex(), "Player.FlashlightOn" );
  927. m_flFlashLightTime = FLASH_DRAIN_TIME + gpGlobals->curtime;
  928. }
  929. }
  930. //-----------------------------------------------------------------------------
  931. //-----------------------------------------------------------------------------
  932. void CHL1_Player::FlashlightTurnOff( void )
  933. {
  934. RemoveEffects( EF_DIMLIGHT );
  935. CPASAttenuationFilter filter( this );
  936. EmitSound( filter, entindex(), "Player.FlashlightOff" );
  937. m_flFlashLightTime = FLASH_CHARGE_TIME + gpGlobals->curtime;
  938. }
  939. void CHL1_Player::UpdateClientData( void )
  940. {
  941. if (m_DmgTake || m_DmgSave || m_bitsHUDDamage != m_bitsDamageType)
  942. {
  943. // Comes from inside me if not set
  944. Vector damageOrigin = GetLocalOrigin();
  945. // send "damage" message
  946. // causes screen to flash, and pain compass to show direction of damage
  947. damageOrigin = m_DmgOrigin;
  948. // only send down damage type that have hud art
  949. int iShowHudDamage = g_pGameRules->Damage_GetShowOnHud();
  950. int visibleDamageBits = m_bitsDamageType & iShowHudDamage;
  951. m_DmgTake = clamp( m_DmgTake, 0, 255 );
  952. m_DmgSave = clamp( m_DmgSave, 0, 255 );
  953. CSingleUserRecipientFilter user( this );
  954. user.MakeReliable();
  955. UserMessageBegin( user, "Damage" );
  956. WRITE_BYTE( m_DmgSave );
  957. WRITE_BYTE( m_DmgTake );
  958. WRITE_LONG( visibleDamageBits );
  959. WRITE_FLOAT( damageOrigin.x ); //BUG: Should be fixed point (to hud) not floats
  960. WRITE_FLOAT( damageOrigin.y ); //BUG: However, the HUD does _not_ implement bitfield messages (yet)
  961. WRITE_FLOAT( damageOrigin.z ); //BUG: We use WRITE_VEC3COORD for everything else
  962. MessageEnd();
  963. m_DmgTake = 0;
  964. m_DmgSave = 0;
  965. m_bitsHUDDamage = m_bitsDamageType;
  966. // Clear off non-time-based damage indicators
  967. int iDamage = g_pGameRules->Damage_GetTimeBased();
  968. m_bitsDamageType &= iDamage;
  969. }
  970. // Update Flashlight
  971. if ( ( m_flFlashLightTime ) && ( m_flFlashLightTime <= gpGlobals->curtime ) )
  972. {
  973. if ( FlashlightIsOn() )
  974. {
  975. if ( m_nFlashBattery )
  976. {
  977. m_flFlashLightTime = FLASH_DRAIN_TIME + gpGlobals->curtime;
  978. m_nFlashBattery--;
  979. if ( !m_nFlashBattery )
  980. FlashlightTurnOff();
  981. }
  982. }
  983. else
  984. {
  985. if ( m_nFlashBattery < 100 )
  986. {
  987. m_flFlashLightTime = FLASH_CHARGE_TIME + gpGlobals->curtime;
  988. m_nFlashBattery++;
  989. }
  990. else
  991. m_flFlashLightTime = 0;
  992. }
  993. }
  994. BaseClass::UpdateClientData();
  995. }
  996. void CHL1_Player::OnSave( IEntitySaveUtils *pUtils )
  997. {
  998. // If I'm pulling a box, stop.
  999. StopPullingObject();
  1000. BaseClass::OnSave(pUtils);
  1001. }
  1002. void CHL1_Player::CreateViewModel( int index /*=0*/ )
  1003. {
  1004. Assert( index >= 0 && index < MAX_VIEWMODELS );
  1005. if ( GetViewModel( index ) )
  1006. return;
  1007. CPredictedViewModel *vm = ( CPredictedViewModel * )CreateEntityByName( "predicted_viewmodel" );
  1008. if ( vm )
  1009. {
  1010. vm->SetAbsOrigin( GetAbsOrigin() );
  1011. vm->SetOwner( this );
  1012. vm->SetIndex( index );
  1013. DispatchSpawn( vm );
  1014. vm->FollowEntity( this );
  1015. m_hViewModel.Set( index, vm );
  1016. }
  1017. }
  1018. void CHL1_Player::OnRestore( void )
  1019. {
  1020. BaseClass::OnRestore();
  1021. // If we are controlling a train, resend our train status
  1022. if( !FBitSet( m_iTrain, TRAIN_OFF ) )
  1023. {
  1024. m_iTrain |= TRAIN_NEW;
  1025. }
  1026. }
  1027. //------------------------------------------------------------------------------
  1028. //------------------------------------------------------------------------------
  1029. static void MatrixOrthogonalize( matrix3x4_t &matrix, int column )
  1030. {
  1031. Vector columns[3];
  1032. int i;
  1033. for ( i = 0; i < 3; i++ )
  1034. {
  1035. MatrixGetColumn( matrix, i, columns[i] );
  1036. }
  1037. int index0 = column;
  1038. int index1 = (column+1)%3;
  1039. int index2 = (column+2)%3;
  1040. columns[index2] = CrossProduct( columns[index0], columns[index1] );
  1041. columns[index1] = CrossProduct( columns[index2], columns[index0] );
  1042. VectorNormalize( columns[index2] );
  1043. VectorNormalize( columns[index1] );
  1044. MatrixSetColumn( columns[index1], index1, matrix );
  1045. MatrixSetColumn( columns[index2], index2, matrix );
  1046. }
  1047. #define SIGN(x) ( (x) < 0 ? -1 : 1 )
  1048. static QAngle AlignAngles( const QAngle &angles, float cosineAlignAngle )
  1049. {
  1050. matrix3x4_t alignMatrix;
  1051. AngleMatrix( angles, alignMatrix );
  1052. for ( int j = 0; j < 3; j++ )
  1053. {
  1054. Vector vec;
  1055. MatrixGetColumn( alignMatrix, j, vec );
  1056. for ( int i = 0; i < 3; i++ )
  1057. {
  1058. if ( fabs(vec[i]) > cosineAlignAngle )
  1059. {
  1060. vec[i] = SIGN(vec[i]);
  1061. vec[(i+1)%3] = 0;
  1062. vec[(i+2)%3] = 0;
  1063. MatrixSetColumn( vec, j, alignMatrix );
  1064. MatrixOrthogonalize( alignMatrix, j );
  1065. break;
  1066. }
  1067. }
  1068. }
  1069. QAngle out;
  1070. MatrixAngles( alignMatrix, out );
  1071. return out;
  1072. }
  1073. 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 )
  1074. {
  1075. physcollision->TraceBox( boxOrigin, boxOrigin + (start-end), mins, maxs, pCollide, start, angles, ptr );
  1076. if ( ptr->DidHit() )
  1077. {
  1078. ptr->endpos = start * (1-ptr->fraction) + end * ptr->fraction;
  1079. ptr->startpos = start;
  1080. ptr->plane.dist = -ptr->plane.dist;
  1081. ptr->plane.normal *= -1;
  1082. }
  1083. }
  1084. //---------------------------------------------
  1085. #include "player_pickup.h"
  1086. #include "props.h"
  1087. #include "vphysics/friction.h"
  1088. #include "physics_saverestore.h"
  1089. ConVar hl2_normspeed( "hl2_normspeed", "190" );
  1090. ConVar player_throwforce( "player_throwforce", "1000" );
  1091. ConVar physcannon_maxmass( "physcannon_maxmass", "250" );
  1092. // derive from this so we can add save/load data to it
  1093. struct game_shadowcontrol_params_t : public hlshadowcontrol_params_t
  1094. {
  1095. DECLARE_SIMPLE_DATADESC();
  1096. };
  1097. BEGIN_SIMPLE_DATADESC( game_shadowcontrol_params_t )
  1098. DEFINE_FIELD( targetPosition, FIELD_POSITION_VECTOR ),
  1099. DEFINE_FIELD( targetRotation, FIELD_VECTOR ),
  1100. DEFINE_FIELD( maxAngular, FIELD_FLOAT ),
  1101. DEFINE_FIELD( maxDampAngular, FIELD_FLOAT ),
  1102. DEFINE_FIELD( maxSpeed, FIELD_FLOAT ),
  1103. DEFINE_FIELD( maxDampSpeed, FIELD_FLOAT ),
  1104. DEFINE_FIELD( dampFactor, FIELD_FLOAT ),
  1105. DEFINE_FIELD( teleportDistance, FIELD_FLOAT ),
  1106. END_DATADESC()
  1107. // when looking level, hold bottom of object 8 inches below eye level
  1108. #define PLAYER_HOLD_LEVEL_EYES -8
  1109. // when looking down, hold bottom of object 0 inches from feet
  1110. #define PLAYER_HOLD_DOWN_FEET 2
  1111. // when looking up, hold bottom of object 24 inches above eye level
  1112. #define PLAYER_HOLD_UP_EYES 24
  1113. // use a +/-30 degree range for the entire range of motion of pitch
  1114. #define PLAYER_LOOK_PITCH_RANGE 30
  1115. // player can reach down 2ft below his feet (otherwise he'll hold the object above the bottom)
  1116. #define PLAYER_REACH_DOWN_DISTANCE 24
  1117. static void ComputePlayerMatrix( CBasePlayer *pPlayer, matrix3x4_t &out )
  1118. {
  1119. if ( !pPlayer )
  1120. return;
  1121. QAngle angles = pPlayer->EyeAngles();
  1122. Vector origin = pPlayer->EyePosition();
  1123. // 0-360 / -180-180
  1124. //angles.x = init ? 0 : AngleDistance( angles.x, 0 );
  1125. //angles.x = clamp( angles.x, -PLAYER_LOOK_PITCH_RANGE, PLAYER_LOOK_PITCH_RANGE );
  1126. angles.x = 0;
  1127. float feet = pPlayer->GetAbsOrigin().z + pPlayer->WorldAlignMins().z;
  1128. float eyes = origin.z;
  1129. float zoffset = 0;
  1130. // moving up (negative pitch is up)
  1131. if ( angles.x < 0 )
  1132. {
  1133. zoffset = RemapVal( angles.x, 0, -PLAYER_LOOK_PITCH_RANGE, PLAYER_HOLD_LEVEL_EYES, PLAYER_HOLD_UP_EYES );
  1134. }
  1135. else
  1136. {
  1137. zoffset = RemapVal( angles.x, 0, PLAYER_LOOK_PITCH_RANGE, PLAYER_HOLD_LEVEL_EYES, PLAYER_HOLD_DOWN_FEET + (feet - eyes) );
  1138. }
  1139. origin.z += zoffset;
  1140. angles.x = 0;
  1141. AngleMatrix( angles, origin, out );
  1142. }
  1143. //-----------------------------------------------------------------------------
  1144. class CGrabController : public IMotionEvent
  1145. {
  1146. DECLARE_SIMPLE_DATADESC();
  1147. public:
  1148. CGrabController( void );
  1149. ~CGrabController( void );
  1150. void AttachEntity( CBasePlayer *pPlayer, CBaseEntity *pEntity, IPhysicsObject *pPhys, bool bIsMegaPhysCannon = false );
  1151. void DetachEntity();
  1152. void OnRestore();
  1153. bool UpdateObject( CBasePlayer *pPlayer, float flError );
  1154. void SetTargetPosition( const Vector &target, const QAngle &targetOrientation );
  1155. float ComputeError();
  1156. float GetLoadWeight( void ) const { return m_flLoadWeight; }
  1157. void SetAngleAlignment( float alignAngleCosine ) { m_angleAlignment = alignAngleCosine; }
  1158. void SetIgnorePitch( bool bIgnore ) { m_bIgnoreRelativePitch = bIgnore; }
  1159. QAngle TransformAnglesToPlayerSpace( const QAngle &anglesIn, CBasePlayer *pPlayer );
  1160. QAngle TransformAnglesFromPlayerSpace( const QAngle &anglesIn, CBasePlayer *pPlayer );
  1161. CBaseEntity *GetAttached() { return (CBaseEntity *)m_attachedEntity; }
  1162. IMotionEvent::simresult_e Simulate( IPhysicsMotionController *pController, IPhysicsObject *pObject, float deltaTime, Vector &linear, AngularImpulse &angular );
  1163. float GetSavedMass( IPhysicsObject *pObject );
  1164. private:
  1165. // Compute the max speed for an attached object
  1166. void ComputeMaxSpeed( CBaseEntity *pEntity, IPhysicsObject *pPhysics );
  1167. game_shadowcontrol_params_t m_shadow;
  1168. float m_timeToArrive;
  1169. float m_errorTime;
  1170. float m_error;
  1171. float m_contactAmount;
  1172. float m_angleAlignment;
  1173. bool m_bCarriedEntityBlocksLOS;
  1174. bool m_bIgnoreRelativePitch;
  1175. float m_flLoadWeight;
  1176. float m_savedRotDamping[VPHYSICS_MAX_OBJECT_LIST_COUNT];
  1177. float m_savedMass[VPHYSICS_MAX_OBJECT_LIST_COUNT];
  1178. EHANDLE m_attachedEntity;
  1179. QAngle m_vecPreferredCarryAngles;
  1180. bool m_bHasPreferredCarryAngles;
  1181. QAngle m_attachedAnglesPlayerSpace;
  1182. Vector m_attachedPositionObjectSpace;
  1183. IPhysicsMotionController *m_controller;
  1184. friend class CWeaponPhysCannon;
  1185. };
  1186. BEGIN_SIMPLE_DATADESC( CGrabController )
  1187. DEFINE_EMBEDDED( m_shadow ),
  1188. DEFINE_FIELD( m_timeToArrive, FIELD_FLOAT ),
  1189. DEFINE_FIELD( m_errorTime, FIELD_FLOAT ),
  1190. DEFINE_FIELD( m_error, FIELD_FLOAT ),
  1191. DEFINE_FIELD( m_contactAmount, FIELD_FLOAT ),
  1192. DEFINE_AUTO_ARRAY( m_savedRotDamping, FIELD_FLOAT ),
  1193. DEFINE_AUTO_ARRAY( m_savedMass, FIELD_FLOAT ),
  1194. DEFINE_FIELD( m_flLoadWeight, FIELD_FLOAT ),
  1195. DEFINE_FIELD( m_bCarriedEntityBlocksLOS, FIELD_BOOLEAN ),
  1196. DEFINE_FIELD( m_bIgnoreRelativePitch, FIELD_BOOLEAN ),
  1197. DEFINE_FIELD( m_attachedEntity, FIELD_EHANDLE ),
  1198. DEFINE_FIELD( m_angleAlignment, FIELD_FLOAT ),
  1199. DEFINE_FIELD( m_vecPreferredCarryAngles, FIELD_VECTOR ),
  1200. DEFINE_FIELD( m_bHasPreferredCarryAngles, FIELD_BOOLEAN ),
  1201. DEFINE_FIELD( m_attachedAnglesPlayerSpace, FIELD_VECTOR ),
  1202. DEFINE_FIELD( m_attachedPositionObjectSpace, FIELD_VECTOR ),
  1203. // Physptrs can't be inside embedded classes
  1204. // DEFINE_PHYSPTR( m_controller ),
  1205. END_DATADESC()
  1206. const float DEFAULT_MAX_ANGULAR = 360.0f * 10.0f;
  1207. const float REDUCED_CARRY_MASS = 1.0f;
  1208. CGrabController::CGrabController( void )
  1209. {
  1210. m_shadow.dampFactor = 1.0;
  1211. m_shadow.teleportDistance = 0;
  1212. m_errorTime = 0;
  1213. m_error = 0;
  1214. // make this controller really stiff!
  1215. m_shadow.maxSpeed = 1000;
  1216. m_shadow.maxAngular = DEFAULT_MAX_ANGULAR;
  1217. m_shadow.maxDampSpeed = m_shadow.maxSpeed*2;
  1218. m_shadow.maxDampAngular = m_shadow.maxAngular;
  1219. m_attachedEntity = NULL;
  1220. m_vecPreferredCarryAngles = vec3_angle;
  1221. m_bHasPreferredCarryAngles = false;
  1222. }
  1223. CGrabController::~CGrabController( void )
  1224. {
  1225. DetachEntity();
  1226. }
  1227. void CGrabController::OnRestore()
  1228. {
  1229. if ( m_controller )
  1230. {
  1231. m_controller->SetEventHandler( this );
  1232. }
  1233. }
  1234. void CGrabController::SetTargetPosition( const Vector &target, const QAngle &targetOrientation )
  1235. {
  1236. m_shadow.targetPosition = target;
  1237. m_shadow.targetRotation = targetOrientation;
  1238. m_timeToArrive = gpGlobals->frametime;
  1239. CBaseEntity *pAttached = GetAttached();
  1240. if ( pAttached )
  1241. {
  1242. IPhysicsObject *pObj = pAttached->VPhysicsGetObject();
  1243. if ( pObj != NULL )
  1244. {
  1245. pObj->Wake();
  1246. }
  1247. else
  1248. {
  1249. DetachEntity();
  1250. }
  1251. }
  1252. }
  1253. float CGrabController::ComputeError()
  1254. {
  1255. if ( m_errorTime <= 0 )
  1256. return 0;
  1257. CBaseEntity *pAttached = GetAttached();
  1258. if ( pAttached )
  1259. {
  1260. Vector pos;
  1261. IPhysicsObject *pObj = pAttached->VPhysicsGetObject();
  1262. if ( pObj )
  1263. {
  1264. pObj->GetShadowPosition( &pos, NULL );
  1265. float error = (m_shadow.targetPosition - pos).Length();
  1266. if ( m_errorTime > 0 )
  1267. {
  1268. if ( m_errorTime > 1 )
  1269. {
  1270. m_errorTime = 1;
  1271. }
  1272. float speed = error / m_errorTime;
  1273. if ( speed > m_shadow.maxSpeed )
  1274. {
  1275. error *= 0.5;
  1276. }
  1277. m_error = (1-m_errorTime) * m_error + error * m_errorTime;
  1278. }
  1279. }
  1280. else
  1281. {
  1282. DevMsg( "Object attached to Physcannon has no physics object\n" );
  1283. DetachEntity();
  1284. return 9999; // force detach
  1285. }
  1286. }
  1287. if ( pAttached->IsEFlagSet( EFL_IS_BEING_LIFTED_BY_BARNACLE ) )
  1288. {
  1289. m_error *= 3.0f;
  1290. }
  1291. m_errorTime = 0;
  1292. return m_error;
  1293. }
  1294. #define MASS_SPEED_SCALE 60
  1295. #define MAX_MASS 40
  1296. void CGrabController::ComputeMaxSpeed( CBaseEntity *pEntity, IPhysicsObject *pPhysics )
  1297. {
  1298. m_shadow.maxSpeed = 1000;
  1299. m_shadow.maxAngular = DEFAULT_MAX_ANGULAR;
  1300. // Compute total mass...
  1301. float flMass = PhysGetEntityMass( pEntity );
  1302. float flMaxMass = physcannon_maxmass.GetFloat();
  1303. if ( flMass <= flMaxMass )
  1304. return;
  1305. float flLerpFactor = clamp( flMass, flMaxMass, 500.0f );
  1306. flLerpFactor = SimpleSplineRemapVal( flLerpFactor, flMaxMass, 500.0f, 0.0f, 1.0f );
  1307. float invMass = pPhysics->GetInvMass();
  1308. float invInertia = pPhysics->GetInvInertia().Length();
  1309. float invMaxMass = 1.0f / MAX_MASS;
  1310. float ratio = invMaxMass / invMass;
  1311. invMass = invMaxMass;
  1312. invInertia *= ratio;
  1313. float maxSpeed = invMass * MASS_SPEED_SCALE * 200;
  1314. float maxAngular = invInertia * MASS_SPEED_SCALE * 360;
  1315. m_shadow.maxSpeed = Lerp( flLerpFactor, m_shadow.maxSpeed, maxSpeed );
  1316. m_shadow.maxAngular = Lerp( flLerpFactor, m_shadow.maxAngular, maxAngular );
  1317. }
  1318. QAngle CGrabController::TransformAnglesToPlayerSpace( const QAngle &anglesIn, CBasePlayer *pPlayer )
  1319. {
  1320. if ( m_bIgnoreRelativePitch )
  1321. {
  1322. matrix3x4_t test;
  1323. QAngle angleTest = pPlayer->EyeAngles();
  1324. angleTest.x = 0;
  1325. AngleMatrix( angleTest, test );
  1326. return TransformAnglesToLocalSpace( anglesIn, test );
  1327. }
  1328. return TransformAnglesToLocalSpace( anglesIn, pPlayer->EntityToWorldTransform() );
  1329. }
  1330. QAngle CGrabController::TransformAnglesFromPlayerSpace( const QAngle &anglesIn, CBasePlayer *pPlayer )
  1331. {
  1332. if ( m_bIgnoreRelativePitch )
  1333. {
  1334. matrix3x4_t test;
  1335. QAngle angleTest = pPlayer->EyeAngles();
  1336. angleTest.x = 0;
  1337. AngleMatrix( angleTest, test );
  1338. return TransformAnglesToWorldSpace( anglesIn, test );
  1339. }
  1340. return TransformAnglesToWorldSpace( anglesIn, pPlayer->EntityToWorldTransform() );
  1341. }
  1342. void CGrabController::AttachEntity( CBasePlayer *pPlayer, CBaseEntity *pEntity, IPhysicsObject *pPhys, bool bIsMegaPhysCannon )
  1343. {
  1344. // play the impact sound of the object hitting the player
  1345. // used as feedback to let the player know he picked up the object
  1346. PhysicsImpactSound( pPlayer, pPhys, CHAN_STATIC, pPhys->GetMaterialIndex(), pPlayer->VPhysicsGetObject()->GetMaterialIndex(), 1.0, 64 );
  1347. Vector position;
  1348. QAngle angles;
  1349. pPhys->GetPosition( &position, &angles );
  1350. // If it has a preferred orientation, use that instead.
  1351. Pickup_GetPreferredCarryAngles( pEntity, pPlayer, pPlayer->EntityToWorldTransform(), angles );
  1352. // ComputeMaxSpeed( pEntity, pPhys );
  1353. // Carried entities can never block LOS
  1354. m_bCarriedEntityBlocksLOS = pEntity->BlocksLOS();
  1355. pEntity->SetBlocksLOS( false );
  1356. m_controller = physenv->CreateMotionController( this );
  1357. m_controller->AttachObject( pPhys, true );
  1358. // Don't do this, it's causing trouble with constraint solvers.
  1359. //m_controller->SetPriority( IPhysicsMotionController::HIGH_PRIORITY );
  1360. pPhys->Wake();
  1361. PhysSetGameFlags( pPhys, FVPHYSICS_PLAYER_HELD );
  1362. SetTargetPosition( position, angles );
  1363. m_attachedEntity = pEntity;
  1364. IPhysicsObject *pList[VPHYSICS_MAX_OBJECT_LIST_COUNT];
  1365. int count = pEntity->VPhysicsGetObjectList( pList, ARRAYSIZE(pList) );
  1366. m_flLoadWeight = 0;
  1367. float damping = 10;
  1368. float flFactor = count / 7.5f;
  1369. if ( flFactor < 1.0f )
  1370. {
  1371. flFactor = 1.0f;
  1372. }
  1373. for ( int i = 0; i < count; i++ )
  1374. {
  1375. float mass = pList[i]->GetMass();
  1376. pList[i]->GetDamping( NULL, &m_savedRotDamping[i] );
  1377. m_flLoadWeight += mass;
  1378. m_savedMass[i] = mass;
  1379. // reduce the mass to prevent the player from adding crazy amounts of energy to the system
  1380. pList[i]->SetMass( REDUCED_CARRY_MASS / flFactor );
  1381. pList[i]->SetDamping( NULL, &damping );
  1382. }
  1383. // Give extra mass to the phys object we're actually picking up
  1384. pPhys->SetMass( REDUCED_CARRY_MASS );
  1385. pPhys->EnableDrag( false );
  1386. m_errorTime = bIsMegaPhysCannon ? -1.5f : -1.0f; // 1 seconds until error starts accumulating
  1387. m_error = 0;
  1388. m_contactAmount = 0;
  1389. m_attachedAnglesPlayerSpace = TransformAnglesToPlayerSpace( angles, pPlayer );
  1390. if ( m_angleAlignment != 0 )
  1391. {
  1392. m_attachedAnglesPlayerSpace = AlignAngles( m_attachedAnglesPlayerSpace, m_angleAlignment );
  1393. }
  1394. VectorITransform( pEntity->WorldSpaceCenter(), pEntity->EntityToWorldTransform(), m_attachedPositionObjectSpace );
  1395. // If it's a prop, see if it has desired carry angles
  1396. CPhysicsProp *pProp = dynamic_cast<CPhysicsProp *>(pEntity);
  1397. if ( pProp )
  1398. {
  1399. m_bHasPreferredCarryAngles = pProp->GetPropDataAngles( "preferred_carryangles", m_vecPreferredCarryAngles );
  1400. }
  1401. else
  1402. {
  1403. m_bHasPreferredCarryAngles = false;
  1404. }
  1405. }
  1406. static void ClampPhysicsVelocity( IPhysicsObject *pPhys, float linearLimit, float angularLimit )
  1407. {
  1408. Vector vel;
  1409. AngularImpulse angVel;
  1410. pPhys->GetVelocity( &vel, &angVel );
  1411. float speed = VectorNormalize(vel) - linearLimit;
  1412. float angSpeed = VectorNormalize(angVel) - angularLimit;
  1413. speed = speed < 0 ? 0 : -speed;
  1414. angSpeed = angSpeed < 0 ? 0 : -angSpeed;
  1415. vel *= speed;
  1416. angVel *= angSpeed;
  1417. pPhys->AddVelocity( &vel, &angVel );
  1418. }
  1419. void CGrabController::DetachEntity()
  1420. {
  1421. CBaseEntity *pEntity = GetAttached();
  1422. if ( pEntity )
  1423. {
  1424. // Restore the LS blocking state
  1425. pEntity->SetBlocksLOS( m_bCarriedEntityBlocksLOS );
  1426. IPhysicsObject *pList[VPHYSICS_MAX_OBJECT_LIST_COUNT];
  1427. int count = pEntity->VPhysicsGetObjectList( pList, ARRAYSIZE(pList) );
  1428. for ( int i = 0; i < count; i++ )
  1429. {
  1430. IPhysicsObject *pPhys = pList[i];
  1431. if ( !pPhys )
  1432. continue;
  1433. // on the odd chance that it's gone to sleep while under anti-gravity
  1434. pPhys->EnableDrag( true );
  1435. pPhys->Wake();
  1436. pPhys->SetMass( m_savedMass[i] );
  1437. pPhys->SetDamping( NULL, &m_savedRotDamping[i] );
  1438. PhysClearGameFlags( pPhys, FVPHYSICS_PLAYER_HELD );
  1439. if ( pPhys->GetContactPoint( NULL, NULL ) )
  1440. {
  1441. PhysForceClearVelocity( pPhys );
  1442. }
  1443. else
  1444. {
  1445. ClampPhysicsVelocity( pPhys, hl2_normspeed.GetFloat() * 1.5f, 2.0f * 360.0f );
  1446. }
  1447. }
  1448. }
  1449. m_attachedEntity = NULL;
  1450. physenv->DestroyMotionController( m_controller );
  1451. m_controller = NULL;
  1452. }
  1453. static bool InContactWithHeavyObject( IPhysicsObject *pObject, float heavyMass )
  1454. {
  1455. bool contact = false;
  1456. IPhysicsFrictionSnapshot *pSnapshot = pObject->CreateFrictionSnapshot();
  1457. while ( pSnapshot->IsValid() )
  1458. {
  1459. IPhysicsObject *pOther = pSnapshot->GetObject( 1 );
  1460. if ( !pOther->IsMoveable() || pOther->GetMass() > heavyMass )
  1461. {
  1462. contact = true;
  1463. break;
  1464. }
  1465. pSnapshot->NextFrictionData();
  1466. }
  1467. pObject->DestroyFrictionSnapshot( pSnapshot );
  1468. return contact;
  1469. }
  1470. IMotionEvent::simresult_e CGrabController::Simulate( IPhysicsMotionController *pController, IPhysicsObject *pObject, float deltaTime, Vector &linear, AngularImpulse &angular )
  1471. {
  1472. game_shadowcontrol_params_t shadowParams = m_shadow;
  1473. if ( InContactWithHeavyObject( pObject, GetLoadWeight() ) )
  1474. {
  1475. m_contactAmount = Approach( 0.1f, m_contactAmount, deltaTime*2.0f );
  1476. }
  1477. else
  1478. {
  1479. m_contactAmount = Approach( 1.0f, m_contactAmount, deltaTime*2.0f );
  1480. }
  1481. shadowParams.maxAngular = m_shadow.maxAngular * m_contactAmount * m_contactAmount * m_contactAmount;
  1482. m_timeToArrive = pObject->ComputeShadowControl( shadowParams, m_timeToArrive, deltaTime );
  1483. // Slide along the current contact points to fix bouncing problems
  1484. Vector velocity;
  1485. AngularImpulse angVel;
  1486. pObject->GetVelocity( &velocity, &angVel );
  1487. PhysComputeSlideDirection( pObject, velocity, angVel, &velocity, &angVel, GetLoadWeight() );
  1488. pObject->SetVelocityInstantaneous( &velocity, NULL );
  1489. linear.Init();
  1490. angular.Init();
  1491. m_errorTime += deltaTime;
  1492. return SIM_LOCAL_ACCELERATION;
  1493. }
  1494. float CGrabController::GetSavedMass( IPhysicsObject *pObject )
  1495. {
  1496. CBaseEntity *pHeld = m_attachedEntity;
  1497. if ( pHeld )
  1498. {
  1499. if ( pObject->GetGameData() == (void*)pHeld )
  1500. {
  1501. IPhysicsObject *pList[VPHYSICS_MAX_OBJECT_LIST_COUNT];
  1502. int count = pHeld->VPhysicsGetObjectList( pList, ARRAYSIZE(pList) );
  1503. for ( int i = 0; i < count; i++ )
  1504. {
  1505. if ( pList[i] == pObject )
  1506. return m_savedMass[i];
  1507. }
  1508. }
  1509. }
  1510. return 0.0f;
  1511. }
  1512. bool CGrabController::UpdateObject( CBasePlayer *pPlayer, float flError )
  1513. {
  1514. CBaseEntity *pEntity = GetAttached();
  1515. if ( !pEntity || ComputeError() > flError || pPlayer->GetGroundEntity() == pEntity || !pEntity->VPhysicsGetObject() )
  1516. {
  1517. return false;
  1518. }
  1519. //Adrian: Oops, our object became motion disabled, let go!
  1520. IPhysicsObject *pPhys = pEntity->VPhysicsGetObject();
  1521. if ( pPhys && pPhys->IsMoveable() == false )
  1522. {
  1523. return false;
  1524. }
  1525. Vector forward, right, up;
  1526. QAngle playerAngles = pPlayer->EyeAngles();
  1527. float pitch = AngleDistance(playerAngles.x,0);
  1528. playerAngles.x = clamp( pitch, -75, 75 );
  1529. AngleVectors( playerAngles, &forward, &right, &up );
  1530. // Now clamp a sphere of object radius at end to the player's bbox
  1531. Vector radial = physcollision->CollideGetExtent( pPhys->GetCollide(), vec3_origin, pEntity->GetAbsAngles(), -forward );
  1532. Vector player2d = pPlayer->CollisionProp()->OBBMaxs();
  1533. float playerRadius = player2d.Length2D();
  1534. float radius = playerRadius + fabs(DotProduct( forward, radial ));
  1535. float distance = 24 + ( radius * 2.0f );
  1536. Vector start = pPlayer->Weapon_ShootPosition();
  1537. Vector end = start + ( forward * distance );
  1538. trace_t tr;
  1539. CTraceFilterSkipTwoEntities traceFilter( pPlayer, pEntity, COLLISION_GROUP_NONE );
  1540. Ray_t ray;
  1541. ray.Init( start, end );
  1542. enginetrace->TraceRay( ray, MASK_SOLID_BRUSHONLY, &traceFilter, &tr );
  1543. if ( tr.fraction < 0.5 )
  1544. {
  1545. end = start + forward * (radius*0.5f);
  1546. }
  1547. else if ( tr.fraction <= 1.0f )
  1548. {
  1549. end = start + forward * ( distance - radius );
  1550. }
  1551. Vector playerMins, playerMaxs, nearest;
  1552. pPlayer->CollisionProp()->WorldSpaceAABB( &playerMins, &playerMaxs );
  1553. Vector playerLine = pPlayer->CollisionProp()->WorldSpaceCenter();
  1554. CalcClosestPointOnLine( end, playerLine+Vector(0,0,playerMins.z), playerLine+Vector(0,0,playerMaxs.z), nearest, NULL );
  1555. Vector delta = end - nearest;
  1556. float len = VectorNormalize(delta);
  1557. if ( len < radius )
  1558. {
  1559. end = nearest + radius * delta;
  1560. }
  1561. /*
  1562. //Show overlays of radius
  1563. if ( g_debug_physcannon.GetBool() )
  1564. {
  1565. NDebugOverlay::Box( end, -Vector( 2,2,2 ), Vector(2,2,2), 0, 255, 0, true, 0 );
  1566. NDebugOverlay::Box( GetAttached()->WorldSpaceCenter(),
  1567. -Vector( radius, radius, radius),
  1568. Vector( radius, radius, radius ),
  1569. 255, 0, 0,
  1570. true,
  1571. 0.0f );
  1572. }
  1573. */
  1574. QAngle angles = TransformAnglesFromPlayerSpace( m_attachedAnglesPlayerSpace, pPlayer );
  1575. // If it has a preferred orientation, update to ensure we're still oriented correctly.
  1576. Pickup_GetPreferredCarryAngles( pEntity, pPlayer, pPlayer->EntityToWorldTransform(), angles );
  1577. // We may be holding a prop that has preferred carry angles
  1578. if ( m_bHasPreferredCarryAngles )
  1579. {
  1580. matrix3x4_t tmp;
  1581. ComputePlayerMatrix( pPlayer, tmp );
  1582. angles = TransformAnglesToWorldSpace( m_vecPreferredCarryAngles, tmp );
  1583. }
  1584. matrix3x4_t attachedToWorld;
  1585. Vector offset;
  1586. AngleMatrix( angles, attachedToWorld );
  1587. VectorRotate( m_attachedPositionObjectSpace, attachedToWorld, offset );
  1588. SetTargetPosition( end - offset, angles );
  1589. return true;
  1590. }
  1591. //-----------------------------------------------------------------------------
  1592. // Player pickup controller
  1593. //-----------------------------------------------------------------------------
  1594. class CPlayerPickupController : public CBaseEntity
  1595. {
  1596. DECLARE_DATADESC();
  1597. DECLARE_CLASS( CPlayerPickupController, CBaseEntity );
  1598. public:
  1599. void Init( CBasePlayer *pPlayer, CBaseEntity *pObject );
  1600. void Shutdown( bool bThrown = false );
  1601. bool OnControls( CBaseEntity *pControls ) { return true; }
  1602. void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
  1603. void OnRestore()
  1604. {
  1605. m_grabController.OnRestore();
  1606. }
  1607. void VPhysicsUpdate( IPhysicsObject *pPhysics ){}
  1608. void VPhysicsShadowUpdate( IPhysicsObject *pPhysics ) {}
  1609. bool IsHoldingEntity( CBaseEntity *pEnt );
  1610. CGrabController &GetGrabController() { return m_grabController; }
  1611. private:
  1612. CGrabController m_grabController;
  1613. CBasePlayer *m_pPlayer;
  1614. };
  1615. LINK_ENTITY_TO_CLASS( player_pickup, CPlayerPickupController );
  1616. //---------------------------------------------------------
  1617. // Save/Restore
  1618. //---------------------------------------------------------
  1619. BEGIN_DATADESC( CPlayerPickupController )
  1620. DEFINE_EMBEDDED( m_grabController ),
  1621. // Physptrs can't be inside embedded classes
  1622. DEFINE_PHYSPTR( m_grabController.m_controller ),
  1623. DEFINE_FIELD( m_pPlayer, FIELD_CLASSPTR ),
  1624. END_DATADESC()
  1625. //-----------------------------------------------------------------------------
  1626. // Purpose:
  1627. // Input : *pPlayer -
  1628. // *pObject -
  1629. //-----------------------------------------------------------------------------
  1630. void CPlayerPickupController::Init( CBasePlayer *pPlayer, CBaseEntity *pObject )
  1631. {
  1632. // Holster player's weapon
  1633. if ( pPlayer->GetActiveWeapon() )
  1634. {
  1635. if ( !pPlayer->GetActiveWeapon()->Holster() )
  1636. {
  1637. Shutdown();
  1638. return;
  1639. }
  1640. }
  1641. // If the target is debris, convert it to non-debris
  1642. if ( pObject->GetCollisionGroup() == COLLISION_GROUP_DEBRIS )
  1643. {
  1644. // Interactive debris converts back to debris when it comes to rest
  1645. pObject->SetCollisionGroup( COLLISION_GROUP_INTERACTIVE_DEBRIS );
  1646. }
  1647. // done so I'll go across level transitions with the player
  1648. SetParent( pPlayer );
  1649. m_grabController.SetIgnorePitch( true );
  1650. m_grabController.SetAngleAlignment( DOT_30DEGREE );
  1651. m_pPlayer = pPlayer;
  1652. IPhysicsObject *pPhysics = pObject->VPhysicsGetObject();
  1653. Pickup_OnPhysGunPickup( pObject, m_pPlayer );
  1654. m_grabController.AttachEntity( pPlayer, pObject, pPhysics );
  1655. m_pPlayer->m_Local.m_iHideHUD |= HIDEHUD_WEAPONSELECTION;
  1656. m_pPlayer->SetUseEntity( this );
  1657. }
  1658. //-----------------------------------------------------------------------------
  1659. // Purpose:
  1660. // Input : bool -
  1661. //-----------------------------------------------------------------------------
  1662. void CPlayerPickupController::Shutdown( bool bThrown )
  1663. {
  1664. CBaseEntity *pObject = m_grabController.GetAttached();
  1665. m_grabController.DetachEntity();
  1666. if ( pObject != NULL )
  1667. {
  1668. Pickup_OnPhysGunDrop( pObject, m_pPlayer, bThrown ? THROWN_BY_PLAYER : DROPPED_BY_PLAYER );
  1669. }
  1670. if ( m_pPlayer )
  1671. {
  1672. m_pPlayer->SetUseEntity( NULL );
  1673. if ( m_pPlayer->GetActiveWeapon() )
  1674. {
  1675. m_pPlayer->GetActiveWeapon()->Deploy();
  1676. }
  1677. m_pPlayer->m_Local.m_iHideHUD &= ~HIDEHUD_WEAPONSELECTION;
  1678. }
  1679. Remove();
  1680. }
  1681. void CPlayerPickupController::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
  1682. {
  1683. if ( ToBasePlayer(pActivator) == m_pPlayer )
  1684. {
  1685. CBaseEntity *pAttached = m_grabController.GetAttached();
  1686. // UNDONE: Use vphysics stress to decide to drop objects
  1687. // UNDONE: Must fix case of forcing objects into the ground you're standing on (causes stress) before that will work
  1688. if ( !pAttached || useType == USE_OFF || (m_pPlayer->m_nButtons & IN_ATTACK2) || m_grabController.ComputeError() > 12 )
  1689. {
  1690. Shutdown();
  1691. return;
  1692. }
  1693. //Adrian: Oops, our object became motion disabled, let go!
  1694. IPhysicsObject *pPhys = pAttached->VPhysicsGetObject();
  1695. if ( pPhys && pPhys->IsMoveable() == false )
  1696. {
  1697. Shutdown();
  1698. return;
  1699. }
  1700. #if STRESS_TEST
  1701. vphysics_objectstress_t stress;
  1702. CalculateObjectStress( pAttached->VPhysicsGetObject(), pAttached, &stress );
  1703. if ( stress.exertedStress > 250 )
  1704. {
  1705. Shutdown();
  1706. return;
  1707. }
  1708. #endif
  1709. // +ATTACK will throw phys objects
  1710. if ( m_pPlayer->m_nButtons & IN_ATTACK )
  1711. {
  1712. Shutdown( true );
  1713. Vector vecLaunch;
  1714. m_pPlayer->EyeVectors( &vecLaunch );
  1715. // JAY: Scale this with mass because some small objects really go flying
  1716. float massFactor = clamp( pAttached->VPhysicsGetObject()->GetMass(), 0.5, 15 );
  1717. massFactor = RemapVal( massFactor, 0.5, 15, 0.5, 4 );
  1718. vecLaunch *= player_throwforce.GetFloat() * massFactor;
  1719. pAttached->VPhysicsGetObject()->ApplyForceCenter( vecLaunch );
  1720. AngularImpulse aVel = RandomAngularImpulse( -10, 10 ) * massFactor;
  1721. pAttached->VPhysicsGetObject()->ApplyTorqueCenter( aVel );
  1722. return;
  1723. }
  1724. if ( useType == USE_SET )
  1725. {
  1726. // update position
  1727. m_grabController.UpdateObject( m_pPlayer, 12 );
  1728. }
  1729. }
  1730. }
  1731. //-----------------------------------------------------------------------------
  1732. // Purpose:
  1733. // Input : *pEnt -
  1734. // Output : Returns true on success, false on failure.
  1735. //-----------------------------------------------------------------------------
  1736. bool CPlayerPickupController::IsHoldingEntity( CBaseEntity *pEnt )
  1737. {
  1738. return ( m_grabController.GetAttached() == pEnt );
  1739. }
  1740. ConVar hl1_new_pull( "hl1_new_pull", "1" );
  1741. void PlayerPickupObject( CBasePlayer *pPlayer, CBaseEntity *pObject )
  1742. {
  1743. if( hl1_new_pull.GetBool() )
  1744. {
  1745. CHL1_Player *pHL1Player = dynamic_cast<CHL1_Player*>(pPlayer);
  1746. if( pHL1Player && !pHL1Player->IsPullingObject() )
  1747. {
  1748. pHL1Player->StartPullingObject(pObject);
  1749. }
  1750. }
  1751. }