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.

1117 lines
35 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================//
  7. #include "cbase.h"
  8. #include "vehicle_base.h"
  9. #include "engine/IEngineSound.h"
  10. #include "in_buttons.h"
  11. #include "soundenvelope.h"
  12. #include "soundent.h"
  13. #include "physics_saverestore.h"
  14. #include "vphysics/constraints.h"
  15. #include "vcollide_parse.h"
  16. #include "ndebugoverlay.h"
  17. #include "npc_vehicledriver.h"
  18. #include "vehicle_crane.h"
  19. #include "hl2_player.h"
  20. #include "rumble_shared.h"
  21. // memdbgon must be the last include file in a .cpp file!!!
  22. #include "tier0/memdbgon.h"
  23. #define VEHICLE_HITBOX_DRIVER 1
  24. extern ConVar g_debug_vehicledriver;
  25. // Crane spring constants
  26. #define CRANE_SPRING_CONSTANT_HANGING 2e5f
  27. #define CRANE_SPRING_CONSTANT_INITIAL_RAISING (CRANE_SPRING_CONSTANT_HANGING * 0.5)
  28. #define CRANE_SPRING_CONSTANT_LOWERING 30.0f
  29. #define CRANE_SPRING_DAMPING 2e5f
  30. #define CRANE_SPRING_RELATIVE_DAMPING 2
  31. // Crane bones that have physics followers
  32. static const char *pCraneFollowerBoneNames[] =
  33. {
  34. "base",
  35. "arm",
  36. "platform",
  37. };
  38. // Crane tip
  39. LINK_ENTITY_TO_CLASS( crane_tip, CCraneTip );
  40. BEGIN_DATADESC( CCraneTip )
  41. DEFINE_PHYSPTR( m_pSpring ),
  42. END_DATADESC()
  43. // Crane
  44. LINK_ENTITY_TO_CLASS( prop_vehicle_crane, CPropCrane );
  45. BEGIN_DATADESC( CPropCrane )
  46. // Inputs
  47. DEFINE_INPUTFUNC( FIELD_VOID, "Lock", InputLock ),
  48. DEFINE_INPUTFUNC( FIELD_VOID, "Unlock", InputUnlock ),
  49. DEFINE_INPUTFUNC( FIELD_VOID, "ForcePlayerIn", InputForcePlayerIn ),
  50. // Keys
  51. DEFINE_EMBEDDED( m_ServerVehicle ),
  52. DEFINE_EMBEDDED( m_BoneFollowerManager ),
  53. DEFINE_FIELD( m_hPlayer, FIELD_EHANDLE ),
  54. DEFINE_FIELD( m_bMagnetOn, FIELD_BOOLEAN ),
  55. DEFINE_FIELD( m_hNPCDriver, FIELD_EHANDLE ),
  56. DEFINE_FIELD( m_nNPCButtons, FIELD_INTEGER ),
  57. DEFINE_FIELD( m_bLocked, FIELD_BOOLEAN ),
  58. DEFINE_FIELD( m_bEnterAnimOn, FIELD_BOOLEAN ),
  59. DEFINE_FIELD( m_bExitAnimOn, FIELD_BOOLEAN ),
  60. DEFINE_FIELD( m_vecEyeExitEndpoint, FIELD_POSITION_VECTOR ),
  61. DEFINE_OUTPUT( m_playerOn, "PlayerOn" ),
  62. DEFINE_OUTPUT( m_playerOff, "PlayerOff" ),
  63. DEFINE_FIELD( m_iTurning, FIELD_INTEGER ),
  64. DEFINE_FIELD( m_bStartSoundAtCrossover, FIELD_BOOLEAN ),
  65. DEFINE_FIELD( m_flTurn, FIELD_FLOAT ),
  66. DEFINE_FIELD( m_bExtending, FIELD_BOOLEAN ),
  67. DEFINE_FIELD( m_flExtension, FIELD_FLOAT ),
  68. DEFINE_FIELD( m_flExtensionRate, FIELD_FLOAT ),
  69. DEFINE_FIELD( m_bDropping, FIELD_BOOLEAN ),
  70. //DEFINE_FIELD( m_flNextDangerSoundTime, FIELD_TIME ),
  71. //DEFINE_FIELD( m_flNextCreakSound, FIELD_TIME ),
  72. DEFINE_FIELD( m_flNextDropAllowedTime, FIELD_TIME ),
  73. DEFINE_FIELD( m_flSlowRaiseTime, FIELD_TIME ),
  74. DEFINE_FIELD( m_flMaxExtensionSpeed, FIELD_FLOAT ),
  75. DEFINE_FIELD( m_flMaxTurnSpeed, FIELD_FLOAT ),
  76. DEFINE_FIELD( m_flExtensionAccel, FIELD_FLOAT ),
  77. DEFINE_FIELD( m_flExtensionDecel, FIELD_FLOAT ),
  78. DEFINE_FIELD( m_flTurnAccel, FIELD_FLOAT ),
  79. DEFINE_FIELD( m_flTurnDecel, FIELD_FLOAT ),
  80. DEFINE_KEYFIELD( m_iszMagnetName, FIELD_STRING, "magnetname" ),
  81. DEFINE_FIELD( m_hCraneMagnet, FIELD_EHANDLE ),
  82. DEFINE_FIELD( m_hCraneTip, FIELD_EHANDLE ),
  83. DEFINE_FIELD( m_hRope, FIELD_EHANDLE ),
  84. DEFINE_PHYSPTR( m_pConstraintGroup ),
  85. DEFINE_KEYFIELD( m_vehicleScript, FIELD_STRING, "vehiclescript" ),
  86. END_DATADESC()
  87. IMPLEMENT_SERVERCLASS_ST(CPropCrane, DT_PropCrane)
  88. SendPropEHandle(SENDINFO(m_hPlayer)),
  89. SendPropBool(SENDINFO(m_bMagnetOn)),
  90. SendPropBool(SENDINFO(m_bEnterAnimOn)),
  91. SendPropBool(SENDINFO(m_bExitAnimOn)),
  92. SendPropVector(SENDINFO(m_vecEyeExitEndpoint), -1, SPROP_COORD),
  93. END_SEND_TABLE();
  94. //------------------------------------------------
  95. // Precache
  96. //------------------------------------------------
  97. void CPropCrane::Precache( void )
  98. {
  99. BaseClass::Precache();
  100. m_ServerVehicle.Initialize( STRING(m_vehicleScript) );
  101. }
  102. //------------------------------------------------
  103. // Spawn
  104. //------------------------------------------------
  105. void CPropCrane::Spawn( void )
  106. {
  107. Precache();
  108. SetModel( STRING( GetModelName() ) );
  109. SetCollisionGroup( COLLISION_GROUP_VEHICLE );
  110. BaseClass::Spawn();
  111. SetSolid( SOLID_BBOX );
  112. AddSolidFlags( FSOLID_NOT_SOLID );
  113. SetMoveType( MOVETYPE_NOCLIP );
  114. m_takedamage = DAMAGE_EVENTS_ONLY;
  115. m_flTurn = 0;
  116. m_flExtension = 0;
  117. m_flNextDangerSoundTime = 0;
  118. m_flNextCreakSound = 0;
  119. m_flNextDropAllowedTime = 0;
  120. m_flSlowRaiseTime = 0;
  121. m_bDropping = false;
  122. m_bMagnetOn = false;
  123. InitCraneSpeeds();
  124. SetPoseParameter( "armextensionpose", m_flExtension );
  125. CreateVPhysics();
  126. SetNextThink( gpGlobals->curtime );
  127. }
  128. //-----------------------------------------------------------------------------
  129. // Purpose:
  130. //-----------------------------------------------------------------------------
  131. void CPropCrane::Activate( void )
  132. {
  133. BaseClass::Activate();
  134. // If we load a game, we don't need to set this all up again.
  135. if ( m_hCraneMagnet )
  136. return;
  137. // Find our magnet
  138. if ( m_iszMagnetName == NULL_STRING )
  139. {
  140. Warning( "prop_vehicle_crane %s has no magnet entity specified!\n", STRING(GetEntityName()) );
  141. UTIL_Remove( this );
  142. return;
  143. }
  144. m_hCraneMagnet = dynamic_cast<CPhysMagnet *>(gEntList.FindEntityByName( NULL, STRING(m_iszMagnetName) ));
  145. if ( !m_hCraneMagnet )
  146. {
  147. Warning( "prop_vehicle_crane %s failed to find magnet %s.\n", STRING(GetEntityName()), STRING(m_iszMagnetName) );
  148. UTIL_Remove( this );
  149. return;
  150. }
  151. // We want the magnet to cast a long shadow
  152. m_hCraneMagnet->SetShadowCastDistance( 2048 );
  153. // Create our constraint group
  154. constraint_groupparams_t group;
  155. group.Defaults();
  156. m_pConstraintGroup = physenv->CreateConstraintGroup( group );
  157. m_hCraneMagnet->SetConstraintGroup( m_pConstraintGroup );
  158. // Create our crane tip
  159. Vector vecOrigin;
  160. QAngle vecAngles;
  161. GetCraneTipPosition( &vecOrigin, &vecAngles );
  162. m_hCraneTip = CCraneTip::Create( m_hCraneMagnet, m_pConstraintGroup, vecOrigin, vecAngles );
  163. if ( !m_hCraneTip )
  164. {
  165. UTIL_Remove( this );
  166. return;
  167. }
  168. m_pConstraintGroup->Activate();
  169. // Make a rope to connect 'em
  170. int iIndex = m_hCraneMagnet->LookupAttachment("magnetcable_a");
  171. m_hRope = CRopeKeyframe::Create( this, m_hCraneMagnet, 1, iIndex );
  172. if ( m_hRope )
  173. {
  174. m_hRope->m_Width = 3;
  175. m_hRope->m_nSegments = ROPE_MAX_SEGMENTS / 2;
  176. m_hRope->EnableWind( false );
  177. m_hRope->SetupHangDistance( 0 );
  178. m_hRope->m_RopeLength = (m_hCraneMagnet->GetAbsOrigin() - m_hCraneTip->GetAbsOrigin()).Length() * 1.1;
  179. }
  180. // Start with the magnet off
  181. TurnMagnetOff();
  182. }
  183. //-----------------------------------------------------------------------------
  184. // Purpose:
  185. // Output : Returns true on success, false on failure.
  186. //-----------------------------------------------------------------------------
  187. bool CPropCrane::CreateVPhysics( void )
  188. {
  189. BaseClass::CreateVPhysics();
  190. m_BoneFollowerManager.InitBoneFollowers( this, ARRAYSIZE(pCraneFollowerBoneNames), pCraneFollowerBoneNames );
  191. return true;
  192. }
  193. //-----------------------------------------------------------------------------
  194. // Purpose:
  195. //-----------------------------------------------------------------------------
  196. void CPropCrane::UpdateOnRemove( void )
  197. {
  198. m_BoneFollowerManager.DestroyBoneFollowers();
  199. BaseClass::UpdateOnRemove();
  200. }
  201. //-----------------------------------------------------------------------------
  202. // Purpose:
  203. //-----------------------------------------------------------------------------
  204. void CPropCrane::InitCraneSpeeds( void )
  205. {
  206. m_flMaxExtensionSpeed = CRANE_EXTENSION_RATE_MAX * 2;
  207. m_flMaxTurnSpeed = CRANE_TURN_RATE_MAX * 2;
  208. m_flExtensionAccel = CRANE_EXTENSION_ACCEL * 2;
  209. m_flExtensionDecel = CRANE_EXTENSION_DECEL * 2;
  210. m_flTurnAccel = CRANE_TURN_ACCEL * 2;
  211. m_flTurnDecel = CRANE_DECEL * 2;
  212. }
  213. //-----------------------------------------------------------------------------
  214. // Purpose:
  215. //-----------------------------------------------------------------------------
  216. void CPropCrane::TraceAttack( const CTakeDamageInfo &info, const Vector &vecDir, trace_t *ptr, CDmgAccumulator *pAccumulator )
  217. {
  218. if ( ptr->hitbox == VEHICLE_HITBOX_DRIVER )
  219. {
  220. if ( m_hPlayer != NULL )
  221. {
  222. m_hPlayer->TakeDamage( info );
  223. }
  224. }
  225. }
  226. //-----------------------------------------------------------------------------
  227. // Purpose:
  228. //-----------------------------------------------------------------------------
  229. int CPropCrane::OnTakeDamage( const CTakeDamageInfo &inputInfo )
  230. {
  231. //Do scaled up physics damage to the car
  232. CTakeDamageInfo info = inputInfo;
  233. info.ScaleDamage( 25 );
  234. // reset the damage
  235. info.SetDamage( inputInfo.GetDamage() );
  236. //Check to do damage to driver
  237. if ( m_hPlayer != NULL )
  238. {
  239. //Take no damage from physics damages
  240. if ( info.GetDamageType() & DMG_CRUSH )
  241. return 0;
  242. //Take the damage
  243. m_hPlayer->TakeDamage( info );
  244. }
  245. return 0;
  246. }
  247. //-----------------------------------------------------------------------------
  248. // Purpose:
  249. //-----------------------------------------------------------------------------
  250. Vector CPropCrane::BodyTarget( const Vector &posSrc, bool bNoisy )
  251. {
  252. Vector shotPos;
  253. matrix3x4_t matrix;
  254. int eyeAttachmentIndex = LookupAttachment("vehicle_driver_eyes");
  255. GetAttachment( eyeAttachmentIndex, matrix );
  256. MatrixGetColumn( matrix, 3, shotPos );
  257. if ( bNoisy )
  258. {
  259. shotPos[0] += random->RandomFloat( -8.0f, 8.0f );
  260. shotPos[1] += random->RandomFloat( -8.0f, 8.0f );
  261. shotPos[2] += random->RandomFloat( -8.0f, 8.0f );
  262. }
  263. return shotPos;
  264. }
  265. //-----------------------------------------------------------------------------
  266. // Purpose:
  267. //-----------------------------------------------------------------------------
  268. void CPropCrane::Think(void)
  269. {
  270. SetNextThink( gpGlobals->curtime + 0.1 );
  271. if ( GetDriver() )
  272. {
  273. BaseClass::Think();
  274. if ( m_hNPCDriver )
  275. {
  276. GetServerVehicle()->NPC_DriveVehicle();
  277. }
  278. // play enter animation
  279. StudioFrameAdvance();
  280. // If the enter or exit animation has finished, tell the server vehicle
  281. if ( IsSequenceFinished() && (m_bExitAnimOn || m_bEnterAnimOn) )
  282. {
  283. if ( m_bEnterAnimOn )
  284. {
  285. // Finished entering, display the hint for using the crane
  286. UTIL_HudHintText( m_hPlayer, "#Valve_Hint_CraneKeys" );
  287. }
  288. GetServerVehicle()->HandleEntryExitFinish( m_bExitAnimOn, true );
  289. }
  290. }
  291. else
  292. {
  293. // Run the crane's movement
  294. RunCraneMovement( 0.1 );
  295. }
  296. // Update follower bones
  297. m_BoneFollowerManager.UpdateBoneFollowers(this);
  298. }
  299. //-----------------------------------------------------------------------------
  300. // Purpose:
  301. // Input : *player -
  302. //-----------------------------------------------------------------------------
  303. void CPropCrane::ItemPostFrame( CBasePlayer *player )
  304. {
  305. }
  306. //-----------------------------------------------------------------------------
  307. // Purpose:
  308. //-----------------------------------------------------------------------------
  309. void CPropCrane::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
  310. {
  311. CBasePlayer *pPlayer = ToBasePlayer( pActivator );
  312. if ( !pPlayer )
  313. return;
  314. ResetUseKey( pPlayer );
  315. GetServerVehicle()->HandlePassengerEntry( pPlayer, (value>0) );
  316. }
  317. //-----------------------------------------------------------------------------
  318. // Purpose: Return true of the player's allowed to enter / exit the vehicle
  319. //-----------------------------------------------------------------------------
  320. bool CPropCrane::CanEnterVehicle( CBaseEntity *pEntity )
  321. {
  322. // Prevent entering if the vehicle's being driven by an NPC
  323. if ( GetDriver() && GetDriver() != pEntity )
  324. return false;
  325. // Prevent entering if the vehicle's locked
  326. return ( !m_bLocked );
  327. }
  328. //-----------------------------------------------------------------------------
  329. // Purpose: Return true of the player's allowed to enter / exit the vehicle
  330. //-----------------------------------------------------------------------------
  331. bool CPropCrane::CanExitVehicle( CBaseEntity *pEntity )
  332. {
  333. // Prevent exiting if the vehicle's locked, or rotating
  334. // Adrian: Check also if I'm currently jumping in or out.
  335. return ( !m_bLocked && (GetLocalAngularVelocity() == vec3_angle) && m_bExitAnimOn == false && m_bEnterAnimOn == false );
  336. }
  337. //-----------------------------------------------------------------------------
  338. // Purpose: Override base class to add display
  339. //-----------------------------------------------------------------------------
  340. void CPropCrane::DrawDebugGeometryOverlays(void)
  341. {
  342. // Draw if BBOX is on
  343. if ( m_debugOverlays & OVERLAY_BBOX_BIT )
  344. {
  345. Vector vecPoint = m_hCraneMagnet->GetAbsOrigin();
  346. int iIndex = m_hCraneMagnet->LookupAttachment("magnetcable_a");
  347. if ( iIndex >= 0 )
  348. {
  349. m_hCraneMagnet->GetAttachment( iIndex, vecPoint );
  350. }
  351. NDebugOverlay::Line( m_hCraneTip->GetAbsOrigin(), vecPoint, 255,255,255, true, 0.1 );
  352. }
  353. BaseClass::DrawDebugGeometryOverlays();
  354. }
  355. //-----------------------------------------------------------------------------
  356. // Purpose:
  357. //-----------------------------------------------------------------------------
  358. void CPropCrane::EnterVehicle( CBaseCombatCharacter *pPassenger )
  359. {
  360. if ( pPassenger == NULL )
  361. return;
  362. CBasePlayer *pPlayer = ToBasePlayer( pPassenger );
  363. if ( pPlayer != NULL )
  364. {
  365. // Remove any player who may be in the vehicle at the moment
  366. if ( m_hPlayer )
  367. {
  368. ExitVehicle( VEHICLE_ROLE_DRIVER );
  369. }
  370. m_hPlayer = pPlayer;
  371. m_playerOn.FireOutput( pPlayer, this, 0 );
  372. m_hPlayer->RumbleEffect( RUMBLE_FLAT_BOTH, 0, RUMBLE_FLAG_LOOP );
  373. m_hPlayer->RumbleEffect( RUMBLE_FLAT_BOTH, 10, RUMBLE_FLAG_UPDATE_SCALE );
  374. m_ServerVehicle.SoundStart();
  375. }
  376. else
  377. {
  378. // NPCs not yet supported - jdw
  379. Assert( 0 );
  380. }
  381. }
  382. //-----------------------------------------------------------------------------
  383. // Purpose:
  384. //-----------------------------------------------------------------------------
  385. void CPropCrane::ExitVehicle( int nRole )
  386. {
  387. CBasePlayer *pPlayer = m_hPlayer;
  388. if ( !pPlayer )
  389. return;
  390. m_hPlayer = NULL;
  391. ResetUseKey( pPlayer );
  392. m_playerOff.FireOutput( pPlayer, this, 0 );
  393. m_bEnterAnimOn = false;
  394. m_ServerVehicle.SoundShutdown( 1.0 );
  395. }
  396. //-----------------------------------------------------------------------------
  397. // Purpose:
  398. //-----------------------------------------------------------------------------
  399. void CPropCrane::ResetUseKey( CBasePlayer *pPlayer )
  400. {
  401. pPlayer->m_afButtonPressed &= ~IN_USE;
  402. }
  403. //-----------------------------------------------------------------------------
  404. // Purpose: Pass player movement into the crane's driving system
  405. //-----------------------------------------------------------------------------
  406. void CPropCrane::SetupMove( CBasePlayer *player, CUserCmd *ucmd, IMoveHelper *pHelper, CMoveData *move )
  407. {
  408. // If the player's entering/exiting the vehicle, prevent movement
  409. if ( !m_bEnterAnimOn && !m_bExitAnimOn )
  410. {
  411. int buttons = ucmd->buttons;
  412. if ( !(buttons & (IN_MOVELEFT|IN_MOVERIGHT)) )
  413. {
  414. if ( ucmd->sidemove < 0 )
  415. {
  416. buttons |= IN_MOVELEFT;
  417. }
  418. else if ( ucmd->sidemove > 0 )
  419. {
  420. buttons |= IN_MOVERIGHT;
  421. }
  422. }
  423. DriveCrane( buttons, player->m_afButtonPressed );
  424. }
  425. // Run the crane's movement
  426. RunCraneMovement( gpGlobals->frametime );
  427. }
  428. //-----------------------------------------------------------------------------
  429. // Purpose: Crane rotates around with +left and +right, and extends/retracts
  430. // the cable with +forward and +back.
  431. //-----------------------------------------------------------------------------
  432. void CPropCrane::DriveCrane( int iDriverButtons, int iButtonsPressed, float flNPCSteering )
  433. {
  434. bool bWasExtending = m_bExtending;
  435. // Handle rotation of the crane
  436. if ( iDriverButtons & IN_MOVELEFT )
  437. {
  438. // NPCs may cheat and set the steering
  439. if ( flNPCSteering )
  440. {
  441. m_flTurn = flNPCSteering;
  442. }
  443. else
  444. {
  445. // Try adding some randomness to make it feel shaky?
  446. float flTurnAdd = m_flTurnAccel;
  447. // If we're turning back on ourselves, use decel speed
  448. if ( m_flTurn < 0 )
  449. {
  450. flTurnAdd = MAX( flTurnAdd, m_flTurnDecel );
  451. }
  452. m_flTurn = UTIL_Approach( m_flMaxTurnSpeed, m_flTurn, flTurnAdd * gpGlobals->frametime );
  453. }
  454. m_iTurning = TURNING_LEFT;
  455. }
  456. else if ( iDriverButtons & IN_MOVERIGHT )
  457. {
  458. // NPCs may cheat and set the steering
  459. if ( flNPCSteering )
  460. {
  461. m_flTurn = flNPCSteering;
  462. }
  463. else
  464. {
  465. // Try adding some randomness to make it feel shaky?
  466. float flTurnAdd = m_flTurnAccel;
  467. // If we're turning back on ourselves, increase the rate
  468. if ( m_flTurn > 0 )
  469. {
  470. flTurnAdd = MAX( flTurnAdd, m_flTurnDecel );
  471. }
  472. m_flTurn = UTIL_Approach( -m_flMaxTurnSpeed, m_flTurn, flTurnAdd * gpGlobals->frametime );
  473. }
  474. m_iTurning = TURNING_RIGHT;
  475. }
  476. else
  477. {
  478. m_flTurn = UTIL_Approach( 0, m_flTurn, m_flTurnDecel * gpGlobals->frametime );
  479. m_iTurning = TURNING_NOT;
  480. }
  481. if ( m_hPlayer )
  482. {
  483. float maxTurn = GetMaxTurnRate();
  484. static float maxRumble = 0.35f;
  485. static float minRumble = 0.1f;
  486. float rumbleRange = maxRumble - minRumble;
  487. float rumble;
  488. float factor = fabs(m_flTurn) / maxTurn;
  489. factor = MIN( factor, 1.0f );
  490. rumble = minRumble + (rumbleRange * factor);
  491. m_hPlayer->RumbleEffect( RUMBLE_FLAT_BOTH, (int)(rumble * 100), RUMBLE_FLAG_UPDATE_SCALE );
  492. }
  493. SetLocalAngularVelocity( QAngle(0,m_flTurn * 10,0) );
  494. // Handle extension / retraction of the arm
  495. if ( iDriverButtons & IN_FORWARD )
  496. {
  497. m_flExtensionRate = UTIL_Approach( m_flMaxExtensionSpeed, m_flExtensionRate, m_flExtensionAccel * gpGlobals->frametime );
  498. m_bExtending = true;
  499. }
  500. else if ( iDriverButtons & IN_BACK )
  501. {
  502. m_flExtensionRate = UTIL_Approach( -m_flMaxExtensionSpeed, m_flExtensionRate, m_flExtensionAccel * gpGlobals->frametime );
  503. m_bExtending = true;
  504. }
  505. else
  506. {
  507. m_flExtensionRate = UTIL_Approach( 0, m_flExtensionRate, m_flExtensionDecel * gpGlobals->frametime );
  508. m_bExtending = false;
  509. }
  510. //Msg("Turn: %f\nExtensionRate: %f\n", m_flTurn, m_flExtensionRate );
  511. //If we're holding down an attack button, update our state
  512. if ( iButtonsPressed & (IN_ATTACK | IN_ATTACK2) )
  513. {
  514. // If we have something on the magnet, turn the magnet off
  515. if ( m_hCraneMagnet->GetTotalMassAttachedObjects() )
  516. {
  517. TurnMagnetOff();
  518. }
  519. else if ( !m_bDropping && m_flNextDropAllowedTime < gpGlobals->curtime )
  520. {
  521. TurnMagnetOn();
  522. // Drop the magnet till it hits something
  523. m_bDropping = true;
  524. m_hCraneMagnet->ResetHasHitSomething();
  525. m_hCraneTip->m_pSpring->SetSpringConstant( CRANE_SPRING_CONSTANT_LOWERING );
  526. m_ServerVehicle.PlaySound( VS_MISC1 );
  527. }
  528. }
  529. float flSpeedPercentage = clamp( fabs(m_flTurn) / m_flMaxTurnSpeed, 0, 1 );
  530. vbs_sound_update_t params;
  531. params.Defaults();
  532. params.bThrottleDown = (m_iTurning != TURNING_NOT);
  533. params.flCurrentSpeedFraction = flSpeedPercentage;
  534. params.flWorldSpaceSpeed = 0;
  535. m_ServerVehicle.SoundUpdate( params );
  536. // Play sounds for arm extension / retraction
  537. if ( m_bExtending && !bWasExtending )
  538. {
  539. m_ServerVehicle.StopSound( VS_ENGINE2_STOP );
  540. m_ServerVehicle.PlaySound( VS_ENGINE2_START );
  541. }
  542. else if ( !m_bExtending && bWasExtending )
  543. {
  544. m_ServerVehicle.StopSound( VS_ENGINE2_START );
  545. m_ServerVehicle.PlaySound( VS_ENGINE2_STOP );
  546. }
  547. }
  548. //-----------------------------------------------------------------------------
  549. // Purpose:
  550. //-----------------------------------------------------------------------------
  551. void CPropCrane::RecalculateCraneTip( void )
  552. {
  553. Vector vecOrigin;
  554. QAngle vecAngles;
  555. GetCraneTipPosition( &vecOrigin, &vecAngles );
  556. m_hCraneTip->SetAbsOrigin( vecOrigin );
  557. // NOTE: We need to do this because we're not using Physics...
  558. if ( m_hCraneTip->VPhysicsGetObject() )
  559. {
  560. m_hCraneTip->VPhysicsGetObject()->UpdateShadow( vecOrigin, vec3_angle, true, TICK_INTERVAL * 2.0f );
  561. }
  562. }
  563. //-----------------------------------------------------------------------------
  564. // Purpose:
  565. // Input : *pPlayer -
  566. // *pMoveData -
  567. //-----------------------------------------------------------------------------
  568. void CPropCrane::RunCraneMovement( float flTime )
  569. {
  570. if ( m_flExtensionRate )
  571. {
  572. // Extend / Retract the crane
  573. m_flExtension = clamp( m_flExtension + (m_flExtensionRate * 10 * flTime), 0, 2 );
  574. SetPoseParameter( "armextensionpose", m_flExtension );
  575. StudioFrameAdvance();
  576. }
  577. // Drop the magnet until it hits the ground
  578. if ( m_bDropping )
  579. {
  580. // Drop until the magnet hits something
  581. if ( m_hCraneMagnet->HasHitSomething() )
  582. {
  583. // We hit the ground, stop dropping
  584. m_hCraneTip->m_pSpring->SetSpringConstant( CRANE_SPRING_CONSTANT_INITIAL_RAISING );
  585. m_bDropping = false;
  586. m_flNextDropAllowedTime = gpGlobals->curtime + 3.0;
  587. m_flSlowRaiseTime = gpGlobals->curtime;
  588. m_ServerVehicle.PlaySound( VS_MISC2 );
  589. }
  590. }
  591. else if ( (m_flSlowRaiseTime + CRANE_SLOWRAISE_TIME) > gpGlobals->curtime )
  592. {
  593. float flDelta = (gpGlobals->curtime - m_flSlowRaiseTime);
  594. flDelta = clamp( flDelta, 0, CRANE_SLOWRAISE_TIME );
  595. float flCurrentSpringConstant = RemapVal( flDelta, 0, CRANE_SLOWRAISE_TIME, CRANE_SPRING_CONSTANT_INITIAL_RAISING, CRANE_SPRING_CONSTANT_HANGING );
  596. m_hCraneTip->m_pSpring->SetSpringConstant( flCurrentSpringConstant );
  597. }
  598. // If we've moved in any way, update the tip
  599. if ( m_bDropping || m_flExtensionRate || GetLocalAngularVelocity() != vec3_angle )
  600. {
  601. RecalculateCraneTip();
  602. }
  603. // Make danger sounds underneath the magnet if we have something attached to it
  604. /*
  605. if ( (m_flNextDangerSoundTime < gpGlobals->curtime) && (m_hCraneMagnet->GetTotalMassAttachedObjects() > 0) )
  606. {
  607. // Trace down from the magnet and make a danger sound on the ground
  608. trace_t tr;
  609. Vector vecSource = m_hCraneMagnet->GetAbsOrigin();
  610. UTIL_TraceLine( vecSource, vecSource - Vector(0,0,2048), MASK_SOLID_BRUSHONLY, m_hCraneMagnet, 0, &tr );
  611. if ( tr.fraction < 1.0 )
  612. {
  613. // Make the volume proportional to the amount of mass on the magnet
  614. float flVolume = clamp( (m_hCraneMagnet->GetTotalMassAttachedObjects() * 0.5), 100.f, 600.f );
  615. CSoundEnt::InsertSound( SOUND_DANGER, tr.endpos, flVolume, 0.2, this );
  616. //Msg("Total: %.2f Volume: %.2f\n", m_hCraneMagnet->GetTotalMassAttachedObjects(), flVolume );
  617. //Vector vecVolume = Vector(flVolume,flVolume,flVolume) * 0.5;
  618. //NDebugOverlay::Box( tr.endpos, -vecVolume, vecVolume, 255,0,0, false, 0.3 );
  619. //NDebugOverlay::Cross3D( tr.endpos, -Vector(10,10,10), Vector(10,10,10), 255,0,0, false, 0.3 );
  620. }
  621. m_flNextDangerSoundTime = gpGlobals->curtime + 0.3;
  622. }
  623. */
  624. // Play creak sounds on the magnet if there's heavy weight on it
  625. if ( (m_flNextCreakSound < gpGlobals->curtime) && (m_hCraneMagnet->GetTotalMassAttachedObjects() > 100) )
  626. {
  627. // Randomly play creaks from the magnet, and increase the chance based on the turning speed
  628. float flSpeedPercentage = clamp( fabs(m_flTurn) / m_flMaxTurnSpeed, 0, 1 );
  629. if ( RandomFloat(0,1) > (0.95 - (0.1 * flSpeedPercentage)) )
  630. {
  631. if ( m_ServerVehicle.m_vehicleSounds.iszSound[VS_MISC4] != NULL_STRING )
  632. {
  633. CPASAttenuationFilter filter( m_hCraneMagnet );
  634. EmitSound_t ep;
  635. ep.m_nChannel = CHAN_VOICE;
  636. ep.m_pSoundName = STRING(m_ServerVehicle.m_vehicleSounds.iszSound[VS_MISC4]);
  637. ep.m_flVolume = 1.0f;
  638. ep.m_SoundLevel = SNDLVL_NORM;
  639. CBaseEntity::EmitSound( filter, m_hCraneMagnet->entindex(), ep );
  640. }
  641. m_flNextCreakSound = gpGlobals->curtime + 5.0;
  642. }
  643. }
  644. }
  645. //-----------------------------------------------------------------------------
  646. // Purpose:
  647. //-----------------------------------------------------------------------------
  648. void CPropCrane::TurnMagnetOn( void )
  649. {
  650. if ( !m_hCraneMagnet->IsOn() )
  651. {
  652. variant_t emptyVariant;
  653. m_hCraneMagnet->AcceptInput( "Toggle", this, this, emptyVariant, USE_TOGGLE );
  654. m_ServerVehicle.PlaySound( VS_MISC3 );
  655. m_bMagnetOn = true;
  656. }
  657. }
  658. //-----------------------------------------------------------------------------
  659. // Purpose:
  660. //-----------------------------------------------------------------------------
  661. void CPropCrane::TurnMagnetOff( void )
  662. {
  663. if ( m_hCraneMagnet->IsOn() )
  664. {
  665. variant_t emptyVariant;
  666. m_hCraneMagnet->AcceptInput( "Toggle", this, this, emptyVariant, USE_TOGGLE );
  667. m_ServerVehicle.PlaySound( VS_MISC3 );
  668. m_bMagnetOn = false;
  669. }
  670. }
  671. //-----------------------------------------------------------------------------
  672. // Purpose:
  673. //-----------------------------------------------------------------------------
  674. const Vector &CPropCrane::GetCraneTipPosition( void )
  675. {
  676. return m_hCraneTip->GetAbsOrigin();
  677. }
  678. //-----------------------------------------------------------------------------
  679. // Purpose: Fills out the values with the desired position of the crane's tip
  680. //-----------------------------------------------------------------------------
  681. void CPropCrane::GetCraneTipPosition( Vector *vecOrigin, QAngle *vecAngles )
  682. {
  683. GetAttachment( "cable_tip", *vecOrigin, *vecAngles );
  684. }
  685. //-----------------------------------------------------------------------------
  686. // Purpose: Vehicles are permanently oriented off angle for vphysics.
  687. //-----------------------------------------------------------------------------
  688. void CPropCrane::GetVectors(Vector* pForward, Vector* pRight, Vector* pUp) const
  689. {
  690. // This call is necessary to cause m_rgflCoordinateFrame to be recomputed
  691. const matrix3x4_t &entityToWorld = EntityToWorldTransform();
  692. if (pForward != NULL)
  693. {
  694. MatrixGetColumn( entityToWorld, 1, *pForward );
  695. }
  696. if (pRight != NULL)
  697. {
  698. MatrixGetColumn( entityToWorld, 0, *pRight );
  699. }
  700. if (pUp != NULL)
  701. {
  702. MatrixGetColumn( entityToWorld, 2, *pUp );
  703. }
  704. }
  705. //-----------------------------------------------------------------------------
  706. // Purpose:
  707. //-----------------------------------------------------------------------------
  708. CBaseEntity *CPropCrane::GetDriver( void )
  709. {
  710. if ( m_hNPCDriver )
  711. return m_hNPCDriver;
  712. return m_hPlayer;
  713. }
  714. //-----------------------------------------------------------------------------
  715. // Purpose: Prevent the player from entering / exiting the vehicle
  716. //-----------------------------------------------------------------------------
  717. void CPropCrane::InputLock( inputdata_t &inputdata )
  718. {
  719. m_bLocked = true;
  720. }
  721. //-----------------------------------------------------------------------------
  722. // Purpose: Allow the player to enter / exit the vehicle
  723. //-----------------------------------------------------------------------------
  724. void CPropCrane::InputUnlock( inputdata_t &inputdata )
  725. {
  726. m_bLocked = false;
  727. }
  728. //-----------------------------------------------------------------------------
  729. // Purpose:
  730. // Input : &inputdata -
  731. //-----------------------------------------------------------------------------
  732. void CPropCrane::InputForcePlayerIn( inputdata_t &inputdata )
  733. {
  734. CBasePlayer *pPlayer = UTIL_PlayerByIndex( 1 );
  735. if ( pPlayer && !m_hPlayer )
  736. {
  737. GetServerVehicle()->HandlePassengerEntry( pPlayer, 0 );
  738. }
  739. }
  740. //-----------------------------------------------------------------------------
  741. // Purpose:
  742. //-----------------------------------------------------------------------------
  743. void CPropCrane::SetNPCDriver( CNPC_VehicleDriver *pDriver )
  744. {
  745. m_hNPCDriver = pDriver;
  746. m_nNPCButtons = 0;
  747. if ( pDriver )
  748. {
  749. m_flMaxExtensionSpeed = CRANE_EXTENSION_RATE_MAX * 1.5;
  750. m_flMaxTurnSpeed = CRANE_TURN_RATE_MAX * 1.5;
  751. m_flExtensionAccel = CRANE_EXTENSION_ACCEL * 2;
  752. m_flExtensionDecel = CRANE_EXTENSION_DECEL * 20; // Npcs stop quickly to make them more accurate
  753. m_flTurnAccel = CRANE_TURN_ACCEL * 2;
  754. m_flTurnDecel = CRANE_DECEL * 10; // Npcs stop quickly to make them more accurate
  755. // Set our owner entity to be the NPC, so it can path check without hitting us
  756. SetOwnerEntity( pDriver );
  757. }
  758. else
  759. {
  760. // Restore player crane speeds
  761. InitCraneSpeeds();
  762. SetOwnerEntity( NULL );
  763. // Shutdown the crane's sounds
  764. m_ServerVehicle.SoundShutdown( 1.0 );
  765. }
  766. }
  767. //-----------------------------------------------------------------------------
  768. // Purpose: Allows us to turn off the rumble
  769. //-----------------------------------------------------------------------------
  770. void CPropCrane::PreExitVehicle( CBaseCombatCharacter *pPlayer, int nRole )
  771. {
  772. if ( pPlayer != m_hPlayer )
  773. return;
  774. if ( m_hPlayer != NULL )
  775. {
  776. // Stop rumbles
  777. m_hPlayer->RumbleEffect( RUMBLE_FLAT_BOTH, 0, RUMBLE_FLAG_STOP );
  778. }
  779. }
  780. //========================================================================================================================================
  781. // CRANE VEHICLE SERVER VEHICLE
  782. //========================================================================================================================================
  783. CPropCrane *CCraneServerVehicle::GetCrane( void )
  784. {
  785. return (CPropCrane*)GetDrivableVehicle();
  786. }
  787. //-----------------------------------------------------------------------------
  788. // Purpose:
  789. //-----------------------------------------------------------------------------
  790. void CCraneServerVehicle::GetVehicleViewPosition( int nRole, Vector *pAbsOrigin, QAngle *pAbsAngles, float *pFOV /*= NULL*/ )
  791. {
  792. // FIXME: This needs to be reconciled with the other versions of this function!
  793. Assert( nRole == VEHICLE_ROLE_DRIVER );
  794. CBasePlayer *pPlayer = ToBasePlayer( GetDrivableVehicle()->GetDriver() );
  795. Assert( pPlayer );
  796. *pAbsAngles = pPlayer->EyeAngles(); // yuck. this is an in/out parameter.
  797. float flPitchFactor = 1.0;
  798. matrix3x4_t vehicleEyePosToWorld;
  799. Vector vehicleEyeOrigin;
  800. QAngle vehicleEyeAngles;
  801. GetCrane()->GetAttachment( "vehicle_driver_eyes", vehicleEyeOrigin, vehicleEyeAngles );
  802. AngleMatrix( vehicleEyeAngles, vehicleEyePosToWorld );
  803. // Compute the relative rotation between the unperterbed eye attachment + the eye angles
  804. matrix3x4_t cameraToWorld;
  805. AngleMatrix( *pAbsAngles, cameraToWorld );
  806. matrix3x4_t worldToEyePos;
  807. MatrixInvert( vehicleEyePosToWorld, worldToEyePos );
  808. matrix3x4_t vehicleCameraToEyePos;
  809. ConcatTransforms( worldToEyePos, cameraToWorld, vehicleCameraToEyePos );
  810. // Now perterb the attachment point
  811. vehicleEyeAngles.x = RemapAngleRange( PITCH_CURVE_ZERO * flPitchFactor, PITCH_CURVE_LINEAR, vehicleEyeAngles.x );
  812. vehicleEyeAngles.z = RemapAngleRange( ROLL_CURVE_ZERO * flPitchFactor, ROLL_CURVE_LINEAR, vehicleEyeAngles.z );
  813. AngleMatrix( vehicleEyeAngles, vehicleEyeOrigin, vehicleEyePosToWorld );
  814. // Now treat the relative eye angles as being relative to this new, perterbed view position...
  815. matrix3x4_t newCameraToWorld;
  816. ConcatTransforms( vehicleEyePosToWorld, vehicleCameraToEyePos, newCameraToWorld );
  817. // output new view abs angles
  818. MatrixAngles( newCameraToWorld, *pAbsAngles );
  819. // UNDONE: *pOrigin would already be correct in single player if the HandleView() on the server ran after vphysics
  820. MatrixGetColumn( newCameraToWorld, 3, *pAbsOrigin );
  821. }
  822. //-----------------------------------------------------------------------------
  823. // Purpose:
  824. //-----------------------------------------------------------------------------
  825. void CCraneServerVehicle::NPC_SetDriver( CNPC_VehicleDriver *pDriver )
  826. {
  827. GetCrane()->SetNPCDriver( pDriver );
  828. if ( pDriver )
  829. {
  830. SetVehicleVolume( 1.0 ); // Vehicles driven by NPCs are louder
  831. GetCrane()->SetSimulatedEveryTick( false );
  832. }
  833. else
  834. {
  835. SetVehicleVolume( 0.5 );
  836. GetCrane()->SetSimulatedEveryTick( true );
  837. }
  838. }
  839. //-----------------------------------------------------------------------------
  840. // Purpose:
  841. //-----------------------------------------------------------------------------
  842. void CCraneServerVehicle::NPC_DriveVehicle( void )
  843. {
  844. if ( g_debug_vehicledriver.GetInt() )
  845. {
  846. if ( m_nNPCButtons )
  847. {
  848. Vector vecForward, vecRight;
  849. GetCrane()->GetVectors( &vecForward, &vecRight, NULL );
  850. if ( m_nNPCButtons & IN_FORWARD )
  851. {
  852. NDebugOverlay::Line( GetCrane()->GetAbsOrigin(), GetCrane()->GetAbsOrigin() + vecForward * 200, 0,255,0, true, 0.1 );
  853. }
  854. if ( m_nNPCButtons & IN_BACK )
  855. {
  856. NDebugOverlay::Line( GetCrane()->GetAbsOrigin(), GetCrane()->GetAbsOrigin() - vecForward * 200, 0,255,0, true, 0.1 );
  857. }
  858. if ( m_nNPCButtons & IN_MOVELEFT )
  859. {
  860. NDebugOverlay::Line( GetCrane()->GetAbsOrigin(), GetCrane()->GetAbsOrigin() - vecRight * 200, 0,255,0, true, 0.1 );
  861. }
  862. if ( m_nNPCButtons & IN_MOVERIGHT )
  863. {
  864. NDebugOverlay::Line( GetCrane()->GetAbsOrigin(), GetCrane()->GetAbsOrigin() + vecRight * 200, 0,255,0, true, 0.1 );
  865. }
  866. if ( m_nNPCButtons & IN_JUMP )
  867. {
  868. NDebugOverlay::Box( GetCrane()->GetAbsOrigin(), -Vector(20,20,20), Vector(20,20,20), 0,255,0, true, 0.1 );
  869. }
  870. }
  871. }
  872. GetCrane()->DriveCrane( m_nNPCButtons, m_nNPCButtons, m_flTurnDegrees );
  873. // Clear out attack buttons each frame
  874. m_nNPCButtons &= ~IN_ATTACK;
  875. m_nNPCButtons &= ~IN_ATTACK2;
  876. // Run the crane's movement
  877. GetCrane()->RunCraneMovement( 0.1 );
  878. }
  879. //===============================================================================================================================
  880. // CRANE CABLE TIP
  881. //===============================================================================================================================
  882. //-----------------------------------------------------------------------------
  883. // Purpose: To by usable by the constraint system, this needs to have a phys model.
  884. //-----------------------------------------------------------------------------
  885. void CCraneTip::Spawn( void )
  886. {
  887. Precache();
  888. SetModel( "models/props_junk/cardboard_box001a.mdl" );
  889. AddEffects( EF_NODRAW );
  890. // We don't want this to be solid, because we don't want it to collide with the hydra.
  891. SetSolid( SOLID_VPHYSICS );
  892. AddSolidFlags( FSOLID_NOT_SOLID );
  893. VPhysicsInitShadow( false, false );
  894. // Disable movement on this sucker, we're going to move him manually
  895. SetMoveType( MOVETYPE_NONE );
  896. BaseClass::Spawn();
  897. m_pSpring = NULL;
  898. }
  899. //-----------------------------------------------------------------------------
  900. // Purpose:
  901. //-----------------------------------------------------------------------------
  902. void CCraneTip::Precache( void )
  903. {
  904. PrecacheModel( "models/props_junk/cardboard_box001a.mdl" );
  905. BaseClass::Precache();
  906. }
  907. //-----------------------------------------------------------------------------
  908. // Purpose: Activate/create the constraint
  909. //-----------------------------------------------------------------------------
  910. bool CCraneTip::CreateConstraint( CBaseAnimating *pCraneMagnet, IPhysicsConstraintGroup *pGroup )
  911. {
  912. IPhysicsObject *pPhysObject = VPhysicsGetObject();
  913. IPhysicsObject *pCraneMagnetPhysObject = pCraneMagnet->VPhysicsGetObject();
  914. if ( !pCraneMagnetPhysObject )
  915. {
  916. Msg(" Error: Tried to create a crane_tip with a crane magnet that has no physics model.\n" );
  917. return false;
  918. }
  919. Assert( pPhysObject );
  920. // Check to see if it's got an attachment point to connect to
  921. Vector vecPoint = pCraneMagnet->GetAbsOrigin();
  922. int iIndex = pCraneMagnet->LookupAttachment("magnetcable_a");
  923. if ( iIndex >= 0 )
  924. {
  925. pCraneMagnet->GetAttachment( iIndex, vecPoint );
  926. }
  927. // Create our spring
  928. /*
  929. constraint_lengthparams_t length;
  930. length.Defaults();
  931. length.InitWorldspace( pPhysObject, pCraneMagnetPhysObject, GetAbsOrigin(), vecPoint );
  932. length.constraint.Defaults();
  933. m_pConstraint = physenv->CreateLengthConstraint( pPhysObject, pCraneMagnetPhysObject, pGroup, length );
  934. */
  935. springparams_t spring;
  936. spring.constant = CRANE_SPRING_CONSTANT_HANGING;
  937. spring.damping = CRANE_SPRING_DAMPING;
  938. spring.naturalLength = (GetAbsOrigin() - vecPoint).Length();
  939. spring.relativeDamping = CRANE_SPRING_RELATIVE_DAMPING;
  940. spring.startPosition = GetAbsOrigin();
  941. spring.endPosition = vecPoint;
  942. spring.useLocalPositions = false;
  943. spring.onlyStretch = true;
  944. m_pSpring = physenv->CreateSpring( pPhysObject, pCraneMagnetPhysObject, &spring );
  945. return true;
  946. }
  947. //-----------------------------------------------------------------------------
  948. // Purpose: Create a Hydra Impale between the hydra and the entity passed in
  949. //-----------------------------------------------------------------------------
  950. CCraneTip *CCraneTip::Create( CBaseAnimating *pCraneMagnet, IPhysicsConstraintGroup *pGroup, const Vector &vecOrigin, const QAngle &vecAngles )
  951. {
  952. CCraneTip *pCraneTip = (CCraneTip *)CBaseEntity::Create( "crane_tip", vecOrigin, vecAngles );
  953. if ( !pCraneTip )
  954. return NULL;
  955. if ( !pCraneTip->CreateConstraint( pCraneMagnet, pGroup ) )
  956. return NULL;
  957. return pCraneTip;
  958. }