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.

3558 lines
110 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================
  7. #include "cbase.h"
  8. #include "gamemovement.h"
  9. #include "tf_gamerules.h"
  10. #include "tf_shareddefs.h"
  11. #include "in_buttons.h"
  12. #include "movevars_shared.h"
  13. #include "collisionutils.h"
  14. #include "debugoverlay_shared.h"
  15. #include "baseobject_shared.h"
  16. #include "particle_parse.h"
  17. #include "baseobject_shared.h"
  18. #include "coordsize.h"
  19. #include "tf_weapon_medigun.h"
  20. #include "tf_wearable_item_demoshield.h"
  21. #include "takedamageinfo.h"
  22. #include "tf_weapon_buff_item.h"
  23. #include "halloween/tf_weapon_spellbook.h"
  24. #ifdef CLIENT_DLL
  25. #include "c_tf_player.h"
  26. #include "c_world.h"
  27. #include "c_team.h"
  28. #include "prediction.h"
  29. #define CTeam C_Team
  30. #else
  31. #include "tf_player.h"
  32. #include "team.h"
  33. #include "bot/tf_bot.h"
  34. #include "tf_fx.h"
  35. #endif
  36. ConVar tf_duck_debug_spew( "tf_duck_debug_spew", "0", FCVAR_REPLICATED | FCVAR_DEVELOPMENTONLY );
  37. ConVar tf_showspeed( "tf_showspeed", "0", FCVAR_REPLICATED | FCVAR_DEVELOPMENTONLY );
  38. ConVar tf_avoidteammates( "tf_avoidteammates", "1", FCVAR_REPLICATED | FCVAR_CHEAT | FCVAR_DEVELOPMENTONLY, "Controls how teammates interact when colliding.\n 0: Teammates block each other\n 1: Teammates pass through each other, but push each other away (default)" );
  39. ConVar tf_avoidteammates_pushaway( "tf_avoidteammates_pushaway", "1", FCVAR_REPLICATED, "Whether or not teammates push each other away when occupying the same space" );
  40. ConVar tf_solidobjects( "tf_solidobjects", "1", FCVAR_REPLICATED | FCVAR_CHEAT | FCVAR_DEVELOPMENTONLY );
  41. ConVar tf_clamp_back_speed( "tf_clamp_back_speed", "0.9", FCVAR_REPLICATED | FCVAR_DEVELOPMENTONLY );
  42. ConVar tf_clamp_back_speed_min( "tf_clamp_back_speed_min", "100", FCVAR_REPLICATED | FCVAR_DEVELOPMENTONLY );
  43. ConVar tf_clamp_airducks( "tf_clamp_airducks", "1", FCVAR_REPLICATED );
  44. ConVar tf_resolve_stuck_players( "tf_resolve_stuck_players", "1", FCVAR_REPLICATED );
  45. ConVar tf_scout_hype_mod( "tf_scout_hype_mod", "55", FCVAR_REPLICATED | FCVAR_CHEAT | FCVAR_DEVELOPMENTONLY );
  46. ConVar tf_max_charge_speed( "tf_max_charge_speed", "750", FCVAR_NOTIFY | FCVAR_REPLICATED | FCVAR_CHEAT | FCVAR_DEVELOPMENTONLY );
  47. ConVar tf_parachute_gravity( "tf_parachute_gravity", "0.2f", FCVAR_DEVELOPMENTONLY | FCVAR_REPLICATED, "Gravity while parachute is deployed" );
  48. ConVar tf_parachute_maxspeed_xy( "tf_parachute_maxspeed_xy", "400.0f", FCVAR_DEVELOPMENTONLY | FCVAR_REPLICATED, "Max XY Speed while Parachute is deployed" );
  49. ConVar tf_parachute_maxspeed_z( "tf_parachute_maxspeed_z", "-100.0f", FCVAR_DEVELOPMENTONLY | FCVAR_REPLICATED, "Max Z Speed while Parachute is deployed" );
  50. ConVar tf_parachute_maxspeed_onfire_z( "tf_parachute_maxspeed_onfire_z", "10.0f", FCVAR_DEVELOPMENTONLY | FCVAR_REPLICATED, "Max Z Speed when on Fire and Parachute is deployed" );
  51. ConVar tf_parachute_aircontrol( "tf_parachute_aircontrol", "2.5f", FCVAR_DEVELOPMENTONLY | FCVAR_REPLICATED, "Multiplier for how much air control players have when Parachute is deployed" );
  52. ConVar tf_halloween_kart_aircontrol( "tf_halloween_kart_aircontrol", "1.2f", FCVAR_CHEAT | FCVAR_REPLICATED, "Multiplier for how much air control players have when in Kart Mode" );
  53. ConVar tf_ghost_up_speed( "tf_ghost_up_speed", "300.f", FCVAR_CHEAT | FCVAR_REPLICATED, "Speed that ghost go upward while holding jump key" );
  54. ConVar tf_ghost_xy_speed( "tf_ghost_xy_speed", "300.f", FCVAR_CHEAT | FCVAR_REPLICATED );
  55. ConVar tf_grapplinghook_move_speed( "tf_grapplinghook_move_speed", "750", FCVAR_REPLICATED | FCVAR_CHEAT );
  56. ConVar tf_grapplinghook_use_acceleration( "tf_grapplinghook_use_acceleration", "0", FCVAR_REPLICATED, "Use full acceleration calculation for grappling hook movement" );
  57. ConVar tf_grapplinghook_acceleration( "tf_grapplinghook_acceleration", "3500", FCVAR_REPLICATED | FCVAR_CHEAT );
  58. ConVar tf_grapplinghook_dampening( "tf_grapplinghook_dampening", "500", FCVAR_REPLICATED | FCVAR_CHEAT );
  59. ConVar tf_grapplinghook_follow_distance( "tf_grapplinghook_follow_distance", "64", FCVAR_REPLICATED | FCVAR_CHEAT );
  60. ConVar tf_grapplinghook_jump_up_speed( "tf_grapplinghook_jump_up_speed", "375", FCVAR_REPLICATED | FCVAR_CHEAT );
  61. ConVar tf_grapplinghook_prevent_fall_damage( "tf_grapplinghook_prevent_fall_damage", "0", FCVAR_REPLICATED | FCVAR_CHEAT );
  62. ConVar tf_grapplinghook_medic_latch_speed_scale( "tf_grapplinghook_medic_latch_speed_scale", "0.65", FCVAR_REPLICATED | FCVAR_CHEAT );
  63. #ifdef STAGING_ONLY
  64. ConVar tf_movement_doubletap_window( "tf_movement_doubletap_window", "0.1f", FCVAR_REPLICATED | FCVAR_CHEAT );
  65. ConVar tf_space_gravity_jump_multipler( "tf_space_gravity_jump_multipler", "1.05", FCVAR_CHEAT | FCVAR_REPLICATED, "Multiplier for player jump velocity in space" );
  66. ConVar tf_space_aircontrol( "tf_space_aircontrol", "1.0", FCVAR_CHEAT | FCVAR_REPLICATED, "Multiplier for how much air control players have in space" );
  67. ConVar tf_taunt_move_speed( "tf_taunt_move_speed", "0", FCVAR_REPLICATED | FCVAR_CHEAT );
  68. #endif // STAGING_ONLY
  69. extern ConVar cl_forwardspeed;
  70. extern ConVar cl_backspeed;
  71. extern ConVar cl_sidespeed;
  72. extern ConVar mp_tournament_readymode_countdown;
  73. #define TF_MAX_SPEED (400 * 1.3) // 400 is Scout max speed, and we allow up to 3% movement bonus.
  74. #define TF_WATERJUMP_FORWARD 30
  75. #define TF_WATERJUMP_UP 300
  76. #define TF_TIME_TO_DUCK 0.3f
  77. #define TF_AIRDUCKED_COUNT 2
  78. //ConVar tf_waterjump_up( "tf_waterjump_up", "300", FCVAR_REPLICATED | FCVAR_CHEAT | FCVAR_DEVELOPMENTONLY );
  79. //ConVar tf_waterjump_forward( "tf_waterjump_forward", "30", FCVAR_REPLICATED | FCVAR_CHEAT | FCVAR_DEVELOPMENTONLY );
  80. #define NUM_CROUCH_HINTS 3
  81. class CTFGameMovement : public CGameMovement
  82. {
  83. public:
  84. DECLARE_CLASS( CTFGameMovement, CGameMovement );
  85. CTFGameMovement();
  86. virtual void PlayerMove();
  87. virtual unsigned int PlayerSolidMask( bool brushOnly = false );
  88. virtual void ProcessMovement( CBasePlayer *pBasePlayer, CMoveData *pMove );
  89. virtual bool CanAccelerate();
  90. virtual bool CheckJumpButton();
  91. virtual int CheckStuck( void );
  92. virtual bool CheckWater( void );
  93. virtual void WaterMove( void );
  94. virtual void FullWalkMove();
  95. virtual void WalkMove( void );
  96. virtual void AirMove( void );
  97. virtual void FullTossMove( void );
  98. virtual void CategorizePosition( void );
  99. virtual void CheckFalling( void );
  100. virtual void Duck( void );
  101. virtual Vector GetPlayerViewOffset( bool ducked ) const;
  102. bool GrapplingHookMove( void );
  103. bool ChargeMove( void );
  104. bool StunMove( void );
  105. bool TauntMove( void );
  106. void VehicleMove( void );
  107. bool HighMaxSpeedMove( void );
  108. virtual float GetAirSpeedCap( void );
  109. virtual void TracePlayerBBox( const Vector& start, const Vector& end, unsigned int fMask, int collisionGroup, trace_t& pm );
  110. virtual CBaseHandle TestPlayerPosition( const Vector& pos, int collisionGroup, trace_t& pm );
  111. virtual void StepMove( Vector &vecDestination, trace_t &trace );
  112. virtual bool GameHasLadders() const;
  113. virtual void SetGroundEntity( trace_t *pm );
  114. virtual void PlayerRoughLandingEffects( float fvol );
  115. virtual void HandleDuckingSpeedCrop( void );
  116. protected:
  117. virtual void CheckWaterJump( void );
  118. void FullWalkMoveUnderwater();
  119. private:
  120. bool CheckWaterJumpButton( void );
  121. void AirDash( void );
  122. void PreventBunnyJumping();
  123. void ToggleParachute( void );
  124. void CheckKartWallBumping();
  125. // Ducking.
  126. #if 0
  127. // New duck tests!
  128. void HandleDuck( int nButtonsPressed );
  129. void HandleUnDuck( int nButtonsReleased );
  130. void TestDuck();
  131. #endif
  132. void DuckOverrides();
  133. void OnDuck( int nButtonsPressed );
  134. void OnUnDuck( int nButtonsReleased );
  135. #ifdef STAGING_ONLY
  136. void CheckForDoubleTap( void );
  137. void OnDoubleTapped( int nKey );
  138. void TeleportMove( Vector &vecDirection, float flDist );
  139. CUtlMap< int, float > m_MoveKeyDownTimes;
  140. float m_flNextDoubleTapTeleportTime;
  141. #endif // STAGING_ONLY
  142. private:
  143. Vector m_vecWaterPoint;
  144. CTFPlayer *m_pTFPlayer;
  145. bool m_isPassingThroughEnemies;
  146. static float CalcWishSpeedThreshold()
  147. {
  148. return 100.0f * sv_friction.GetFloat() / (sv_accelerate.GetFloat());
  149. }
  150. };
  151. // Expose our interface.
  152. static CTFGameMovement g_GameMovement;
  153. IGameMovement *g_pGameMovement = ( IGameMovement * )&g_GameMovement;
  154. EXPOSE_SINGLE_INTERFACE_GLOBALVAR(CGameMovement, IGameMovement,INTERFACENAME_GAMEMOVEMENT, g_GameMovement );
  155. // ---------------------------------------------------------------------------------------- //
  156. // CTFGameMovement.
  157. // ---------------------------------------------------------------------------------------- //
  158. CTFGameMovement::CTFGameMovement()
  159. {
  160. m_pTFPlayer = NULL;
  161. m_isPassingThroughEnemies = false;
  162. #ifdef STAGING_ONLY
  163. m_MoveKeyDownTimes.SetLessFunc( DefLessFunc (int) );
  164. m_flNextDoubleTapTeleportTime = 0.f;
  165. #endif // STAGING_ONLY
  166. }
  167. //----------------------------------------------------------------------------------------
  168. // Purpose: moves the player
  169. //----------------------------------------------------------------------------------------
  170. void CTFGameMovement::PlayerMove()
  171. {
  172. // call base class to do movement
  173. BaseClass::PlayerMove();
  174. // handle player's interaction with water
  175. int nNewWaterLevel = m_pTFPlayer->GetWaterLevel();
  176. if ( m_nOldWaterLevel != nNewWaterLevel )
  177. {
  178. if ( WL_NotInWater == m_nOldWaterLevel )
  179. {
  180. // The player has just entered the water. Determine if we should play a splash sound.
  181. bool bPlaySplash = false;
  182. Vector vecVelocity = m_pTFPlayer->GetAbsVelocity();
  183. if ( vecVelocity.z <= -200.0f )
  184. {
  185. // If the player has significant downward velocity, play a splash regardless of water depth. (e.g. Jumping hard into a puddle)
  186. bPlaySplash = true;
  187. }
  188. else
  189. {
  190. // Look at the water depth below the player. If it's significantly deep, play a splash to accompany the sinking that's about to happen.
  191. Vector vecStart = m_pTFPlayer->GetAbsOrigin();
  192. Vector vecEnd = vecStart;
  193. vecEnd.z -= 20; // roughly thigh deep
  194. trace_t tr;
  195. // see if we hit anything solid a little bit below the player
  196. UTIL_TraceLine( vecStart, vecEnd, MASK_SOLID,m_pTFPlayer, COLLISION_GROUP_NONE, &tr );
  197. if ( tr.fraction >= 1.0f )
  198. {
  199. // some amount of water below the player, play a splash
  200. bPlaySplash = true;
  201. }
  202. }
  203. if ( bPlaySplash )
  204. {
  205. m_pTFPlayer->EmitSound( "Physics.WaterSplash" );
  206. }
  207. }
  208. }
  209. // Remove our shield charge if we slow down a bunch.
  210. float flSpeed = VectorLength( mv->m_vecVelocity );
  211. if ( flSpeed < 300.0f )
  212. {
  213. m_pTFPlayer->m_Shared.EndCharge();
  214. }
  215. }
  216. Vector CTFGameMovement::GetPlayerViewOffset( bool ducked ) const
  217. {
  218. return ( ( ducked ) ? ( VEC_DUCK_VIEW_SCALED( m_pTFPlayer ) ) : ( m_pTFPlayer->GetClassEyeHeight() ) );
  219. }
  220. //-----------------------------------------------------------------------------
  221. // Purpose: Allow bots etc to use slightly different solid masks
  222. //-----------------------------------------------------------------------------
  223. unsigned int CTFGameMovement::PlayerSolidMask( bool brushOnly )
  224. {
  225. unsigned int uMask = 0;
  226. // Ghost players dont collide with anything but the world
  227. if ( m_pTFPlayer && m_pTFPlayer->m_Shared.InCond( TF_COND_HALLOWEEN_GHOST_MODE ) )
  228. {
  229. return MASK_PLAYERSOLID_BRUSHONLY;
  230. }
  231. if ( m_pTFPlayer && !m_isPassingThroughEnemies )
  232. {
  233. switch( m_pTFPlayer->GetTeamNumber() )
  234. {
  235. case TF_TEAM_RED:
  236. uMask = CONTENTS_BLUETEAM;
  237. break;
  238. case TF_TEAM_BLUE:
  239. uMask = CONTENTS_REDTEAM;
  240. break;
  241. }
  242. }
  243. return ( uMask | BaseClass::PlayerSolidMask( brushOnly ) );
  244. }
  245. //-----------------------------------------------------------------------------
  246. // Purpose: Overridden to allow players to run faster than the maxspeed
  247. //-----------------------------------------------------------------------------
  248. void CTFGameMovement::ProcessMovement( CBasePlayer *pBasePlayer, CMoveData *pMove )
  249. {
  250. // Verify data.
  251. Assert( pBasePlayer );
  252. Assert( pMove );
  253. if ( !pBasePlayer || !pMove )
  254. return;
  255. // Reset point contents for water check.
  256. ResetGetPointContentsCache();
  257. // Cropping movement speed scales mv->m_fForwardSpeed etc. globally
  258. // Once we crop, we don't want to recursively crop again, so we set the crop
  259. // flag globally here once per usercmd cycle.
  260. m_iSpeedCropped = SPEED_CROPPED_RESET;
  261. // Get the current TF player.
  262. m_pTFPlayer = ToTFPlayer( pBasePlayer );
  263. player = m_pTFPlayer;
  264. mv = pMove;
  265. // The max speed is currently set to the scout - if this changes we need to change this!
  266. mv->m_flMaxSpeed = TF_MAX_SPEED;
  267. // Handle charging demomens
  268. ChargeMove();
  269. // Handle player stun.
  270. StunMove();
  271. // Handle player taunt move
  272. TauntMove();
  273. // Handle grappling hook move
  274. GrapplingHookMove();
  275. // Handle scouts that can move really fast with buffs
  276. HighMaxSpeedMove();
  277. // Run the command.
  278. PlayerMove();
  279. #ifdef STAGING_ONLY
  280. CheckForDoubleTap();
  281. #endif // STAGING_ONLY
  282. FinishMove();
  283. #if defined(GAME_DLL)
  284. m_pTFPlayer->m_bTakenBlastDamageSinceLastMovement = false;
  285. #endif
  286. }
  287. //-----------------------------------------------------------------------------
  288. // Purpose:
  289. //-----------------------------------------------------------------------------
  290. bool CTFGameMovement::GrapplingHookMove()
  291. {
  292. CBaseEntity *pHookTarget = m_pTFPlayer->GetGrapplingHookTarget();
  293. if ( !pHookTarget )
  294. return false;
  295. // check if player can be moved
  296. if ( !m_pTFPlayer->CanPlayerMove() || m_pTFPlayer->m_Shared.IsControlStunned() )
  297. {
  298. mv->m_flForwardMove = 0.f;
  299. mv->m_flSideMove = 0.f;
  300. mv->m_flUpMove = 0.f;
  301. mv->m_nButtons = 0;
  302. m_pTFPlayer->m_nButtons = mv->m_nButtons;
  303. return false;
  304. }
  305. m_pTFPlayer->SetGroundEntity( NULL );
  306. Vector vDesiredMove = pHookTarget->WorldSpaceCenter() - m_pTFPlayer->WorldSpaceCenter();
  307. CTFPlayer *pPlayerToCheckForRune = m_pTFPlayer;
  308. if ( pHookTarget->IsPlayer() )
  309. {
  310. CTFPlayer *pHookedPlayer = ToTFPlayer( pHookTarget );
  311. bool bFollowingAllyGrapple = false;
  312. // If our target is grappling, adjust aim to behind them
  313. CBaseEntity *pHookedPlayerTarget = pHookedPlayer->GetGrapplingHookTarget();
  314. if ( pHookedPlayerTarget )
  315. {
  316. bFollowingAllyGrapple = pHookedPlayer->GetTeamNumber() == m_pTFPlayer->GetTeamNumber();
  317. Vector vTargetGrapple = pHookedPlayerTarget->WorldSpaceCenter() - pHookedPlayer->WorldSpaceCenter();
  318. vTargetGrapple.NormalizeInPlace();
  319. vDesiredMove += vTargetGrapple * ( -1 * tf_grapplinghook_follow_distance.GetFloat() );
  320. }
  321. else
  322. {
  323. // Otherwise, aim short of their center.
  324. vDesiredMove += vDesiredMove.Normalized() * ( -1 * tf_grapplinghook_follow_distance.GetFloat() );
  325. }
  326. if ( bFollowingAllyGrapple )
  327. {
  328. pPlayerToCheckForRune = pHookedPlayer;
  329. }
  330. }
  331. mv->m_flMaxSpeed = tf_grapplinghook_move_speed.GetFloat();
  332. // If we're grappling along with an ally, use their rune to avoid falling behind or passing them
  333. if ( pPlayerToCheckForRune->m_Shared.GetCarryingRuneType() == RUNE_AGILITY )
  334. {
  335. mv->m_flMaxSpeed = 950.f;
  336. }
  337. // Heavies get a grapple speed reduction across the board, even if they have Agility
  338. if ( pPlayerToCheckForRune->GetPlayerClass()->GetClassIndex() == TF_CLASS_HEAVYWEAPONS )
  339. {
  340. mv->m_flMaxSpeed *= 0.70f;
  341. }
  342. // Grapple movement speed penalty if player is carrying the flag and a powerup
  343. else if ( pPlayerToCheckForRune->HasTheFlag() && pPlayerToCheckForRune->m_Shared.GetCarryingRuneType() != RUNE_NONE )
  344. {
  345. if ( pPlayerToCheckForRune->m_Shared.GetCarryingRuneType() == RUNE_AGILITY )
  346. {
  347. mv->m_flMaxSpeed *= 0.8f;
  348. }
  349. else
  350. {
  351. mv->m_flMaxSpeed *= 0.65f;
  352. }
  353. }
  354. // Pyros that are hooked into enemy players travel slower because of their advantage in close quarters
  355. else if ( pPlayerToCheckForRune->GetPlayerClass()->GetClassIndex() == TF_CLASS_PYRO && pPlayerToCheckForRune->m_Shared.InCond( TF_COND_GRAPPLED_TO_PLAYER ) )
  356. {
  357. mv->m_flMaxSpeed *= 0.7f;
  358. }
  359. // if the medic hook latched on to teammate, his movement should be slower to eventually detach from the healing target
  360. // this requires medic to do something instead of getting a free ride (except medic with AGILITY rune)
  361. if ( m_pTFPlayer->GetPlayerClass()->GetClassIndex() == TF_CLASS_MEDIC && pHookTarget->IsPlayer() && pHookTarget->InSameTeam( m_pTFPlayer ) && m_pTFPlayer->m_Shared.GetCarryingRuneType() != RUNE_AGILITY )
  362. {
  363. mv->m_flMaxSpeed *= tf_grapplinghook_medic_latch_speed_scale.GetFloat();
  364. }
  365. if ( tf_grapplinghook_use_acceleration.GetBool() )
  366. {
  367. // Use acceleration with dampening
  368. float flSpeed = mv->m_vecVelocity.Length();
  369. if ( flSpeed > 0.f ) {
  370. float flDampen = Min( tf_grapplinghook_dampening.GetFloat() * gpGlobals->frametime, flSpeed );
  371. mv->m_vecVelocity *= ( flSpeed - flDampen ) / flSpeed;
  372. }
  373. mv->m_vecVelocity += vDesiredMove.Normalized() * ( tf_grapplinghook_acceleration.GetFloat() * gpGlobals->frametime );
  374. flSpeed = mv->m_vecVelocity.Length();
  375. if ( flSpeed > mv->m_flMaxSpeed )
  376. {
  377. mv->m_vecVelocity *= mv->m_flMaxSpeed / flSpeed;
  378. }
  379. }
  380. else
  381. {
  382. // Simple velocity calculation
  383. float vDist = vDesiredMove.Length();
  384. if ( vDist > mv->m_flMaxSpeed * gpGlobals->frametime )
  385. {
  386. mv->m_vecVelocity = vDesiredMove * ( mv->m_flMaxSpeed / vDist );
  387. }
  388. else
  389. {
  390. mv->m_vecVelocity = vDesiredMove / gpGlobals->frametime;
  391. }
  392. }
  393. // slow down when player is close to the hook target to prevent yoyo effect
  394. float flDistSqrToTarget = m_pTFPlayer->GetAbsOrigin().DistToSqr( pHookTarget->GetAbsOrigin() );
  395. if ( flDistSqrToTarget < 10000 )
  396. {
  397. // remap the speed between 80-100 unit distance
  398. mv->m_vecVelocity = mv->m_vecVelocity.Normalized() * RemapValClamped( flDistSqrToTarget, 6400, 10000, 0.f, mv->m_flMaxSpeed );
  399. }
  400. mv->m_flForwardMove = 0.f;
  401. mv->m_flSideMove = 0.f;
  402. mv->m_flUpMove = 0.f;
  403. return true;
  404. }
  405. //-----------------------------------------------------------------------------
  406. // Purpose:
  407. //-----------------------------------------------------------------------------
  408. bool CTFGameMovement::ChargeMove()
  409. {
  410. if ( !m_pTFPlayer->m_Shared.InCond( TF_COND_SHIELD_CHARGE ) )
  411. {
  412. // Check for Quick Fix Medic healing a charging player
  413. if ( !m_pTFPlayer->IsPlayerClass( TF_CLASS_MEDIC ) )
  414. return false;
  415. CTFWeaponBase *pTFWeapon = m_pTFPlayer->GetActiveTFWeapon();
  416. if ( !pTFWeapon )
  417. return false;
  418. if ( pTFWeapon->GetWeaponID() != TF_WEAPON_MEDIGUN )
  419. return false;
  420. CWeaponMedigun *pMedigun = static_cast< CWeaponMedigun* >( pTFWeapon );
  421. if ( !pMedigun || pMedigun->GetMedigunType() != MEDIGUN_QUICKFIX )
  422. return false;
  423. CTFPlayer *pHealTarget = ToTFPlayer( pMedigun->GetHealTarget() );
  424. if ( !pHealTarget || !pHealTarget->m_Shared.InCond( TF_COND_SHIELD_CHARGE ) )
  425. return false;
  426. }
  427. mv->m_flMaxSpeed = tf_max_charge_speed.GetFloat();
  428. int oldbuttons = mv->m_nButtons;
  429. // Handle demoman shield charge.
  430. mv->m_flForwardMove = tf_max_charge_speed.GetFloat();
  431. mv->m_flSideMove = 0.0f;
  432. mv->m_flUpMove = 0.0f;
  433. if ( mv->m_nButtons & IN_ATTACK2 )
  434. {
  435. // Allow the player to continue to hold alt-fire.
  436. mv->m_nButtons = IN_ATTACK2;
  437. }
  438. else
  439. {
  440. mv->m_nButtons = 0;
  441. }
  442. if ( oldbuttons & IN_ATTACK )
  443. {
  444. mv->m_nButtons |= IN_ATTACK;
  445. }
  446. return true;
  447. }
  448. //-----------------------------------------------------------------------------
  449. // Purpose:
  450. //-----------------------------------------------------------------------------
  451. bool CTFGameMovement::StunMove()
  452. {
  453. // Handle control stun.
  454. if ( m_pTFPlayer->m_Shared.IsControlStunned()
  455. || m_pTFPlayer->m_Shared.IsLoserStateStunned() )
  456. {
  457. // Can't fire or select weapons.
  458. if ( m_pTFPlayer->IsPlayerClass( TF_CLASS_HEAVYWEAPONS ) )
  459. {
  460. // Heavies can still spin their gun.
  461. if ( mv->m_nButtons & IN_ATTACK2 || mv->m_nButtons & IN_ATTACK )
  462. {
  463. mv->m_nButtons = IN_ATTACK2; // Turn off all other buttons.
  464. }
  465. }
  466. else
  467. {
  468. mv->m_nButtons = 0;
  469. }
  470. if ( m_pTFPlayer->m_Shared.IsControlStunned() )
  471. {
  472. mv->m_flForwardMove = 0.0f;
  473. mv->m_flSideMove = 0.0f;
  474. mv->m_flUpMove = 0.0f;
  475. }
  476. m_pTFPlayer->m_nButtons = mv->m_nButtons;
  477. }
  478. // Handle movement stuns
  479. float flStunAmount = m_pTFPlayer->m_Shared.GetAmountStunned( TF_STUN_MOVEMENT );
  480. // Lerp to the desired amount
  481. if ( flStunAmount )
  482. {
  483. if ( m_pTFPlayer->m_Shared.m_flStunLerpTarget != flStunAmount )
  484. {
  485. m_pTFPlayer->m_Shared.m_flLastMovementStunChange = gpGlobals->curtime;
  486. m_pTFPlayer->m_Shared.m_flStunLerpTarget = flStunAmount;
  487. m_pTFPlayer->m_Shared.m_bStunNeedsFadeOut = true;
  488. }
  489. mv->m_flForwardMove *= 1.f - flStunAmount;
  490. mv->m_flSideMove *= 1.f - flStunAmount;
  491. if ( m_pTFPlayer->m_Shared.GetStunFlags() & TF_STUN_MOVEMENT_FORWARD_ONLY )
  492. {
  493. mv->m_flForwardMove = 0.f;
  494. }
  495. return true;
  496. }
  497. else if ( m_pTFPlayer->m_Shared.m_flLastMovementStunChange )
  498. {
  499. // Lerp out to normal speed
  500. if ( m_pTFPlayer->m_Shared.m_bStunNeedsFadeOut )
  501. {
  502. m_pTFPlayer->m_Shared.m_flLastMovementStunChange = gpGlobals->curtime;
  503. m_pTFPlayer->m_Shared.m_bStunNeedsFadeOut = false;
  504. }
  505. float flCurStun = RemapValClamped( (gpGlobals->curtime - m_pTFPlayer->m_Shared.m_flLastMovementStunChange), 0.2, 0.0, 0.0, 1.0 );
  506. if ( flCurStun )
  507. {
  508. float flRemap = m_pTFPlayer->m_Shared.m_flStunLerpTarget * flCurStun;
  509. mv->m_flForwardMove *= (1.0 - flRemap);
  510. mv->m_flSideMove *= (1.0 - flRemap);
  511. if ( m_pTFPlayer->m_Shared.GetStunFlags() & TF_STUN_MOVEMENT_FORWARD_ONLY )
  512. {
  513. mv->m_flForwardMove = 0.f;
  514. }
  515. }
  516. else
  517. {
  518. m_pTFPlayer->m_Shared.m_flStunLerpTarget = 0.f;
  519. m_pTFPlayer->m_Shared.m_flLastMovementStunChange = 0;
  520. }
  521. return true;
  522. }
  523. // No one can move when in a final countdown transition.
  524. // Do this here to avoid the inevitable hack that prevents players
  525. // from receiving a flag or condition by stalling thinks, etc.
  526. if ( TFGameRules() && TFGameRules()->BInMatchStartCountdown() )
  527. {
  528. mv->m_flForwardMove = 0.f;
  529. mv->m_flSideMove = 0.f;
  530. mv->m_flUpMove = 0.f;
  531. mv->m_nButtons = 0;
  532. m_pTFPlayer->m_nButtons = mv->m_nButtons;
  533. }
  534. return false;
  535. }
  536. //-----------------------------------------------------------------------------
  537. // Purpose:
  538. //-----------------------------------------------------------------------------
  539. bool CTFGameMovement::TauntMove( void )
  540. {
  541. if ( m_pTFPlayer->m_Shared.InCond( TF_COND_HALLOWEEN_KART ) )
  542. {
  543. VehicleMove();
  544. }
  545. else if ( m_pTFPlayer->m_Shared.InCond( TF_COND_TAUNTING ) && m_pTFPlayer->CanMoveDuringTaunt() )
  546. {
  547. m_pTFPlayer->SetTauntYaw( mv->m_vecViewAngles[YAW] );
  548. bool bForceMoveForward = m_pTFPlayer->IsTauntForceMovingForward();
  549. float flMaxMoveSpeed = m_pTFPlayer->GetTauntMoveSpeed();
  550. float flAcceleration = m_pTFPlayer->GetTauntMoveAcceleration();
  551. float flMoveDir = 0.f;
  552. if ( !bForceMoveForward )
  553. {
  554. // Grab analog inputs, normalized to [0,1], to allow controller to also drive taunt movement.
  555. if ( mv->m_flForwardMove > 0 && cl_forwardspeed.GetFloat() > 0 )
  556. {
  557. flMoveDir += mv->m_flForwardMove / cl_forwardspeed.GetFloat();
  558. }
  559. else if ( mv->m_flForwardMove < 0 && cl_backspeed.GetFloat() > 0 )
  560. {
  561. flMoveDir += mv->m_flForwardMove / cl_backspeed.GetFloat();
  562. }
  563. // No need to read buttons explicitly anymore, since that input is already included in m_flForwardMove
  564. /* if ( mv->m_nButtons & IN_FORWARD )
  565. flMoveDir += 1.f;
  566. if ( mv->m_nButtons & IN_BACK )
  567. flMoveDir += -1.f; */
  568. // Clamp to [0,1], just in case.
  569. if ( flMoveDir > 1.0f )
  570. {
  571. flMoveDir = 1.0f;
  572. }
  573. else if ( flMoveDir < -1.0f )
  574. {
  575. flMoveDir = -1.0f;
  576. }
  577. }
  578. else
  579. {
  580. flMoveDir = 1.f;
  581. }
  582. bool bMoving = flMoveDir != 0.f;
  583. float flSign = bMoving ? 1.f : -1.f;
  584. #ifdef STAGING_ONLY
  585. flMaxMoveSpeed = tf_taunt_move_speed.GetFloat() > 0.f ? tf_taunt_move_speed.GetFloat() : flMaxMoveSpeed;
  586. #endif // STAGING_ONLY
  587. if ( flAcceleration > 0.f )
  588. {
  589. m_pTFPlayer->SetCurrentTauntMoveSpeed( clamp( m_pTFPlayer->GetCurrentTauntMoveSpeed() + flSign * ( gpGlobals->frametime / flAcceleration ) * flMaxMoveSpeed, 0.f, flMaxMoveSpeed ) );
  590. }
  591. else
  592. {
  593. m_pTFPlayer->SetCurrentTauntMoveSpeed( flMaxMoveSpeed );
  594. }
  595. // don't allow taunt to move if the player cannot move
  596. if ( !m_pTFPlayer->CanPlayerMove() )
  597. {
  598. flMaxMoveSpeed = 0.f;
  599. }
  600. float flSmoothMoveSpeed = 0.f;
  601. if ( flMaxMoveSpeed > 0.f )
  602. {
  603. flSmoothMoveSpeed = SimpleSpline( m_pTFPlayer->GetCurrentTauntMoveSpeed() / flMaxMoveSpeed ) * flMaxMoveSpeed;
  604. }
  605. mv->m_flMaxSpeed = flMaxMoveSpeed;
  606. mv->m_flForwardMove = flMoveDir * flSmoothMoveSpeed;
  607. mv->m_flClientMaxSpeed = flMaxMoveSpeed;
  608. return true;
  609. }
  610. else
  611. {
  612. m_pTFPlayer->SetCurrentTauntMoveSpeed( 0.f );
  613. }
  614. return false;
  615. }
  616. ConVar tf_halloween_kart_dash_speed( "tf_halloween_kart_dash_speed", "1000", FCVAR_CHEAT | FCVAR_REPLICATED );
  617. ConVar tf_halloween_kart_dash_accel( "tf_halloween_kart_dash_accel", "750", FCVAR_CHEAT | FCVAR_REPLICATED );
  618. ConVar tf_halloween_kart_normal_speed( "tf_halloween_kart_normal_speed", "650", FCVAR_CHEAT | FCVAR_REPLICATED );
  619. ConVar tf_halloween_kart_normal_accel( "tf_halloween_kart_normal_accel", "300", FCVAR_CHEAT | FCVAR_REPLICATED );
  620. ConVar tf_halloween_kart_slowmoving_accel( "tf_halloween_kart_slowmoving_accel", "500", FCVAR_CHEAT | FCVAR_REPLICATED );
  621. ConVar tf_halloween_kart_slowmoving_threshold( "tf_halloween_kart_slowmoving_threshold", "300", FCVAR_CHEAT | FCVAR_REPLICATED );
  622. ConVar tf_halloween_kart_reverse_speed( "tf_halloween_kart_reverse_speed", "-50", FCVAR_CHEAT | FCVAR_REPLICATED );
  623. ConVar tf_halloween_kart_brake_speed( "tf_halloween_kart_brake_speed", "0", FCVAR_CHEAT | FCVAR_REPLICATED );
  624. ConVar tf_halloween_kart_brake_accel( "tf_halloween_kart_brake_accel", "500", FCVAR_CHEAT | FCVAR_REPLICATED );
  625. ConVar tf_halloween_kart_idle_speed( "tf_halloween_kart_idle_speed", "0", FCVAR_CHEAT | FCVAR_REPLICATED );
  626. ConVar tf_halloween_kart_coast_accel( "tf_halloween_kart_coast_accel", "300", FCVAR_CHEAT | FCVAR_REPLICATED );
  627. ConVar tf_halloween_kart_bombhead_scale( "tf_halloween_kart_bombhead_scale", "1.5f", FCVAR_CHEAT | FCVAR_REPLICATED );
  628. void CTFGameMovement::VehicleMove( void )
  629. {
  630. // Reset Flags
  631. m_pTFPlayer->m_iKartState = 0;
  632. m_pTFPlayer->SetTauntYaw( mv->m_vecViewAngles[YAW] );
  633. float flMaxMoveSpeed = tf_halloween_kart_normal_speed.GetFloat();
  634. float flTargetSpeed = tf_halloween_kart_idle_speed.GetFloat();
  635. // Just standard accell by default
  636. float flAcceleration = tf_halloween_kart_coast_accel.GetFloat();
  637. bool bInput = false;
  638. // Hitting the gas
  639. if ( mv->m_flForwardMove > 0.0f )
  640. {
  641. // Grab normalized analog input (no need to check key input explicitly, since it's already baked into m_flForwardMove
  642. float flNormalizedForwardInput = cl_forwardspeed.GetFloat() > 0.0f ? mv->m_flForwardMove / cl_forwardspeed.GetFloat() : 0.0f;
  643. if ( flNormalizedForwardInput > 1.0f )
  644. {
  645. flNormalizedForwardInput = 1.0f;
  646. }
  647. // Target normal speed
  648. flTargetSpeed = tf_halloween_kart_normal_speed.GetFloat();
  649. // Use normal accell speed if it's faster than our current speed
  650. if ( flTargetSpeed > m_pTFPlayer->GetCurrentTauntMoveSpeed() )
  651. {
  652. if ( m_pTFPlayer->GetCurrentTauntMoveSpeed() < tf_halloween_kart_slowmoving_threshold.GetFloat() )
  653. {
  654. flAcceleration = tf_halloween_kart_slowmoving_accel.GetFloat() * flNormalizedForwardInput;
  655. }
  656. else
  657. {
  658. flAcceleration = tf_halloween_kart_normal_accel.GetFloat() * flNormalizedForwardInput;
  659. }
  660. }
  661. bInput = true;
  662. m_pTFPlayer->m_iKartState |= CTFPlayerShared::kKartState_Driving;
  663. }
  664. else if ( mv->m_flForwardMove < 0.0f ) // Hitting the brakes
  665. {
  666. // Grab normalized analog input (no need to check key input explicitly, since it's already baked into m_flForwardMove. And flip the sign, since we're going backwards.
  667. float flNormalizedForwardInput = cl_backspeed.GetFloat() > 0.0f ? mv->m_flForwardMove / cl_backspeed.GetFloat() : 0.0f;
  668. if ( flNormalizedForwardInput < -1.0f )
  669. {
  670. flNormalizedForwardInput = 1.0f;
  671. }
  672. else
  673. {
  674. flNormalizedForwardInput = -flNormalizedForwardInput;
  675. }
  676. // slowing down
  677. if ( m_pTFPlayer->GetCurrentTauntMoveSpeed() > 0 )
  678. {
  679. // Target brake speed
  680. flTargetSpeed = tf_halloween_kart_brake_speed.GetFloat();
  681. // Use brake accell speed if it's slower than our current speed
  682. if ( flTargetSpeed < m_pTFPlayer->GetCurrentTauntMoveSpeed() )
  683. {
  684. flAcceleration = tf_halloween_kart_brake_accel.GetFloat() * flNormalizedForwardInput;
  685. }
  686. m_pTFPlayer->m_iKartState |= CTFPlayerShared::kKartState_Braking;
  687. }
  688. // if we are already stopped, look for new input to start going backwards
  689. else
  690. {
  691. // check for new input, else do nothing
  692. if ( mv->m_flOldForwardMove >= 0.0f || m_pTFPlayer->GetCurrentTauntMoveSpeed() < 0 || m_pTFPlayer->GetVehicleReverseTime() < gpGlobals->curtime )
  693. {
  694. // going backwards, keep going backwards
  695. flTargetSpeed = tf_halloween_kart_reverse_speed.GetFloat();
  696. // Use brake accell speed if it's slower than our current speed
  697. if ( flTargetSpeed < m_pTFPlayer->GetCurrentTauntMoveSpeed() )
  698. {
  699. flAcceleration = tf_halloween_kart_brake_accel.GetFloat() * flNormalizedForwardInput;
  700. }
  701. m_pTFPlayer->m_iKartState |= CTFPlayerShared::kKartState_Reversing;
  702. }
  703. else
  704. {
  705. // Stall for 1 second then start reversing
  706. if ( m_pTFPlayer->GetVehicleReverseTime() == FLT_MAX )
  707. {
  708. m_pTFPlayer->SetVehicleReverseTime( gpGlobals->curtime + 0.6f );
  709. }
  710. m_pTFPlayer->m_iKartState |= CTFPlayerShared::kKartState_Stopped;
  711. }
  712. }
  713. bInput = true;
  714. }
  715. if ( m_pTFPlayer->GetCurrentTauntMoveSpeed() > 0 )
  716. {
  717. m_pTFPlayer->SetVehicleReverseTime( FLT_MAX );
  718. }
  719. // braking?
  720. if ( bInput && Sign( m_pTFPlayer->GetCurrentTauntMoveSpeed() ) != Sign( flTargetSpeed ) )
  721. {
  722. flAcceleration = tf_halloween_kart_brake_accel.GetFloat();
  723. }
  724. if ( m_pTFPlayer->m_Shared.InCond( TF_COND_HALLOWEEN_BOMB_HEAD ) )
  725. {
  726. flMaxMoveSpeed *= tf_halloween_kart_bombhead_scale.GetFloat();
  727. flAcceleration *= tf_halloween_kart_bombhead_scale.GetFloat();
  728. }
  729. float flTargetMoveSpeed = Approach( flTargetSpeed, m_pTFPlayer->GetCurrentTauntMoveSpeed(), flAcceleration * gpGlobals->frametime );
  730. float flSmoothMoveSpeed = Bias( fabs( m_pTFPlayer->GetCurrentTauntMoveSpeed() ) / flMaxMoveSpeed, 0.7f ) * flMaxMoveSpeed * Sign( flTargetMoveSpeed );
  731. // Boost slams the accelerator
  732. if ( m_pTFPlayer->m_Shared.InCond( TF_COND_HALLOWEEN_KART_DASH ) )
  733. {
  734. flTargetSpeed = tf_halloween_kart_dash_speed.GetFloat();
  735. flMaxMoveSpeed = tf_halloween_kart_dash_speed.GetFloat();
  736. flTargetMoveSpeed = flTargetSpeed;
  737. flSmoothMoveSpeed = flTargetSpeed;
  738. flAcceleration = tf_halloween_kart_dash_accel.GetFloat();
  739. }
  740. m_pTFPlayer->SetCurrentTauntMoveSpeed( flTargetMoveSpeed );
  741. float flLeanAccel = flTargetSpeed > flSmoothMoveSpeed ? flAcceleration : flTargetSpeed < flSmoothMoveSpeed ? -flAcceleration : 0.f;
  742. flLeanAccel = Sign( m_pTFPlayer->GetCurrentTauntMoveSpeed() ) != Sign( flTargetSpeed ) ? -flLeanAccel : flLeanAccel;
  743. m_pTFPlayer->m_PlayerAnimState->Vehicle_LeanAccel( flLeanAccel );
  744. #ifdef DEBUG
  745. engine->Con_NPrintf( 0, "Speed: %3.2f", m_pTFPlayer->GetCurrentTauntMoveSpeed() );
  746. engine->Con_NPrintf( 1, "Target: %3.2f", flTargetSpeed );
  747. engine->Con_NPrintf( 2, "Accell: %3.2f", flAcceleration );
  748. #endif
  749. mv->m_flMaxSpeed = flMaxMoveSpeed;
  750. mv->m_flForwardMove = flSmoothMoveSpeed;
  751. mv->m_flClientMaxSpeed = flMaxMoveSpeed;
  752. mv->m_flSideMove = 0.f; // No sideways movement
  753. }
  754. bool CTFGameMovement::HighMaxSpeedMove()
  755. {
  756. if ( fabsf( mv->m_flForwardMove ) < player->MaxSpeed() )
  757. {
  758. if ( AlmostEqual( mv->m_flForwardMove, cl_forwardspeed.GetFloat() ) )
  759. {
  760. mv->m_flForwardMove = player->MaxSpeed();
  761. }
  762. else if ( AlmostEqual( mv->m_flForwardMove, -cl_backspeed.GetFloat() ) )
  763. {
  764. mv->m_flForwardMove = -player->MaxSpeed();
  765. }
  766. }
  767. if ( fabsf( mv->m_flSideMove ) < player->MaxSpeed() )
  768. {
  769. if ( AlmostEqual( mv->m_flSideMove, cl_sidespeed.GetFloat() ) )
  770. {
  771. mv->m_flSideMove = player->MaxSpeed();
  772. }
  773. else if ( AlmostEqual( mv->m_flSideMove, -cl_sidespeed.GetFloat() ) )
  774. {
  775. mv->m_flSideMove = -player->MaxSpeed();
  776. }
  777. }
  778. return true;
  779. }
  780. bool CTFGameMovement::CanAccelerate()
  781. {
  782. // Only allow the player to accelerate when in certain states.
  783. int nCurrentState = m_pTFPlayer->m_Shared.GetState();
  784. if ( nCurrentState == TF_STATE_ACTIVE )
  785. {
  786. return player->GetWaterJumpTime() == 0;
  787. }
  788. else if ( player->IsObserver() )
  789. {
  790. return true;
  791. }
  792. else
  793. {
  794. return false;
  795. }
  796. }
  797. //-----------------------------------------------------------------------------
  798. // Purpose: Check to see if we are in water. If so the jump button acts like a
  799. // swim upward key.
  800. //-----------------------------------------------------------------------------
  801. bool CTFGameMovement::CheckWaterJumpButton( void )
  802. {
  803. // See if we are water jumping. If so, decrement count and return.
  804. if ( player->m_flWaterJumpTime )
  805. {
  806. player->m_flWaterJumpTime -= gpGlobals->frametime;
  807. if (player->m_flWaterJumpTime < 0)
  808. {
  809. player->m_flWaterJumpTime = 0;
  810. }
  811. return false;
  812. }
  813. // In water above our waist.
  814. if ( player->GetWaterLevel() >= 2 || m_pTFPlayer->m_Shared.InCond( TF_COND_SWIMMING_NO_EFFECTS ) )
  815. {
  816. // Swimming, not jumping.
  817. SetGroundEntity( NULL );
  818. int iCannotSwim = 0;
  819. CALL_ATTRIB_HOOK_INT_ON_OTHER( m_pTFPlayer, iCannotSwim, cannot_swim );
  820. if ( iCannotSwim )
  821. {
  822. return false;
  823. }
  824. // We move up a certain amount.
  825. if ( player->GetWaterType() == CONTENTS_WATER || m_pTFPlayer->m_Shared.InCond( TF_COND_SWIMMING_NO_EFFECTS ) )
  826. {
  827. mv->m_vecVelocity[2] = 100;
  828. }
  829. else if ( player->GetWaterType() == CONTENTS_SLIME )
  830. {
  831. mv->m_vecVelocity[2] = 80;
  832. }
  833. // Play swimming sound.
  834. if ( player->m_flSwimSoundTime <= 0 && !m_pTFPlayer->m_Shared.InCond( TF_COND_SWIMMING_NO_EFFECTS ) )
  835. {
  836. // Don't play sound again for 1 second.
  837. player->m_flSwimSoundTime = 1000;
  838. PlaySwimSound();
  839. }
  840. return false;
  841. }
  842. return true;
  843. }
  844. void CTFGameMovement::AirDash( void )
  845. {
  846. // Apply approx. the jump velocity added to an air dash.
  847. Assert( GetCurrentGravity() == 800.0f );
  848. float flJumpMod = 1.f;
  849. // Passive version
  850. CALL_ATTRIB_HOOK_FLOAT_ON_OTHER( m_pTFPlayer, flJumpMod, mod_jump_height );
  851. // Weapon-restricted version
  852. CTFWeaponBase *pWpn = m_pTFPlayer->GetActiveTFWeapon();
  853. if ( pWpn )
  854. {
  855. CALL_ATTRIB_HOOK_FLOAT_ON_OTHER( pWpn, flJumpMod, mod_jump_height_from_weapon );
  856. }
  857. // Lose hype on airdash
  858. int iHypeResetsOnJump = 0;
  859. CALL_ATTRIB_HOOK_INT_ON_OTHER( m_pTFPlayer, iHypeResetsOnJump, hype_resets_on_jump );
  860. if ( iHypeResetsOnJump != 0 )
  861. {
  862. // Loose x hype on jump
  863. float flHype = m_pTFPlayer->m_Shared.GetScoutHypeMeter();
  864. m_pTFPlayer->m_Shared.SetScoutHypeMeter( flHype - iHypeResetsOnJump );
  865. m_pTFPlayer->TeamFortress_SetSpeed();
  866. }
  867. if ( m_pTFPlayer->m_Shared.GetCarryingRuneType() == RUNE_AGILITY )
  868. {
  869. flJumpMod *= 1.8f;
  870. }
  871. float flDashZ = 268.3281572999747f * flJumpMod;
  872. // Get the wish direction.
  873. Vector vecForward, vecRight;
  874. AngleVectors( mv->m_vecViewAngles, &vecForward, &vecRight, NULL );
  875. vecForward.z = 0.0f;
  876. vecRight.z = 0.0f;
  877. VectorNormalize( vecForward );
  878. VectorNormalize( vecRight );
  879. // Copy movement amounts
  880. float flForwardMove = mv->m_flForwardMove;
  881. float flSideMove = mv->m_flSideMove;
  882. // Find the direction,velocity in the x,y plane.
  883. Vector vecWishDirection( ( ( vecForward.x * flForwardMove ) + ( vecRight.x * flSideMove ) ),
  884. ( ( vecForward.y * flForwardMove ) + ( vecRight.y * flSideMove ) ),
  885. 0.0f );
  886. // Update the velocity on the scout.
  887. mv->m_vecVelocity = vecWishDirection;
  888. mv->m_vecVelocity.z += flDashZ;
  889. int iAirDash = m_pTFPlayer->m_Shared.GetAirDash();
  890. if ( iAirDash == 0 )
  891. {
  892. #if defined(GAME_DLL)
  893. // Our first air jump.
  894. m_pTFPlayer->SpeakConceptIfAllowed( MP_CONCEPT_DOUBLE_JUMP, "started_jumping:1" );
  895. #else
  896. IGameEvent *event = gameeventmanager->CreateEvent( "air_dash" );
  897. if ( event )
  898. {
  899. event->SetInt( "player", m_pTFPlayer->GetUserID() );
  900. gameeventmanager->FireEventClientSide( event );
  901. }
  902. #endif
  903. }
  904. else
  905. {
  906. #ifdef GAME_DLL
  907. // Exertion damage from multi-dashing ( atomizer )
  908. if ( !m_pTFPlayer->m_Shared.InCond( TF_COND_HALLOWEEN_SPEED_BOOST ) && !m_pTFPlayer->m_Shared.InCond( TF_COND_SODAPOPPER_HYPE ) )
  909. {
  910. m_pTFPlayer->TakeDamage( CTakeDamageInfo( m_pTFPlayer, m_pTFPlayer, vec3_origin, m_pTFPlayer->WorldSpaceCenter( ), 10.f, DMG_BULLET ) );
  911. }
  912. #endif
  913. }
  914. m_pTFPlayer->m_Shared.SetAirDash( iAirDash+1 );
  915. // Play the gesture.
  916. m_pTFPlayer->DoAnimationEvent( PLAYERANIMEVENT_DOUBLEJUMP );
  917. #ifdef GAME_DLL
  918. // Pitch shift a sound for all airdashes greater then 1
  919. if ( iAirDash > 0 )
  920. {
  921. EmitSound_t params;
  922. params.m_pSoundName = "General.banana_slip";
  923. params.m_flSoundTime = 0;
  924. params.m_pflSoundDuration = 0;
  925. //params.m_bWarnOnDirectWaveReference = true;
  926. CPASFilter filter( m_pTFPlayer->GetAbsOrigin( ) );
  927. params.m_flVolume = 0.1f;
  928. params.m_SoundLevel = SNDLVL_25dB;
  929. params.m_nPitch = RemapVal( iAirDash, 1.0f, 5.0f, 100.f, 120.f );
  930. params.m_nFlags |= ( SND_CHANGE_PITCH | SND_CHANGE_VOL );
  931. m_pTFPlayer->StopSound( "General.banana_slip" );
  932. m_pTFPlayer->EmitSound( filter, m_pTFPlayer->entindex( ), params );
  933. }
  934. #endif // GAME_DLL
  935. }
  936. // Only allow bunny jumping up to 1.2x server / player maxspeed setting
  937. #define BUNNYJUMP_MAX_SPEED_FACTOR 1.2f
  938. void CTFGameMovement::PreventBunnyJumping()
  939. {
  940. if ( m_pTFPlayer->m_Shared.InCond( TF_COND_HALLOWEEN_KART ) )
  941. return;
  942. // Speed at which bunny jumping is limited
  943. float maxscaledspeed = BUNNYJUMP_MAX_SPEED_FACTOR * player->m_flMaxspeed;
  944. if ( maxscaledspeed <= 0.0f )
  945. return;
  946. // Current player speed
  947. float spd = mv->m_vecVelocity.Length();
  948. if ( spd <= maxscaledspeed )
  949. return;
  950. // Apply this cropping fraction to velocity
  951. float fraction = ( maxscaledspeed / spd );
  952. mv->m_vecVelocity *= fraction;
  953. }
  954. void CTFGameMovement::ToggleParachute()
  955. {
  956. if ( (m_pTFPlayer->GetFlags() & FL_ONGROUND) || (mv->m_nOldButtons & IN_JUMP) )
  957. return;
  958. // Can not add if in kart (Kart code does it for spell) but players can manually undeploy
  959. if ( m_pTFPlayer->m_Shared.InCond( TF_COND_HALLOWEEN_KART ) )
  960. {
  961. if ( m_pTFPlayer->m_Shared.InCond( TF_COND_PARACHUTE_DEPLOYED ) )
  962. {
  963. m_pTFPlayer->m_Shared.RemoveCond( TF_COND_PARACHUTE_DEPLOYED );
  964. }
  965. return;
  966. }
  967. // Check for Parachute and deploy / undeploy
  968. int iParachute = 0;
  969. // Passive version
  970. CALL_ATTRIB_HOOK_INT_ON_OTHER( m_pTFPlayer, iParachute, parachute_attribute );
  971. if ( iParachute )
  972. {
  973. // Toggle between the conditions
  974. if ( m_pTFPlayer->m_Shared.InCond( TF_COND_PARACHUTE_DEPLOYED ) )
  975. {
  976. m_pTFPlayer->m_Shared.RemoveCond( TF_COND_PARACHUTE_DEPLOYED );
  977. }
  978. else
  979. {
  980. int iParachuteDisabled = 0;
  981. CALL_ATTRIB_HOOK_INT_ON_OTHER( m_pTFPlayer, iParachuteDisabled, parachute_disabled );
  982. if ( !iParachuteDisabled )
  983. {
  984. m_pTFPlayer->m_Shared.AddCond( TF_COND_PARACHUTE_DEPLOYED );
  985. }
  986. }
  987. }
  988. }
  989. bool CTFGameMovement::CheckJumpButton()
  990. {
  991. // Are we dead? Then we cannot jump.
  992. if ( player->pl.deadflag )
  993. return false;
  994. // Check to see if we are in water.
  995. if ( !CheckWaterJumpButton() )
  996. return false;
  997. if ( m_pTFPlayer->GetGrapplingHookTarget() )
  998. {
  999. float flStartZ = mv->m_vecVelocity[2];
  1000. mv->m_vecVelocity[2] += tf_grapplinghook_jump_up_speed.GetFloat();
  1001. // Heavy gets a jump height reduction across the board, even if he has Agility
  1002. // Powered up flag carriers get the same penalty
  1003. if ( m_pTFPlayer->GetPlayerClass()->GetClassIndex() == TF_CLASS_HEAVYWEAPONS || ( m_pTFPlayer->m_Shared.GetCarryingRuneType() != RUNE_NONE && m_pTFPlayer->HasTheFlag() ) )
  1004. {
  1005. mv->m_vecVelocity[2] *= 0.80f;
  1006. }
  1007. else if ( m_pTFPlayer->m_Shared.GetCarryingRuneType() != RUNE_AGILITY && m_pTFPlayer->m_Shared.GetCarryingRuneType() != RUNE_NONE && m_pTFPlayer->HasTheFlag() )
  1008. {
  1009. mv->m_vecVelocity[2] *= 0.80f;
  1010. }
  1011. if ( mv->m_vecVelocity[2] > GetAirSpeedCap() )
  1012. mv->m_vecVelocity[2] = GetAirSpeedCap();
  1013. // Apply gravity.
  1014. FinishGravity();
  1015. mv->m_outJumpVel.z = mv->m_vecVelocity[2] - flStartZ;
  1016. mv->m_outStepHeight += 0.15f;
  1017. mv->m_nOldButtons |= IN_JUMP;
  1018. return true;
  1019. }
  1020. // holding jump key will make ghost fly
  1021. if ( m_pTFPlayer->m_Shared.InCond( TF_COND_HALLOWEEN_GHOST_MODE ) )
  1022. {
  1023. float flStartZ = mv->m_vecVelocity[2];
  1024. mv->m_vecVelocity[2] = tf_ghost_up_speed.GetFloat();
  1025. // Apply gravity.
  1026. FinishGravity();
  1027. mv->m_outJumpVel.z = mv->m_vecVelocity[2] - flStartZ;
  1028. mv->m_outStepHeight += 0.15f;
  1029. mv->m_nOldButtons |= IN_JUMP;
  1030. return true;
  1031. }
  1032. // Can't jump if our weapon disallows it.
  1033. CTFWeaponBase *pWpn = m_pTFPlayer->GetActiveTFWeapon();
  1034. if ( pWpn && !pWpn->OwnerCanJump() )
  1035. return false;
  1036. // Cannot jump while taunting
  1037. if ( m_pTFPlayer->m_Shared.InCond( TF_COND_TAUNTING ) )
  1038. return false;
  1039. // Check to see if the player is a scout.
  1040. bool bScout = m_pTFPlayer->GetPlayerClass()->IsClass( TF_CLASS_SCOUT );
  1041. bool bAirDash = false;
  1042. bool bOnGround = ( player->GetGroundEntity() != NULL );
  1043. ToggleParachute();
  1044. // Cannot jump will ducked.
  1045. if ( player->GetFlags() & FL_DUCKING )
  1046. {
  1047. // Let a scout do it.
  1048. bool bAllow = ( bScout && !bOnGround );
  1049. if ( !bAllow )
  1050. return false;
  1051. }
  1052. // Cannot jump while in the unduck transition.
  1053. if ( ( player->m_Local.m_bDucking && ( player->GetFlags() & FL_DUCKING ) ) || ( player->m_Local.m_flDuckJumpTime > 0.0f ) )
  1054. return false;
  1055. // Cannot jump again until the jump button has been released.
  1056. if ( mv->m_nOldButtons & IN_JUMP )
  1057. return false;
  1058. // In air, so ignore jumps
  1059. // (unless you are a scout or ghost or parachute
  1060. if ( !bOnGround )
  1061. {
  1062. if ( m_pTFPlayer->CanAirDash() )
  1063. {
  1064. bAirDash = true;
  1065. }
  1066. else
  1067. {
  1068. mv->m_nOldButtons |= IN_JUMP;
  1069. return false;
  1070. }
  1071. }
  1072. // Check for an air dash.
  1073. if ( bAirDash )
  1074. {
  1075. AirDash();
  1076. // Reset air duck for Scouts on AirDash.
  1077. m_pTFPlayer->m_Shared.SetAirDucked( 0 );
  1078. return true;
  1079. }
  1080. PreventBunnyJumping();
  1081. // Start jump animation and player sound (specific TF animation and flags).
  1082. m_pTFPlayer->DoAnimationEvent( PLAYERANIMEVENT_JUMP );
  1083. player->PlayStepSound( (Vector &)mv->GetAbsOrigin(), player->m_pSurfaceData, 1.0, true );
  1084. m_pTFPlayer->m_Shared.SetJumping( true );
  1085. if ( m_pTFPlayer->m_Shared.InCond( TF_COND_HALLOWEEN_KART ) )
  1086. {
  1087. m_pTFPlayer->EmitSound( "BumperCar.Jump" );
  1088. }
  1089. // Set the player as in the air.
  1090. SetGroundEntity( NULL );
  1091. // Check the surface the player is standing on to see if it impacts jumping.
  1092. float flGroundFactor = 1.0f;
  1093. if ( player->m_pSurfaceData )
  1094. {
  1095. flGroundFactor = player->m_pSurfaceData->game.jumpFactor;
  1096. }
  1097. // fMul = sqrt( 2.0 * gravity * jump_height (21.0units) ) * GroundFactor
  1098. Assert( GetCurrentGravity() == 800.0f );
  1099. float flJumpMod = 1.f;
  1100. //if ( m_pTFPlayer->m_Shared.InCond( TF_COND_HALLOWEEN_KART ) )
  1101. //{
  1102. // flJumpMod *= 1.3f;
  1103. //}
  1104. // Passive version
  1105. CALL_ATTRIB_HOOK_FLOAT_ON_OTHER( m_pTFPlayer, flJumpMod, mod_jump_height );
  1106. // Weapon-restricted version
  1107. if ( pWpn )
  1108. {
  1109. CALL_ATTRIB_HOOK_FLOAT_ON_OTHER( pWpn, flJumpMod, mod_jump_height_from_weapon );
  1110. }
  1111. /*
  1112. #ifdef STAGING_ONLY
  1113. if ( m_pTFPlayer->m_Shared.InCond( TF_COND_SPACE_GRAVITY ) )
  1114. {
  1115. flJumpMod *= tf_space_gravity_jump_multipler.GetFloat();
  1116. }
  1117. #endif // STAGING_ONLY
  1118. */
  1119. if ( m_pTFPlayer->m_Shared.GetCarryingRuneType() == RUNE_AGILITY )
  1120. {
  1121. flJumpMod *= 1.8f;
  1122. }
  1123. float flMul = ( 289.0f * flJumpMod ) * flGroundFactor;
  1124. // Save the current z velocity.
  1125. float flStartZ = mv->m_vecVelocity[2];
  1126. // Acclerate upward
  1127. if ( ( player->m_Local.m_bDucking ) || ( player->GetFlags() & FL_DUCKING ) )
  1128. {
  1129. // If we are ducking...
  1130. // d = 0.5 * g * t^2 - distance traveled with linear accel
  1131. // t = sqrt(2.0 * 45 / g) - how long to fall 45 units
  1132. // v = g * t - velocity at the end (just invert it to jump up that high)
  1133. // v = g * sqrt(2.0 * 45 / g )
  1134. // v^2 = g * g * 2.0 * 45 / g
  1135. // v = sqrt( g * 2.0 * 45 )
  1136. mv->m_vecVelocity[2] = flMul; // 2 * gravity * jump_height * ground_factor
  1137. }
  1138. else
  1139. {
  1140. mv->m_vecVelocity[2] += flMul; // 2 * gravity * jump_height * ground_factor
  1141. }
  1142. // Apply gravity.
  1143. FinishGravity();
  1144. // Save the output data for the physics system to react to if need be.
  1145. mv->m_outJumpVel.z += mv->m_vecVelocity[2] - flStartZ;
  1146. mv->m_outStepHeight += 0.15f;
  1147. // Flag that we jumped and don't jump again until it is released.
  1148. mv->m_nOldButtons |= IN_JUMP;
  1149. return true;
  1150. }
  1151. //--------------------------------------------------------
  1152. int CTFGameMovement::CheckStuck( void )
  1153. {
  1154. // assume we are not stuck in a player
  1155. m_isPassingThroughEnemies = false;
  1156. if ( tf_resolve_stuck_players.GetBool() )
  1157. {
  1158. const Vector &originalPos = mv->GetAbsOrigin();
  1159. trace_t traceresult;
  1160. TracePlayerBBox( originalPos, originalPos, PlayerSolidMask(), COLLISION_GROUP_PLAYER_MOVEMENT, traceresult );
  1161. #ifdef GAME_DLL
  1162. if ( TFGameRules() && TFGameRules()->IsMannVsMachineMode() && m_pTFPlayer && m_pTFPlayer->GetTeamNumber() == TF_TEAM_PVE_INVADERS )
  1163. {
  1164. if ( traceresult.startsolid )
  1165. {
  1166. if ( m_pTFPlayer->m_playerMovementStuckTimer.HasStarted() && m_pTFPlayer->m_playerMovementStuckTimer.IsElapsed() )
  1167. {
  1168. DevMsg( "%3.2f: A robot is interpenetrating a solid - killed!\n", gpGlobals->curtime );
  1169. UTIL_LogPrintf( "\"%s<%i><%s><%s>\" startsolid killed (position \"%3.2f %3.2f %3.2f\")\n",
  1170. m_pTFPlayer->GetPlayerName(),
  1171. m_pTFPlayer->GetUserID(),
  1172. m_pTFPlayer->GetNetworkIDString(),
  1173. m_pTFPlayer->GetTeam()->GetName(),
  1174. m_pTFPlayer->GetAbsOrigin().x, m_pTFPlayer->GetAbsOrigin().y, m_pTFPlayer->GetAbsOrigin().z );
  1175. m_pTFPlayer->TakeDamage( CTakeDamageInfo( m_pTFPlayer, m_pTFPlayer, vec3_origin, m_pTFPlayer->WorldSpaceCenter(), 999999.9f, DMG_CRUSH ) );
  1176. }
  1177. else
  1178. {
  1179. if ( traceresult.m_pEnt )
  1180. {
  1181. Warning( "Robot's getting stuck with %s\n", traceresult.m_pEnt->GetClassname() );
  1182. }
  1183. }
  1184. }
  1185. else
  1186. {
  1187. // Bot is *not* stuck right now. Continually restart timer, so if we become stuck it will count down and expire.
  1188. const float stuckTooLongTime = 10.0f;
  1189. m_pTFPlayer->m_playerMovementStuckTimer.Start( stuckTooLongTime );
  1190. }
  1191. }
  1192. #endif
  1193. if ( traceresult.startsolid && traceresult.DidHitNonWorldEntity() )
  1194. {
  1195. if ( traceresult.m_pEnt->IsPlayer() )
  1196. {
  1197. // We are stuck in an enemy player. Don't collide with enemies until we are no longer penetrating them.
  1198. m_isPassingThroughEnemies = true;
  1199. // verify position is now clear
  1200. TracePlayerBBox( originalPos, originalPos, PlayerSolidMask(), COLLISION_GROUP_PLAYER_MOVEMENT, traceresult );
  1201. if ( !traceresult.DidHit() )
  1202. {
  1203. // no longer stuck
  1204. DevMsg( "%3.2f: Resolved stuck player/player\n", gpGlobals->curtime );
  1205. return 0;
  1206. }
  1207. }
  1208. else if ( fabs( traceresult.m_pEnt->GetAbsVelocity().z ) > 0.7071f && FClassnameIs( traceresult.m_pEnt, "func_tracktrain" ) )
  1209. {
  1210. // we're stuck in a vertically moving tracktrain, assume flat surface normal and move us out
  1211. SetGroundEntity( &traceresult );
  1212. // we're stuck in a vertically moving tracktrain, snap on top of it
  1213. const float maxAdjust = 80.0f;
  1214. const float step = 10.0f;
  1215. Vector tryPos;
  1216. for( float shift = step; shift < maxAdjust; shift += step )
  1217. {
  1218. tryPos = mv->GetAbsOrigin();
  1219. tryPos.z += shift;
  1220. TracePlayerBBox( tryPos, tryPos, PlayerSolidMask(), COLLISION_GROUP_PLAYER_MOVEMENT, traceresult );
  1221. if ( !traceresult.DidHit() || ( traceresult.m_pEnt && traceresult.m_pEnt->IsPlayer() ) )
  1222. {
  1223. // no longer stuck
  1224. mv->SetAbsOrigin( tryPos );
  1225. DevMsg( "%3.2f: Forced stuck player to top of func_tracktrain\n", gpGlobals->curtime );
  1226. return 0;
  1227. }
  1228. }
  1229. }
  1230. }
  1231. }
  1232. return BaseClass::CheckStuck();
  1233. }
  1234. bool CTFGameMovement::CheckWater( void )
  1235. {
  1236. Vector vecPlayerMin = GetPlayerMins();
  1237. Vector vecPlayerMax = GetPlayerMaxs();
  1238. Vector vecPoint( ( mv->GetAbsOrigin().x + ( vecPlayerMin.x + vecPlayerMax.x ) * 0.5f ),
  1239. ( mv->GetAbsOrigin().y + ( vecPlayerMin.y + vecPlayerMax.y ) * 0.5f ),
  1240. ( mv->GetAbsOrigin().z + vecPlayerMin.z + 1 ) );
  1241. // Assume that we are not in water at all.
  1242. int wl = WL_NotInWater;
  1243. int wt = CONTENTS_EMPTY;
  1244. // Check to see if our feet are underwater.
  1245. int nContents = GetPointContentsCached( vecPoint, 0 );
  1246. if ( nContents & MASK_WATER )
  1247. {
  1248. // Clear our jump flag, because we have landed in water.
  1249. m_pTFPlayer->m_Shared.SetJumping( false );
  1250. // Set water type and level.
  1251. wt = nContents;
  1252. wl = WL_Feet;
  1253. float flWaistZ = mv->GetAbsOrigin().z + ( vecPlayerMin.z + vecPlayerMax.z ) * 0.5f + 12.0f;
  1254. // Now check eyes
  1255. vecPoint.z = mv->GetAbsOrigin().z + player->GetViewOffset()[2];
  1256. nContents = GetPointContentsCached( vecPoint, 1 );
  1257. if ( nContents & MASK_WATER )
  1258. {
  1259. // In over our eyes
  1260. wl = WL_Eyes;
  1261. VectorCopy( vecPoint, m_vecWaterPoint );
  1262. m_vecWaterPoint.z = flWaistZ;
  1263. }
  1264. else
  1265. {
  1266. // Now check a point that is at the player hull midpoint (waist) and see if that is underwater.
  1267. vecPoint.z = flWaistZ;
  1268. nContents = GetPointContentsCached( vecPoint, 2 );
  1269. if ( nContents & MASK_WATER )
  1270. {
  1271. // Set the water level at our waist.
  1272. wl = WL_Waist;
  1273. VectorCopy( vecPoint, m_vecWaterPoint );
  1274. }
  1275. }
  1276. }
  1277. // force player to be under water
  1278. if ( m_pTFPlayer->m_Shared.InCond( TF_COND_SWIMMING_CURSE ) )
  1279. {
  1280. wl = WL_Eyes;
  1281. }
  1282. player->SetWaterLevel( wl );
  1283. player->SetWaterType( wt );
  1284. // If we just transitioned from not in water to water, record the time for splashes, etc.
  1285. if ( ( WL_NotInWater == m_nOldWaterLevel ) && ( wl > WL_NotInWater ) )
  1286. {
  1287. m_flWaterEntryTime = gpGlobals->curtime;
  1288. }
  1289. #ifdef GAME_DLL
  1290. else if ( ( WL_NotInWater == wl ) && ( m_nOldWaterLevel > WL_NotInWater ) )
  1291. {
  1292. m_pTFPlayer->SetWaterExitTime( gpGlobals->curtime );
  1293. }
  1294. #endif
  1295. if ( m_nOldWaterLevel != wl )
  1296. {
  1297. m_pTFPlayer->TeamFortress_SetSpeed();
  1298. }
  1299. return ( wl > WL_Feet );
  1300. }
  1301. //-----------------------------------------------------------------------------
  1302. // Purpose:
  1303. //-----------------------------------------------------------------------------
  1304. void CTFGameMovement::WaterMove( void )
  1305. {
  1306. float wishspeed;
  1307. Vector wishdir;
  1308. Vector start, dest;
  1309. Vector temp;
  1310. trace_t pm;
  1311. float speed, newspeed, addspeed, accelspeed;
  1312. // Determine movement angles.
  1313. Vector vecForward, vecRight, vecUp;
  1314. AngleVectors( mv->m_vecViewAngles, &vecForward, &vecRight, &vecUp );
  1315. // Calculate the desired direction and speed.
  1316. Vector vecWishVelocity;
  1317. for ( int iAxis = 0 ; iAxis < 3; ++iAxis )
  1318. {
  1319. vecWishVelocity[iAxis] = ( vecForward[iAxis] * mv->m_flForwardMove ) + ( vecRight[iAxis] * mv->m_flSideMove );
  1320. }
  1321. // if you can't swim just sink instead
  1322. int iCannotSwim = 0;
  1323. CALL_ATTRIB_HOOK_INT_ON_OTHER( m_pTFPlayer, iCannotSwim, cannot_swim );
  1324. if ( iCannotSwim )
  1325. {
  1326. vecWishVelocity[0] *= 0.1;
  1327. vecWishVelocity[1] *= 0.1;
  1328. vecWishVelocity[2] = -60;
  1329. }
  1330. // Check for upward velocity (JUMP).
  1331. else if ( mv->m_nButtons & IN_JUMP )
  1332. {
  1333. if ( player->GetWaterLevel() == WL_Eyes )
  1334. {
  1335. vecWishVelocity[2] += mv->m_flClientMaxSpeed;
  1336. }
  1337. }
  1338. // Sinking if not moving.
  1339. else if ( !mv->m_flForwardMove && !mv->m_flSideMove && !mv->m_flUpMove )
  1340. {
  1341. vecWishVelocity[2] -= 60;
  1342. }
  1343. // Move up based on view angle.
  1344. else
  1345. {
  1346. vecWishVelocity[2] += mv->m_flUpMove;
  1347. }
  1348. // Copy it over and determine speed
  1349. VectorCopy( vecWishVelocity, wishdir );
  1350. wishspeed = VectorNormalize( wishdir );
  1351. // Cap speed.
  1352. if (wishspeed > mv->m_flMaxSpeed)
  1353. {
  1354. VectorScale( vecWishVelocity, mv->m_flMaxSpeed/wishspeed, vecWishVelocity );
  1355. wishspeed = mv->m_flMaxSpeed;
  1356. }
  1357. // Slow us down a bit.
  1358. int iSwimmingMastery = 0;
  1359. CALL_ATTRIB_HOOK_INT_ON_OTHER( m_pTFPlayer, iSwimmingMastery, swimming_mastery );
  1360. if ( iSwimmingMastery == 0 )
  1361. {
  1362. wishspeed *= 0.8;
  1363. }
  1364. // Water friction
  1365. VectorCopy( mv->m_vecVelocity, temp );
  1366. speed = VectorNormalize( temp );
  1367. if ( speed )
  1368. {
  1369. newspeed = speed - gpGlobals->frametime * speed * sv_friction.GetFloat() * player->m_surfaceFriction;
  1370. if ( newspeed < 0.1f )
  1371. {
  1372. newspeed = 0;
  1373. }
  1374. VectorScale (mv->m_vecVelocity, newspeed/speed, mv->m_vecVelocity);
  1375. }
  1376. else
  1377. {
  1378. newspeed = 0;
  1379. }
  1380. // water acceleration
  1381. if ( m_pTFPlayer->m_Shared.InCond( TF_COND_HALLOWEEN_GHOST_MODE ) )
  1382. {
  1383. VectorNormalize(vecWishVelocity);
  1384. accelspeed = sv_accelerate.GetFloat() * wishspeed * gpGlobals->frametime * player->m_surfaceFriction;
  1385. for ( int i = 0; i < 3; i++)
  1386. {
  1387. float deltaSpeed = accelspeed * vecWishVelocity[i];
  1388. mv->m_vecVelocity[i] += deltaSpeed;
  1389. mv->m_outWishVel[i] += deltaSpeed;
  1390. }
  1391. float flGhostXYSpeed = mv->m_vecVelocity.Length2D();
  1392. if ( flGhostXYSpeed > tf_ghost_xy_speed.GetFloat() )
  1393. {
  1394. float flGhostXYSpeedScale = tf_ghost_xy_speed.GetFloat() / flGhostXYSpeed;
  1395. mv->m_vecVelocity.x *= flGhostXYSpeedScale;
  1396. mv->m_vecVelocity.y *= flGhostXYSpeedScale;
  1397. }
  1398. }
  1399. else if (wishspeed >= 0.1f) // old !
  1400. {
  1401. addspeed = wishspeed - newspeed;
  1402. if (addspeed > 0)
  1403. {
  1404. VectorNormalize(vecWishVelocity);
  1405. accelspeed = sv_accelerate.GetFloat() * wishspeed * gpGlobals->frametime * player->m_surfaceFriction;
  1406. if (accelspeed > addspeed)
  1407. {
  1408. accelspeed = addspeed;
  1409. }
  1410. for ( int i = 0; i < 3; i++)
  1411. {
  1412. float deltaSpeed = accelspeed * vecWishVelocity[i];
  1413. mv->m_vecVelocity[i] += deltaSpeed;
  1414. mv->m_outWishVel[i] += deltaSpeed;
  1415. }
  1416. }
  1417. }
  1418. VectorAdd (mv->m_vecVelocity, player->GetBaseVelocity(), mv->m_vecVelocity);
  1419. // Now move
  1420. // assume it is a stair or a slope, so press down from stepheight above
  1421. VectorMA (mv->GetAbsOrigin(), gpGlobals->frametime, mv->m_vecVelocity, dest);
  1422. TracePlayerBBox( mv->GetAbsOrigin(), dest, PlayerSolidMask(), COLLISION_GROUP_PLAYER_MOVEMENT, pm );
  1423. if ( pm.fraction == 1.0f )
  1424. {
  1425. VectorCopy( dest, start );
  1426. if ( player->m_Local.m_bAllowAutoMovement )
  1427. {
  1428. start[2] += player->m_Local.m_flStepSize + 1;
  1429. }
  1430. TracePlayerBBox( start, dest, PlayerSolidMask(), COLLISION_GROUP_PLAYER_MOVEMENT, pm );
  1431. if (!pm.startsolid && !pm.allsolid)
  1432. {
  1433. #if 0
  1434. float stepDist = pm.endpos.z - mv->GetAbsOrigin().z;
  1435. mv->m_outStepHeight += stepDist;
  1436. // walked up the step, so just keep result and exit
  1437. Vector vecNewWaterPoint;
  1438. VectorCopy( m_vecWaterPoint, vecNewWaterPoint );
  1439. vecNewWaterPoint.z += ( dest.z - mv->GetAbsOrigin().z );
  1440. bool bOutOfWater = !( enginetrace->GetPointContents( vecNewWaterPoint ) & MASK_WATER );
  1441. if ( bOutOfWater && ( mv->m_vecVelocity.z > 0.0f ) && ( pm.fraction == 1.0f ) )
  1442. {
  1443. // Check the waist level water positions.
  1444. trace_t traceWater;
  1445. UTIL_TraceLine( vecNewWaterPoint, m_vecWaterPoint, CONTENTS_WATER, player, COLLISION_GROUP_NONE, &traceWater );
  1446. if( traceWater.fraction < 1.0f )
  1447. {
  1448. float flFraction = 1.0f - traceWater.fraction;
  1449. // Vector vecSegment;
  1450. // VectorSubtract( mv->GetAbsOrigin(), dest, vecSegment );
  1451. // VectorMA( mv->GetAbsOrigin(), flFraction, vecSegment, mv->GetAbsOrigin() );
  1452. float flZDiff = dest.z - mv->GetAbsOrigin().z;
  1453. float flSetZ = mv->GetAbsOrigin().z + ( flFraction * flZDiff );
  1454. flSetZ -= 0.0325f;
  1455. VectorCopy (pm.endpos, mv->GetAbsOrigin());
  1456. mv->GetAbsOrigin().z = flSetZ;
  1457. VectorSubtract( mv->m_vecVelocity, player->GetBaseVelocity(), mv->m_vecVelocity );
  1458. mv->m_vecVelocity.z = 0.0f;
  1459. }
  1460. }
  1461. else
  1462. {
  1463. VectorCopy (pm.endpos, mv->GetAbsOrigin());
  1464. VectorSubtract( mv->m_vecVelocity, player->GetBaseVelocity(), mv->m_vecVelocity );
  1465. }
  1466. return;
  1467. #endif
  1468. float stepDist = pm.endpos.z - mv->GetAbsOrigin().z;
  1469. mv->m_outStepHeight += stepDist;
  1470. // walked up the step, so just keep result and exit
  1471. mv->SetAbsOrigin( pm.endpos );
  1472. VectorSubtract( mv->m_vecVelocity, player->GetBaseVelocity(), mv->m_vecVelocity );
  1473. return;
  1474. }
  1475. // Try moving straight along out normal path.
  1476. TryPlayerMove();
  1477. }
  1478. else
  1479. {
  1480. if ( !player->GetGroundEntity() )
  1481. {
  1482. TryPlayerMove();
  1483. VectorSubtract( mv->m_vecVelocity, player->GetBaseVelocity(), mv->m_vecVelocity );
  1484. return;
  1485. }
  1486. StepMove( dest, pm );
  1487. }
  1488. VectorSubtract( mv->m_vecVelocity, player->GetBaseVelocity(), mv->m_vecVelocity );
  1489. }
  1490. //-----------------------------------------------------------------------------
  1491. // Purpose:
  1492. //-----------------------------------------------------------------------------
  1493. void CTFGameMovement::WalkMove( void )
  1494. {
  1495. // Get the movement angles.
  1496. Vector vecForward, vecRight, vecUp;
  1497. AngleVectors( mv->m_vecViewAngles, &vecForward, &vecRight, &vecUp );
  1498. vecForward.z = 0.0f;
  1499. vecRight.z = 0.0f;
  1500. VectorNormalize( vecForward );
  1501. VectorNormalize( vecRight );
  1502. // Copy movement amounts
  1503. float flForwardMove = mv->m_flForwardMove;
  1504. float flSideMove = mv->m_flSideMove;
  1505. // Find the direction,velocity in the x,y plane.
  1506. Vector vecWishDirection( ( ( vecForward.x * flForwardMove ) + ( vecRight.x * flSideMove ) ),
  1507. ( ( vecForward.y * flForwardMove ) + ( vecRight.y * flSideMove ) ),
  1508. 0.0f );
  1509. // Calculate the speed and direction of movement, then clamp the speed.
  1510. float flWishSpeed = VectorNormalize( vecWishDirection );
  1511. flWishSpeed = clamp( flWishSpeed, 0.0f, mv->m_flMaxSpeed );
  1512. // Accelerate in the x,y plane.
  1513. mv->m_vecVelocity.z = 0;
  1514. float flAccelerate = sv_accelerate.GetFloat();
  1515. // if our wish speed is too low (attributes), we must increase acceleration or we'll never overcome friction
  1516. // Reverse the basic friction calculation to find our required acceleration
  1517. if ( flWishSpeed > 0 && flWishSpeed < CalcWishSpeedThreshold() )
  1518. {
  1519. // accelspeed = accel * gpGlobals->frametime * wishspeed * player->m_surfaceFriction;
  1520. // accelspeed > drop;
  1521. // drop = accel * frametime * wish * plFriction
  1522. // accel > drop / (wish * gametime * plFriction)
  1523. // drop = control * (plFriction * sv_friction) * gameTime;
  1524. // accel > control * sv_friction / wish
  1525. float flSpeed = VectorLength( mv->m_vecVelocity );
  1526. float flControl = (flSpeed < sv_stopspeed.GetFloat()) ? sv_stopspeed.GetFloat() : flSpeed;
  1527. flAccelerate = (flControl * sv_friction.GetFloat()) / flWishSpeed + 1;
  1528. }
  1529. Accelerate( vecWishDirection, flWishSpeed, flAccelerate );
  1530. Assert( mv->m_vecVelocity.z == 0.0f );
  1531. // Clamp the players speed in x,y.
  1532. float flNewSpeed = VectorLength( mv->m_vecVelocity );
  1533. if ( flNewSpeed > mv->m_flMaxSpeed )
  1534. {
  1535. float flScale = ( mv->m_flMaxSpeed / flNewSpeed );
  1536. mv->m_vecVelocity.x *= flScale;
  1537. mv->m_vecVelocity.y *= flScale;
  1538. }
  1539. float flForwardPull = m_pTFPlayer->GetMovementForwardPull();
  1540. if ( flForwardPull > 0.0f )
  1541. {
  1542. mv->m_vecVelocity += vecForward * flForwardPull;
  1543. if ( mv->m_vecVelocity.Length2D() > mv->m_flMaxSpeed )
  1544. {
  1545. VectorNormalize( mv->m_vecVelocity );
  1546. mv->m_vecVelocity *= mv->m_flMaxSpeed;
  1547. }
  1548. }
  1549. // Now reduce their backwards speed to some percent of max, if they are traveling backwards
  1550. // unless they are under some minimum, to not penalize deployed snipers or heavies
  1551. if ( tf_clamp_back_speed.GetFloat() < 1.0 && VectorLength( mv->m_vecVelocity ) > tf_clamp_back_speed_min.GetFloat() )
  1552. {
  1553. float flDot = DotProduct( vecForward, mv->m_vecVelocity );
  1554. // are we moving backwards at all?
  1555. if ( flDot < 0 )
  1556. {
  1557. Vector vecBackMove = vecForward * flDot;
  1558. Vector vecRightMove = vecRight * DotProduct( vecRight, mv->m_vecVelocity );
  1559. // clamp the back move vector if it is faster than max
  1560. float flBackSpeed = VectorLength( vecBackMove );
  1561. float flMaxBackSpeed = ( mv->m_flMaxSpeed * tf_clamp_back_speed.GetFloat() );
  1562. if ( flBackSpeed > flMaxBackSpeed )
  1563. {
  1564. vecBackMove *= flMaxBackSpeed / flBackSpeed;
  1565. }
  1566. // reassemble velocity
  1567. mv->m_vecVelocity = vecBackMove + vecRightMove;
  1568. // Re-run this to prevent crazy values (clients can induce this via usercmd viewangles hacking)
  1569. flNewSpeed = VectorLength( mv->m_vecVelocity );
  1570. if ( flNewSpeed > mv->m_flMaxSpeed )
  1571. {
  1572. float flScale = ( mv->m_flMaxSpeed / flNewSpeed );
  1573. mv->m_vecVelocity.x *= flScale;
  1574. mv->m_vecVelocity.y *= flScale;
  1575. }
  1576. }
  1577. }
  1578. // Add base velocity to the player's current velocity - base velocity = velocity from conveyors, etc.
  1579. VectorAdd( mv->m_vecVelocity, player->GetBaseVelocity(), mv->m_vecVelocity );
  1580. // Calculate the current speed and return if we are not really moving.
  1581. float flSpeed = VectorLength( mv->m_vecVelocity );
  1582. if ( flSpeed < 1.0f )
  1583. {
  1584. // I didn't remove the base velocity here since it wasn't moving us in the first place.
  1585. mv->m_vecVelocity.Init();
  1586. return;
  1587. }
  1588. // Calculate the destination.
  1589. Vector vecDestination;
  1590. vecDestination.x = mv->GetAbsOrigin().x + ( mv->m_vecVelocity.x * gpGlobals->frametime );
  1591. vecDestination.y = mv->GetAbsOrigin().y + ( mv->m_vecVelocity.y * gpGlobals->frametime );
  1592. vecDestination.z = mv->GetAbsOrigin().z;
  1593. #ifdef GAME_DLL
  1594. // allow bot to approve position change for intentional movement
  1595. INextBot *bot = player->MyNextBotPointer();
  1596. if ( bot && bot->GetIntentionInterface()->IsPositionAllowed( bot, vecDestination ) == ANSWER_NO )
  1597. {
  1598. // rejected - stay put
  1599. return;
  1600. }
  1601. #endif
  1602. // Try moving to the destination.
  1603. trace_t trace;
  1604. TracePlayerBBox( mv->GetAbsOrigin(), vecDestination, PlayerSolidMask(), COLLISION_GROUP_PLAYER_MOVEMENT, trace );
  1605. if ( trace.fraction == 1.0f )
  1606. {
  1607. // Made it to the destination (remove the base velocity).
  1608. mv->SetAbsOrigin( trace.endpos );
  1609. VectorSubtract( mv->m_vecVelocity, player->GetBaseVelocity(), mv->m_vecVelocity );
  1610. // Save the wish velocity.
  1611. mv->m_outWishVel += ( vecWishDirection * flWishSpeed );
  1612. // Try and keep the player on the ground.
  1613. // NOTE YWB 7/5/07: Don't do this here, our version of CategorizePosition encompasses this test
  1614. // StayOnGround();
  1615. #ifdef CLIENT_DLL
  1616. // Track how far we moved (if we're a Scout or an Engineer carrying a building).
  1617. CTFPlayer* pTFPlayer = ToTFPlayer( player );
  1618. if ( pTFPlayer->IsPlayerClass( TF_CLASS_SCOUT ) ||
  1619. ( pTFPlayer->IsPlayerClass( TF_CLASS_ENGINEER ) && pTFPlayer->m_Shared.IsCarryingObject() ) )
  1620. {
  1621. float fInchesToMeters = 0.0254f;
  1622. float fWorldScale = 0.25;
  1623. float fMeters = pTFPlayer->GetMetersRan();
  1624. float fMetersRan = flSpeed*fInchesToMeters*fWorldScale*gpGlobals->frametime;
  1625. pTFPlayer->SetMetersRan( fMeters + fMetersRan, gpGlobals->framecount );
  1626. }
  1627. #endif
  1628. return;
  1629. }
  1630. CTFPlayer* pBumpPlayer = ToTFPlayer( trace.m_pEnt );
  1631. if ( pBumpPlayer )
  1632. {
  1633. m_pTFPlayer->m_Shared.EndCharge();
  1634. }
  1635. // Now try and do a step move.
  1636. StepMove( vecDestination, trace );
  1637. // Remove base velocity.
  1638. Vector baseVelocity = player->GetBaseVelocity();
  1639. VectorSubtract( mv->m_vecVelocity, baseVelocity, mv->m_vecVelocity );
  1640. CheckKartWallBumping();
  1641. // Save the wish velocity.
  1642. mv->m_outWishVel += ( vecWishDirection * flWishSpeed );
  1643. // Try and keep the player on the ground.
  1644. // NOTE YWB 7/5/07: Don't do this here, our version of CategorizePosition encompasses this test
  1645. // StayOnGround();
  1646. #if 0
  1647. // Debugging!!!
  1648. Vector vecTestVelocity = mv->m_vecVelocity;
  1649. vecTestVelocity.z = 0.0f;
  1650. float flTestSpeed = VectorLength( vecTestVelocity );
  1651. if ( baseVelocity.IsZero() && ( flTestSpeed > ( mv->m_flMaxSpeed + 1.0f ) ) )
  1652. {
  1653. Msg( "Step Max Speed < %f\n", flTestSpeed );
  1654. }
  1655. if ( tf_showspeed.GetBool() )
  1656. {
  1657. Msg( "Speed=%f\n", flTestSpeed );
  1658. }
  1659. #endif
  1660. }
  1661. void CTFGameMovement::CheckKartWallBumping()
  1662. {
  1663. // Karts need to drop their velocity when they bump into things
  1664. if ( !m_pTFPlayer->m_Shared.InCond( TF_COND_HALLOWEEN_KART ) )
  1665. return;
  1666. const float flCurrentSpeed = m_pTFPlayer->GetCurrentTauntMoveSpeed();
  1667. const float flMaxSpeed = mv->m_vecVelocity.Length();
  1668. const float flClampedSpeed = clamp( flCurrentSpeed, -flMaxSpeed, flMaxSpeed );
  1669. m_pTFPlayer->SetCurrentTauntMoveSpeed( flClampedSpeed );
  1670. // We hit a wall at a good speed
  1671. if ( fabs( flCurrentSpeed ) > 100.f && ( flCurrentSpeed - flClampedSpeed > 100.f ) )
  1672. {
  1673. // Play a flinch to show we impacted something
  1674. bool bDashing = m_pTFPlayer->m_Shared.InCond( TF_COND_HALLOWEEN_KART_DASH );
  1675. m_pTFPlayer->DoAnimationEvent( PLAYERANIMEVENT_CUSTOM_GESTURE, bDashing ? ACT_KART_IMPACT_BIG : ACT_KART_IMPACT );
  1676. Vector vAim = m_pTFPlayer->GetLocalVelocity();
  1677. vAim.z = 0;
  1678. vAim.NormalizeInPlace();
  1679. // Handle hitting skybox (disappear).
  1680. trace_t pWallTrace;
  1681. UTIL_TraceLine( m_pTFPlayer->GetAbsOrigin(), m_pTFPlayer->GetAbsOrigin() + vAim * 64, MASK_SOLID, m_pTFPlayer, COLLISION_GROUP_DEBRIS, &pWallTrace );
  1682. // if we collide with a wall that is 90degrees or higher, bump backwards
  1683. if ( pWallTrace.fraction < 1.0 && !( pWallTrace.surface.flags & SURF_SKY ) && pWallTrace.m_pEnt && !pWallTrace.m_pEnt->IsPlayer() && pWallTrace.plane.normal.z <= 0 )
  1684. {
  1685. #ifdef GAME_DLL
  1686. // Bounce off the wall, deflect in the direction of the normal of the surface that we collided with
  1687. Vector vOld = m_pTFPlayer->GetLocalVelocity();
  1688. Vector vNew = ( -2.0f * pWallTrace.plane.normal.Dot( vOld ) * pWallTrace.plane.normal + vOld );
  1689. vNew.NormalizeInPlace();
  1690. m_pTFPlayer->AddHalloweenKartPushEvent( m_pTFPlayer, NULL, NULL, vNew * vOld.Length() / 2.0f, 0 );
  1691. if ( bDashing )
  1692. {
  1693. // Stop moving
  1694. m_pTFPlayer->SetAbsVelocity( vec3_origin );
  1695. m_pTFPlayer->SetCurrentTauntMoveSpeed( 0 );
  1696. m_pTFPlayer->m_Shared.RemoveCond( TF_COND_HALLOWEEN_KART_DASH );
  1697. }
  1698. m_pTFPlayer->SetCurrentTauntMoveSpeed( 0.f );
  1699. #endif
  1700. #ifdef CLIENT_DLL
  1701. if ( bDashing )
  1702. {
  1703. m_pTFPlayer->EmitSound( "BumperCar.BumpHard" );
  1704. m_pTFPlayer->ParticleProp()->Create( "kart_impact_sparks", PATTACH_ABSORIGIN, NULL, vAim );
  1705. }
  1706. else
  1707. {
  1708. m_pTFPlayer->EmitSound( "BumperCar.Bump" );
  1709. m_pTFPlayer->ParticleProp()->Create( "kart_impact_sparks", PATTACH_ABSORIGIN, NULL, vAim );
  1710. }
  1711. #endif
  1712. }
  1713. }
  1714. }
  1715. //-----------------------------------------------------------------------------
  1716. // Purpose:
  1717. //-----------------------------------------------------------------------------
  1718. float CTFGameMovement::GetAirSpeedCap( void )
  1719. {
  1720. if ( m_pTFPlayer->GetGrapplingHookTarget() )
  1721. {
  1722. if ( m_pTFPlayer->m_Shared.GetCarryingRuneType() == RUNE_AGILITY )
  1723. {
  1724. switch ( m_pTFPlayer->GetPlayerClass()->GetClassIndex() )
  1725. {
  1726. case TF_CLASS_SOLDIER:
  1727. case TF_CLASS_HEAVYWEAPONS:
  1728. return 850.f;
  1729. default:
  1730. return 950.f;
  1731. }
  1732. }
  1733. return tf_grapplinghook_move_speed.GetFloat();
  1734. }
  1735. else if ( m_pTFPlayer->m_Shared.InCond( TF_COND_SHIELD_CHARGE ) )
  1736. {
  1737. return tf_max_charge_speed.GetFloat();
  1738. }
  1739. else
  1740. {
  1741. float flCap = BaseClass::GetAirSpeedCap();
  1742. /*
  1743. #ifdef STAGING_ONLY
  1744. if ( m_pTFPlayer->m_Shared.InCond( TF_COND_SPACE_GRAVITY ) )
  1745. {
  1746. flCap *= tf_space_aircontrol.GetFloat();
  1747. }
  1748. #endif
  1749. */
  1750. if ( m_pTFPlayer->m_Shared.InCond( TF_COND_PARACHUTE_DEPLOYED ) )
  1751. {
  1752. flCap *= tf_parachute_aircontrol.GetFloat();
  1753. }
  1754. if ( m_pTFPlayer->m_Shared.InCond( TF_COND_HALLOWEEN_KART ) )
  1755. {
  1756. if ( m_pTFPlayer->m_Shared.InCond( TF_COND_HALLOWEEN_KART_DASH ) )
  1757. {
  1758. return tf_halloween_kart_dash_speed.GetFloat();
  1759. }
  1760. flCap *= tf_halloween_kart_aircontrol.GetFloat();
  1761. }
  1762. float flIncreasedAirControl = 1.f;
  1763. CALL_ATTRIB_HOOK_FLOAT_ON_OTHER( m_pTFPlayer, flIncreasedAirControl, mod_air_control );
  1764. return ( flCap * flIncreasedAirControl );
  1765. }
  1766. }
  1767. //-----------------------------------------------------------------------------
  1768. // Purpose:
  1769. //-----------------------------------------------------------------------------
  1770. void CTFGameMovement::AirMove( void )
  1771. {
  1772. // check if grappling move should do step move
  1773. if ( m_pTFPlayer->GetGrapplingHookTarget() )
  1774. {
  1775. // Try moving to the destination.
  1776. Vector vecDestination = mv->GetAbsOrigin() + ( mv->m_vecVelocity * gpGlobals->frametime );
  1777. trace_t trace;
  1778. TracePlayerBBox( mv->GetAbsOrigin(), vecDestination, PlayerSolidMask(), COLLISION_GROUP_PLAYER_MOVEMENT, trace );
  1779. if ( trace.fraction != 1.f )
  1780. {
  1781. StepMove( vecDestination, trace );
  1782. return;
  1783. }
  1784. }
  1785. int i;
  1786. Vector wishvel;
  1787. float fmove, smove;
  1788. Vector wishdir;
  1789. float wishspeed;
  1790. Vector forward, right, up;
  1791. AngleVectors (mv->m_vecViewAngles, &forward, &right, &up); // Determine movement angles
  1792. // Copy movement amounts
  1793. fmove = mv->m_flForwardMove;
  1794. smove = mv->m_flSideMove;
  1795. // Zero out z components of movement vectors
  1796. forward[2] = 0;
  1797. right[2] = 0;
  1798. VectorNormalize(forward); // Normalize remainder of vectors
  1799. VectorNormalize(right); //
  1800. for (i=0 ; i<2 ; i++) // Determine x and y parts of velocity
  1801. wishvel[i] = forward[i]*fmove + right[i]*smove;
  1802. wishvel[2] = 0; // Zero out z part of velocity
  1803. VectorCopy (wishvel, wishdir); // Determine maginitude of speed of move
  1804. wishspeed = VectorNormalize(wishdir);
  1805. //
  1806. // clamp to server defined max speed
  1807. //
  1808. if ( wishspeed != 0 && (wishspeed > mv->m_flMaxSpeed))
  1809. {
  1810. VectorScale (wishvel, mv->m_flMaxSpeed/wishspeed, wishvel);
  1811. wishspeed = mv->m_flMaxSpeed;
  1812. }
  1813. float flAirAccel = sv_airaccelerate.GetFloat();
  1814. /*
  1815. #ifdef STAGING_ONLY
  1816. if ( m_pTFPlayer->m_Shared.InCond( TF_COND_SPACE_GRAVITY ) )
  1817. {
  1818. flAirAccel *= tf_space_aircontrol.GetFloat();
  1819. }
  1820. #endif
  1821. */
  1822. AirAccelerate( wishdir, wishspeed, flAirAccel );
  1823. float flForwardPull = m_pTFPlayer->GetMovementForwardPull();
  1824. if ( flForwardPull > 0.0f )
  1825. {
  1826. mv->m_vecVelocity += forward * flForwardPull;
  1827. if ( mv->m_vecVelocity.Length2D() > mv->m_flMaxSpeed )
  1828. {
  1829. float flZ = mv->m_vecVelocity.z;
  1830. mv->m_vecVelocity.z = 0.0f;
  1831. VectorNormalize( mv->m_vecVelocity );
  1832. mv->m_vecVelocity *= mv->m_flMaxSpeed;
  1833. mv->m_vecVelocity.z = flZ;
  1834. }
  1835. }
  1836. // Add in any base velocity to the current velocity.
  1837. VectorAdd( mv->m_vecVelocity, player->GetBaseVelocity(), mv->m_vecVelocity );
  1838. int iBlocked = TryPlayerMove();
  1839. // TryPlayerMove uses '2' to indictate wall colision wtf
  1840. if ( iBlocked & 2 )
  1841. {
  1842. CheckKartWallBumping();
  1843. }
  1844. // Now pull the base velocity back out. Base velocity is set if you are on a moving object, like a conveyor (or maybe another monster?)
  1845. VectorSubtract( mv->m_vecVelocity, player->GetBaseVelocity(), mv->m_vecVelocity );
  1846. }
  1847. extern void TracePlayerBBoxForGround( const Vector& start, const Vector& end, const Vector& minsSrc,
  1848. const Vector& maxsSrc, IHandleEntity *player, unsigned int fMask,
  1849. int collisionGroup, trace_t& pm );
  1850. //-----------------------------------------------------------------------------
  1851. // This filter checks against buildable objects.
  1852. //-----------------------------------------------------------------------------
  1853. class CTraceFilterObject : public CTraceFilterSimple
  1854. {
  1855. public:
  1856. DECLARE_CLASS( CTraceFilterObject, CTraceFilterSimple );
  1857. CTraceFilterObject( const IHandleEntity *passentity, int collisionGroup );
  1858. virtual bool ShouldHitEntity( IHandleEntity *pHandleEntity, int contentsMask );
  1859. };
  1860. CTraceFilterObject::CTraceFilterObject( const IHandleEntity *passentity, int collisionGroup ) :
  1861. BaseClass( passentity, collisionGroup )
  1862. {
  1863. }
  1864. bool CTraceFilterObject::ShouldHitEntity( IHandleEntity *pHandleEntity, int contentsMask )
  1865. {
  1866. CBaseEntity *pMe = const_cast< CBaseEntity * >( EntityFromEntityHandle( GetPassEntity() ) );
  1867. CBaseEntity *pEntity = EntityFromEntityHandle( pHandleEntity );
  1868. if ( pEntity )
  1869. {
  1870. #ifdef STAGING_ONLY
  1871. // Special case stealth clips through all players and objects
  1872. CTFPlayer *pTFPlayerMe = ToTFPlayer( pMe );
  1873. if ( pTFPlayerMe && pTFPlayerMe->m_Shared.InCond( TF_COND_STEALTHED_PHASE ) )
  1874. {
  1875. // if we don't want to collide with anything, just remove this if
  1876. if ( pEntity->IsBaseObject() || pEntity->IsPlayer() )
  1877. {
  1878. return false;
  1879. }
  1880. }
  1881. if ( pEntity->IsPlayer() )
  1882. {
  1883. CTFPlayer *pTFPlayerThem = ToTFPlayer( pEntity );
  1884. if ( pTFPlayerThem && pTFPlayerThem->m_Shared.InCond( TF_COND_STEALTHED_PHASE ) )
  1885. return false;
  1886. }
  1887. #endif // STAGING_ONLY
  1888. if ( pEntity->IsBaseObject() )
  1889. {
  1890. CBaseObject *pObject = assert_cast<CBaseObject *>( pEntity );
  1891. if ( pObject && pObject->GetOwner() == pMe )
  1892. {
  1893. #ifdef GAME_DLL
  1894. // engineer-bots should not collide with their buildables to avoid nasty pathing issues
  1895. CTFPlayer *pOwner = ToTFPlayer( pMe );
  1896. if ( pOwner->IsBotOfType( TF_BOT_TYPE ) )
  1897. {
  1898. bool bHitObjectType = pObject->GetType() == OBJ_SENTRYGUN || pObject->GetType() == OBJ_DISPENSER;
  1899. if ( TFGameRules() && TFGameRules()->IsMannVsMachineMode() )
  1900. {
  1901. bHitObjectType |= pObject->GetType() == OBJ_TELEPORTER;
  1902. }
  1903. if ( bHitObjectType )
  1904. {
  1905. // engineer bots not blocked by sentries or dispensers
  1906. return false;
  1907. }
  1908. }
  1909. #endif
  1910. // my buildings are solid to me
  1911. return true;
  1912. }
  1913. }
  1914. #ifdef GAME_DLL
  1915. else if ( pEntity->IsPlayer() )
  1916. {
  1917. if ( TFGameRules() && TFGameRules()->IsMannVsMachineMode() )
  1918. {
  1919. CTFBot *bot = ToTFBot( pEntity );
  1920. if ( bot && ( bot->HasMission( CTFBot::MISSION_DESTROY_SENTRIES ) || bot->HasMission( CTFBot::MISSION_REPROGRAMMED ) ) )
  1921. {
  1922. // Don't collide with sentry busters since they don't collide with us
  1923. return false;
  1924. }
  1925. CTFBot *meBot = ToTFBot( pMe );
  1926. if ( meBot && ( meBot->HasMission( CTFBot::MISSION_DESTROY_SENTRIES ) || meBot->HasMission( CTFBot::MISSION_REPROGRAMMED ) ) )
  1927. {
  1928. // Sentry Busters don't collide with enemies (so they can't be body-blocked)
  1929. return false;
  1930. }
  1931. }
  1932. }
  1933. else if ( pEntity->MyNextBotPointer() && !pEntity->MyNextBotPointer()->GetLocomotionInterface()->ShouldCollideWith( pMe ) )
  1934. {
  1935. return false;
  1936. }
  1937. #endif
  1938. }
  1939. return CTraceFilterSimple::ShouldHitEntity( pHandleEntity, contentsMask );
  1940. }
  1941. CBaseHandle CTFGameMovement::TestPlayerPosition( const Vector& pos, int collisionGroup, trace_t& pm )
  1942. {
  1943. if( tf_solidobjects.GetBool() == false )
  1944. return BaseClass::TestPlayerPosition( pos, collisionGroup, pm );
  1945. Ray_t ray;
  1946. ray.Init( pos, pos, GetPlayerMins(), GetPlayerMaxs() );
  1947. CTraceFilterObject traceFilter( mv->m_nPlayerHandle.Get(), collisionGroup );
  1948. enginetrace->TraceRay( ray, PlayerSolidMask(), &traceFilter, &pm );
  1949. if ( (pm.contents & PlayerSolidMask()) && pm.m_pEnt )
  1950. {
  1951. return pm.m_pEnt->GetRefEHandle();
  1952. }
  1953. else
  1954. {
  1955. return INVALID_EHANDLE_INDEX;
  1956. }
  1957. }
  1958. //-----------------------------------------------------------------------------
  1959. // Traces player movement + position
  1960. //-----------------------------------------------------------------------------
  1961. void CTFGameMovement::TracePlayerBBox( const Vector& start, const Vector& end, unsigned int fMask, int collisionGroup, trace_t& pm )
  1962. {
  1963. if( tf_solidobjects.GetBool() == false )
  1964. return BaseClass::TracePlayerBBox( start, end, fMask, collisionGroup, pm );
  1965. Ray_t ray;
  1966. ray.Init( start, end, GetPlayerMins(), GetPlayerMaxs() );
  1967. CTraceFilterObject traceFilter( mv->m_nPlayerHandle.Get(), collisionGroup );
  1968. enginetrace->TraceRay( ray, fMask, &traceFilter, &pm );
  1969. }
  1970. //-----------------------------------------------------------------------------
  1971. // Purpose:
  1972. // Input : &input -
  1973. //-----------------------------------------------------------------------------
  1974. void CTFGameMovement::CategorizePosition( void )
  1975. {
  1976. // Observer.
  1977. if ( player->IsObserver() )
  1978. return;
  1979. // Reset this each time we-recategorize, otherwise we have bogus friction when we jump into water and plunge downward really quickly
  1980. player->m_surfaceFriction = 1.0f;
  1981. // Doing this before we move may introduce a potential latency in water detection, but
  1982. // doing it after can get us stuck on the bottom in water if the amount we move up
  1983. // is less than the 1 pixel 'threshold' we're about to snap to. Also, we'll call
  1984. // this several times per frame, so we really need to avoid sticking to the bottom of
  1985. // water on each call, and the converse case will correct itself if called twice.
  1986. CheckWater();
  1987. // If standing on a ladder we are not on ground.
  1988. if ( player->GetMoveType() == MOVETYPE_LADDER )
  1989. {
  1990. SetGroundEntity( NULL );
  1991. return;
  1992. }
  1993. // Check for a jump.
  1994. if ( mv->m_vecVelocity.z > 250.0f )
  1995. {
  1996. #if defined(GAME_DLL)
  1997. if ( m_pTFPlayer->m_bTakenBlastDamageSinceLastMovement )
  1998. {
  1999. m_pTFPlayer->SetBlastJumpState( TF_PLAYER_ENEMY_BLASTED_ME );
  2000. }
  2001. #endif
  2002. SetGroundEntity( NULL );
  2003. return;
  2004. }
  2005. // Calculate the start and end position.
  2006. Vector vecStartPos = mv->GetAbsOrigin();
  2007. Vector vecEndPos( mv->GetAbsOrigin().x, mv->GetAbsOrigin().y, ( mv->GetAbsOrigin().z - 2.0f ) );
  2008. // NOTE YWB 7/5/07: Since we're already doing a traceline here, we'll subsume the StayOnGround (stair debouncing) check into the main traceline we do here to see what we're standing on
  2009. bool bUnderwater = ( player->GetWaterLevel() >= WL_Eyes );
  2010. bool bMoveToEndPos = false;
  2011. if ( player->GetMoveType() == MOVETYPE_WALK &&
  2012. player->GetGroundEntity() != NULL && !bUnderwater )
  2013. {
  2014. // if walking and still think we're on ground, we'll extend trace down by stepsize so we don't bounce down slopes
  2015. vecEndPos.z -= player->GetStepSize();
  2016. bMoveToEndPos = true;
  2017. }
  2018. trace_t trace;
  2019. TracePlayerBBox( vecStartPos, vecEndPos, PlayerSolidMask(), COLLISION_GROUP_PLAYER_MOVEMENT, trace );
  2020. // Steep plane, not on ground.
  2021. if ( trace.plane.normal.z < 0.7f )
  2022. {
  2023. // Test four sub-boxes, to see if any of them would have found shallower slope we could actually stand on.
  2024. TracePlayerBBoxForGround( vecStartPos, vecEndPos, GetPlayerMins(), GetPlayerMaxs(), mv->m_nPlayerHandle.Get(), PlayerSolidMask(), COLLISION_GROUP_PLAYER_MOVEMENT, trace );
  2025. if ( trace.plane.normal[2] < 0.7f )
  2026. {
  2027. // Too steep.
  2028. SetGroundEntity( NULL );
  2029. if ( ( mv->m_vecVelocity.z > 0.0f ) &&
  2030. ( player->GetMoveType() != MOVETYPE_NOCLIP ) )
  2031. {
  2032. player->m_surfaceFriction = 0.25f;
  2033. }
  2034. }
  2035. else
  2036. {
  2037. SetGroundEntity( &trace );
  2038. }
  2039. }
  2040. else
  2041. {
  2042. // YWB: This logic block essentially lifted from StayOnGround implementation
  2043. if ( bMoveToEndPos &&
  2044. !trace.startsolid && // not sure we need this check as fraction would == 0.0f?
  2045. trace.fraction > 0.0f && // must go somewhere
  2046. trace.fraction < 1.0f ) // must hit something
  2047. {
  2048. float flDelta = fabs( mv->GetAbsOrigin().z - trace.endpos.z );
  2049. // HACK HACK: The real problem is that trace returning that strange value
  2050. // we can't network over based on bit precision of networking origins
  2051. if ( flDelta > 0.5f * COORD_RESOLUTION )
  2052. {
  2053. Vector org = mv->GetAbsOrigin();
  2054. org.z = trace.endpos.z;
  2055. mv->SetAbsOrigin( org );
  2056. }
  2057. }
  2058. SetGroundEntity( &trace );
  2059. }
  2060. }
  2061. //-----------------------------------------------------------------------------
  2062. // Purpose:
  2063. //-----------------------------------------------------------------------------
  2064. void CTFGameMovement::CheckWaterJump( void )
  2065. {
  2066. Vector flatforward;
  2067. Vector flatvelocity;
  2068. float curspeed;
  2069. // Jump button down?
  2070. bool bJump = ( ( mv->m_nButtons & IN_JUMP ) != 0 );
  2071. Vector forward, right;
  2072. AngleVectors( mv->m_vecViewAngles, &forward, &right, NULL ); // Determine movement angles
  2073. // Already water jumping.
  2074. if (player->m_flWaterJumpTime)
  2075. return;
  2076. // Don't hop out if we just jumped in
  2077. if (mv->m_vecVelocity[2] < -180)
  2078. return; // only hop out if we are moving up
  2079. // See if we are backing up
  2080. flatvelocity[0] = mv->m_vecVelocity[0];
  2081. flatvelocity[1] = mv->m_vecVelocity[1];
  2082. flatvelocity[2] = 0;
  2083. // Must be moving
  2084. curspeed = VectorNormalize( flatvelocity );
  2085. #if 1
  2086. // Copy movement amounts
  2087. float fmove = mv->m_flForwardMove;
  2088. float smove = mv->m_flSideMove;
  2089. for ( int iAxis = 0; iAxis < 2; ++iAxis )
  2090. {
  2091. flatforward[iAxis] = forward[iAxis] * fmove + right[iAxis] * smove;
  2092. }
  2093. #else
  2094. // see if near an edge
  2095. flatforward[0] = forward[0];
  2096. flatforward[1] = forward[1];
  2097. #endif
  2098. flatforward[2] = 0;
  2099. VectorNormalize( flatforward );
  2100. // Are we backing into water from steps or something? If so, don't pop forward
  2101. if ( curspeed != 0.0 && ( DotProduct( flatvelocity, flatforward ) < 0.0 ) && !bJump )
  2102. return;
  2103. Vector vecStart;
  2104. // Start line trace at waist height (using the center of the player for this here)
  2105. vecStart = mv->GetAbsOrigin() + (GetPlayerMins() + GetPlayerMaxs() ) * 0.5;
  2106. Vector vecEnd;
  2107. VectorMA( vecStart, TF_WATERJUMP_FORWARD/*tf_waterjump_forward.GetFloat()*/, flatforward, vecEnd );
  2108. trace_t tr;
  2109. TracePlayerBBox( vecStart, vecEnd, PlayerSolidMask(), COLLISION_GROUP_PLAYER_MOVEMENT, tr );
  2110. if ( tr.fraction < 1.0 ) // solid at waist
  2111. {
  2112. IPhysicsObject *pPhysObj = tr.m_pEnt->VPhysicsGetObject();
  2113. if ( pPhysObj )
  2114. {
  2115. if ( pPhysObj->GetGameFlags() & FVPHYSICS_PLAYER_HELD )
  2116. return;
  2117. }
  2118. vecStart.z = mv->GetAbsOrigin().z + player->GetViewOffset().z + WATERJUMP_HEIGHT;
  2119. VectorMA( vecStart, TF_WATERJUMP_FORWARD/*tf_waterjump_forward.GetFloat()*/, flatforward, vecEnd );
  2120. VectorMA( vec3_origin, -50.0f, tr.plane.normal, player->m_vecWaterJumpVel );
  2121. TracePlayerBBox( vecStart, vecEnd, PlayerSolidMask(), COLLISION_GROUP_PLAYER_MOVEMENT, tr );
  2122. if ( tr.fraction == 1.0 ) // open at eye level
  2123. {
  2124. // Now trace down to see if we would actually land on a standable surface.
  2125. VectorCopy( vecEnd, vecStart );
  2126. vecEnd.z -= 1024.0f;
  2127. TracePlayerBBox( vecStart, vecEnd, PlayerSolidMask(), COLLISION_GROUP_PLAYER_MOVEMENT, tr );
  2128. if ( ( tr.fraction < 1.0f ) && ( tr.plane.normal.z >= 0.7 ) )
  2129. {
  2130. mv->m_vecVelocity[2] = TF_WATERJUMP_UP/*tf_waterjump_up.GetFloat()*/; // Push up
  2131. mv->m_nOldButtons |= IN_JUMP; // Don't jump again until released
  2132. player->AddFlag( FL_WATERJUMP );
  2133. player->m_flWaterJumpTime = 2000.0f; // Do this for 2 seconds
  2134. }
  2135. }
  2136. }
  2137. }
  2138. //-----------------------------------------------------------------------------
  2139. // Purpose:
  2140. //-----------------------------------------------------------------------------
  2141. void CTFGameMovement::CheckFalling( void )
  2142. {
  2143. // if we landed on the ground
  2144. if ( player->GetGroundEntity() != NULL && !IsDead() )
  2145. {
  2146. // turn off the jumping flag if we're on ground after a jump
  2147. if ( m_pTFPlayer->m_Shared.IsJumping() )
  2148. {
  2149. m_pTFPlayer->m_Shared.SetJumping( false );
  2150. #ifdef CLIENT_DLL
  2151. IGameEvent *event = gameeventmanager->CreateEvent( "landed" );
  2152. if ( event && m_pTFPlayer->IsLocalPlayer() )
  2153. {
  2154. event->SetInt( "player", m_pTFPlayer->GetUserID() );
  2155. gameeventmanager->FireEventClientSide( event );
  2156. }
  2157. #endif // CLIENT_DLL
  2158. }
  2159. }
  2160. BaseClass::CheckFalling();
  2161. }
  2162. void CTFGameMovement::FullWalkMoveUnderwater()
  2163. {
  2164. if ( player->GetWaterLevel() == WL_Waist )
  2165. {
  2166. CheckWaterJump();
  2167. }
  2168. // If we are falling again, then we must not trying to jump out of water any more.
  2169. if ( ( mv->m_vecVelocity.z < 0.0f ) && player->m_flWaterJumpTime )
  2170. {
  2171. player->m_flWaterJumpTime = 0.0f;
  2172. }
  2173. // Was jump button pressed?
  2174. if ( mv->m_nButtons & IN_JUMP )
  2175. {
  2176. CheckJumpButton();
  2177. }
  2178. else
  2179. {
  2180. mv->m_nOldButtons &= ~IN_JUMP;
  2181. }
  2182. // Perform regular water movement
  2183. WaterMove();
  2184. // Redetermine position vars
  2185. CategorizePosition();
  2186. // If we are on ground, no downward velocity.
  2187. if ( player->GetGroundEntity() != NULL )
  2188. {
  2189. mv->m_vecVelocity[2] = 0;
  2190. }
  2191. }
  2192. //-----------------------------------------------------------------------------
  2193. // Purpose:
  2194. //-----------------------------------------------------------------------------
  2195. void CTFGameMovement::FullWalkMove()
  2196. {
  2197. if ( !InWater() )
  2198. {
  2199. if ( m_pTFPlayer->m_Shared.InCond( TF_COND_PARACHUTE_DEPLOYED ) && mv->m_vecVelocity[2] < 0 )
  2200. {
  2201. mv->m_vecVelocity[2] = Max( mv->m_vecVelocity[2], tf_parachute_maxspeed_z.GetFloat() );
  2202. float flDrag = tf_parachute_maxspeed_xy.GetFloat();
  2203. // Instead of clamping, we'll dampen
  2204. float flSpeedX = abs( mv->m_vecVelocity[0] );
  2205. float flSpeedY = abs( mv->m_vecVelocity[1] );
  2206. float flReductionX = flSpeedX > flDrag ? ( flSpeedX - flDrag ) / 3.0f - 10.0f : 0;
  2207. float flReductionY = flSpeedY > flDrag ? ( flSpeedY - flDrag ) / 3.0f - 10.0f : 0;
  2208. mv->m_vecVelocity[0] = Clamp( mv->m_vecVelocity[0], -flDrag - flReductionX, flDrag + flReductionX );
  2209. mv->m_vecVelocity[1] = Clamp( mv->m_vecVelocity[1], -flDrag - flReductionY, flDrag + flReductionY );
  2210. }
  2211. StartGravity();
  2212. }
  2213. // If we are leaping out of the water, just update the counters.
  2214. if ( player->m_flWaterJumpTime )
  2215. {
  2216. // Try to jump out of the water (and check to see if we still are).
  2217. WaterJump();
  2218. TryPlayerMove();
  2219. CheckWater();
  2220. return;
  2221. }
  2222. // If we are swimming in the water, see if we are nudging against a place we can jump up out
  2223. // of, and, if so, start out jump. Otherwise, if we are not moving up, then reset jump timer to 0.
  2224. // Also run the swim code if we're a ghost or have the TF_COND_SWIMMING_NO_EFFECTS condition
  2225. if ( InWater() || ( m_pTFPlayer && ( m_pTFPlayer->m_Shared.InCond( TF_COND_HALLOWEEN_GHOST_MODE ) || m_pTFPlayer->m_Shared.InCond( TF_COND_SWIMMING_NO_EFFECTS ) ) ) )
  2226. {
  2227. FullWalkMoveUnderwater();
  2228. return;
  2229. }
  2230. if (mv->m_nButtons & IN_JUMP)
  2231. {
  2232. CheckJumpButton();
  2233. }
  2234. else
  2235. {
  2236. mv->m_nOldButtons &= ~IN_JUMP;
  2237. }
  2238. // Make sure velocity is valid.
  2239. CheckVelocity();
  2240. if (player->GetGroundEntity() != NULL)
  2241. {
  2242. mv->m_vecVelocity[2] = 0.0;
  2243. Friction();
  2244. WalkMove();
  2245. }
  2246. else
  2247. {
  2248. AirMove();
  2249. }
  2250. // Set final flags.
  2251. CategorizePosition();
  2252. // Add any remaining gravitational component if we are not in water.
  2253. if ( !InWater() )
  2254. {
  2255. FinishGravity();
  2256. }
  2257. // If we are on ground, no downward velocity.
  2258. if ( player->GetGroundEntity() != NULL )
  2259. {
  2260. mv->m_vecVelocity[2] = 0;
  2261. }
  2262. // Handling falling.
  2263. CheckFalling();
  2264. // Make sure velocity is valid.
  2265. CheckVelocity();
  2266. // #ifdef GAME_DLL
  2267. // if ( m_pTFPlayer->IsPlayerClass( TF_CLASS_SCOUT ) )
  2268. // {
  2269. // CTFWeaponBase* pWeapon = m_pTFPlayer->GetActiveTFWeapon();
  2270. // if ( pWeapon && pWeapon->GetWeaponID() == TF_WEAPON_SODA_POPPER )
  2271. // {
  2272. // float speed = VectorLength( mv->m_vecVelocity );
  2273. // float fDist = speed*gpGlobals->frametime;
  2274. // float fHype = m_pTFPlayer->m_Shared.GetScoutHypeMeter() + (fDist / tf_scout_hype_mod.GetFloat());
  2275. // if ( fHype > 100.f )
  2276. // fHype = 100.f;
  2277. // m_pTFPlayer->m_Shared.SetScoutHypeMeter( fHype );
  2278. // }
  2279. // }
  2280. // #endif
  2281. }
  2282. //-----------------------------------------------------------------------------
  2283. // Purpose:
  2284. //-----------------------------------------------------------------------------
  2285. void CTFGameMovement::FullTossMove( void )
  2286. {
  2287. trace_t pm;
  2288. Vector move;
  2289. // add velocity if player is moving
  2290. if ( (mv->m_flForwardMove != 0.0f) || (mv->m_flSideMove != 0.0f) || (mv->m_flUpMove != 0.0f))
  2291. {
  2292. Vector forward, right, up;
  2293. float fmove, smove;
  2294. Vector wishdir, wishvel;
  2295. float wishspeed;
  2296. int i;
  2297. AngleVectors (mv->m_vecViewAngles, &forward, &right, &up); // Determine movement angles
  2298. // Copy movement amounts
  2299. fmove = mv->m_flForwardMove;
  2300. smove = mv->m_flSideMove;
  2301. VectorNormalize (forward); // Normalize remainder of vectors.
  2302. VectorNormalize (right); //
  2303. for (i=0 ; i<3 ; i++) // Determine x and y parts of velocity
  2304. wishvel[i] = forward[i]*fmove + right[i]*smove;
  2305. wishvel[2] += mv->m_flUpMove;
  2306. VectorCopy (wishvel, wishdir); // Determine maginitude of speed of move
  2307. wishspeed = VectorNormalize(wishdir);
  2308. //
  2309. // Clamp to server defined max speed
  2310. //
  2311. if (wishspeed > mv->m_flMaxSpeed)
  2312. {
  2313. VectorScale (wishvel, mv->m_flMaxSpeed/wishspeed, wishvel);
  2314. wishspeed = mv->m_flMaxSpeed;
  2315. }
  2316. // Set pmove velocity
  2317. Accelerate ( wishdir, wishspeed, sv_accelerate.GetFloat() );
  2318. }
  2319. if ( mv->m_vecVelocity[2] > 0 )
  2320. {
  2321. SetGroundEntity( NULL );
  2322. }
  2323. // If on ground and not moving, return.
  2324. if ( player->GetGroundEntity() != NULL )
  2325. {
  2326. if (VectorCompare(player->GetBaseVelocity(), vec3_origin) &&
  2327. VectorCompare(mv->m_vecVelocity, vec3_origin))
  2328. return;
  2329. }
  2330. CheckVelocity();
  2331. // add gravity
  2332. if ( player->GetMoveType() == MOVETYPE_FLYGRAVITY )
  2333. {
  2334. AddGravity();
  2335. }
  2336. // move origin
  2337. // Base velocity is not properly accounted for since this entity will move again after the bounce without
  2338. // taking it into account
  2339. VectorAdd (mv->m_vecVelocity, player->GetBaseVelocity(), mv->m_vecVelocity);
  2340. CheckVelocity();
  2341. VectorScale (mv->m_vecVelocity, gpGlobals->frametime, move);
  2342. VectorSubtract (mv->m_vecVelocity, player->GetBaseVelocity(), mv->m_vecVelocity);
  2343. PushEntity( move, &pm ); // Should this clear basevelocity
  2344. CheckVelocity();
  2345. if (pm.allsolid)
  2346. {
  2347. // entity is trapped in another solid
  2348. SetGroundEntity( &pm );
  2349. mv->m_vecVelocity.Init();
  2350. return;
  2351. }
  2352. if ( pm.fraction != 1.0f )
  2353. {
  2354. PerformFlyCollisionResolution( pm, move );
  2355. }
  2356. // Check for in water
  2357. CheckWater();
  2358. }
  2359. //-----------------------------------------------------------------------------
  2360. // Purpose: Does the basic move attempting to climb up step heights. It uses
  2361. // the mv->GetAbsOrigin() and mv->m_vecVelocity. It returns a new
  2362. // new mv->GetAbsOrigin(), mv->m_vecVelocity, and mv->m_outStepHeight.
  2363. //-----------------------------------------------------------------------------
  2364. void CTFGameMovement::StepMove( Vector &vecDestination, trace_t &trace )
  2365. {
  2366. trace_t saveTrace;
  2367. saveTrace = trace;
  2368. Vector vecEndPos;
  2369. VectorCopy( vecDestination, vecEndPos );
  2370. Vector vecPos, vecVel;
  2371. VectorCopy( mv->GetAbsOrigin(), vecPos );
  2372. VectorCopy( mv->m_vecVelocity, vecVel );
  2373. bool bLowRoad = false;
  2374. bool bUpRoad = true;
  2375. // First try the "high road" where we move up and over obstacles
  2376. if ( player->m_Local.m_bAllowAutoMovement )
  2377. {
  2378. // Trace up by step height
  2379. VectorCopy( mv->GetAbsOrigin(), vecEndPos );
  2380. vecEndPos.z += player->m_Local.m_flStepSize + DIST_EPSILON;
  2381. TracePlayerBBox( mv->GetAbsOrigin(), vecEndPos, PlayerSolidMask(), COLLISION_GROUP_PLAYER_MOVEMENT, trace );
  2382. if ( !trace.startsolid && !trace.allsolid )
  2383. {
  2384. mv->SetAbsOrigin( trace.endpos );
  2385. }
  2386. // Trace over from there
  2387. TryPlayerMove();
  2388. // Then trace back down by step height to get final position
  2389. VectorCopy( mv->GetAbsOrigin(), vecEndPos );
  2390. vecEndPos.z -= player->m_Local.m_flStepSize + DIST_EPSILON;
  2391. TracePlayerBBox( mv->GetAbsOrigin(), vecEndPos, PlayerSolidMask(), COLLISION_GROUP_PLAYER_MOVEMENT, trace );
  2392. // If the trace ended up in empty space, copy the end over to the origin.
  2393. if ( !trace.startsolid && !trace.allsolid )
  2394. {
  2395. mv->SetAbsOrigin( trace.endpos );
  2396. }
  2397. // If we are not on the standable ground any more or going the "high road" didn't move us at all, then we'll also want to check the "low road"
  2398. if ( ( trace.fraction != 1.0f &&
  2399. trace.plane.normal[2] < 0.7 ) || VectorCompare( mv->GetAbsOrigin(), vecPos ) )
  2400. {
  2401. bLowRoad = true;
  2402. bUpRoad = false;
  2403. }
  2404. }
  2405. else
  2406. {
  2407. bLowRoad = true;
  2408. bUpRoad = false;
  2409. }
  2410. if ( bLowRoad )
  2411. {
  2412. // Save off upward results
  2413. Vector vecUpPos, vecUpVel;
  2414. if ( bUpRoad )
  2415. {
  2416. VectorCopy( mv->GetAbsOrigin(), vecUpPos );
  2417. VectorCopy( mv->m_vecVelocity, vecUpVel );
  2418. }
  2419. // Take the "low" road
  2420. mv->SetAbsOrigin( vecPos );
  2421. VectorCopy( vecVel, mv->m_vecVelocity );
  2422. VectorCopy( vecDestination, vecEndPos );
  2423. TryPlayerMove( &vecEndPos, &saveTrace );
  2424. // Down results.
  2425. Vector vecDownPos, vecDownVel;
  2426. VectorCopy( mv->GetAbsOrigin(), vecDownPos );
  2427. VectorCopy( mv->m_vecVelocity, vecDownVel );
  2428. if ( bUpRoad )
  2429. {
  2430. float flUpDist = ( vecUpPos.x - vecPos.x ) * ( vecUpPos.x - vecPos.x ) + ( vecUpPos.y - vecPos.y ) * ( vecUpPos.y - vecPos.y );
  2431. float flDownDist = ( vecDownPos.x - vecPos.x ) * ( vecDownPos.x - vecPos.x ) + ( vecDownPos.y - vecPos.y ) * ( vecDownPos.y - vecPos.y );
  2432. // decide which one went farther
  2433. if ( flUpDist >= flDownDist )
  2434. {
  2435. mv->SetAbsOrigin( vecUpPos );
  2436. VectorCopy( vecUpVel, mv->m_vecVelocity );
  2437. // copy z value from the Low Road move
  2438. mv->m_vecVelocity.z = vecDownVel.z;
  2439. }
  2440. }
  2441. }
  2442. float flStepDist = mv->GetAbsOrigin().z - vecPos.z;
  2443. if ( flStepDist > 0 )
  2444. {
  2445. mv->m_outStepHeight += flStepDist;
  2446. }
  2447. }
  2448. bool CTFGameMovement::GameHasLadders() const
  2449. {
  2450. return false;
  2451. }
  2452. void CTFGameMovement::SetGroundEntity( trace_t *pm )
  2453. {
  2454. if ( m_pTFPlayer->m_Shared.InCond( TF_COND_HALLOWEEN_KART ) && !m_pTFPlayer->GetGroundEntity() && pm && pm->m_pEnt )
  2455. {
  2456. m_pTFPlayer->EmitSound( "BumperCar.JumpLand" );
  2457. }
  2458. BaseClass::SetGroundEntity( pm );
  2459. if ( pm && pm->m_pEnt )
  2460. {
  2461. #ifdef GAME_DLL
  2462. int iAirDash = m_pTFPlayer->m_Shared.GetAirDash();
  2463. if ( iAirDash > 0 )
  2464. {
  2465. m_pTFPlayer->SpeakConceptIfAllowed( MP_CONCEPT_DOUBLE_JUMP, "started_jumping:0" );
  2466. }
  2467. m_pTFPlayer->m_Shared.SetWeaponKnockbackID( -1 );
  2468. m_pTFPlayer->m_bScattergunJump = false;
  2469. #endif // GAME_DLL
  2470. m_pTFPlayer->m_Shared.SetAirDash( 0 );
  2471. m_pTFPlayer->m_Shared.SetAirDucked( 0 );
  2472. if ( m_pTFPlayer->m_Shared.InCond( TF_COND_GRAPPLINGHOOK_SAFEFALL ) )
  2473. {
  2474. // CheckFalling happens after this. reset the fall velocity to prevent fall damage
  2475. if ( tf_grapplinghook_prevent_fall_damage.GetBool() )
  2476. player->m_Local.m_flFallVelocity = 0;
  2477. m_pTFPlayer->m_Shared.RemoveCond( TF_COND_GRAPPLINGHOOK_SAFEFALL );
  2478. }
  2479. }
  2480. }
  2481. //-----------------------------------------------------------------------------
  2482. // Purpose:
  2483. //-----------------------------------------------------------------------------
  2484. void CTFGameMovement::PlayerRoughLandingEffects( float fvol )
  2485. {
  2486. if ( m_pTFPlayer )
  2487. {
  2488. /*
  2489. #ifdef STAGING_ONLY
  2490. // No impact effects if we're in space low-grav
  2491. if ( m_pTFPlayer->m_Shared.InCond( TF_COND_SPACE_GRAVITY ) )
  2492. {
  2493. return;
  2494. }
  2495. #endif // STAGING_ONLY
  2496. */
  2497. // don't play landing sound when grappling hook into a surface
  2498. if ( m_pTFPlayer->m_Shared.InCond( TF_COND_GRAPPLINGHOOK ) )
  2499. {
  2500. return;
  2501. }
  2502. if ( m_pTFPlayer->IsPlayerClass(TF_CLASS_SCOUT) )
  2503. {
  2504. // Scouts don't play rumble unless they take damage.
  2505. if ( fvol < 1.0 )
  2506. {
  2507. fvol = 0;
  2508. }
  2509. }
  2510. }
  2511. BaseClass::PlayerRoughLandingEffects( fvol );
  2512. }
  2513. #if 0
  2514. // Not being used currently - part of TestDuck!
  2515. //-----------------------------------------------------------------------------
  2516. // Purpose:
  2517. //-----------------------------------------------------------------------------
  2518. void CTFGameMovement::HandleDuck( int nButtonsPressed )
  2519. {
  2520. // XBOX SERVER ONLY
  2521. #if !defined(CLIENT_DLL)
  2522. if ( IsX360() && nButtonsPressed & IN_DUCK )
  2523. {
  2524. // Hinting logic
  2525. if ( player->GetToggledDuckState() && player->m_nNumCrouches < NUM_CROUCH_HINTS )
  2526. {
  2527. UTIL_HudHintText( player, "#Valve_Hint_Crouch" );
  2528. player->m_nNumCrouches++;
  2529. }
  2530. }
  2531. #endif
  2532. bool bInAir = ( player->GetGroundEntity() == NULL );
  2533. bool bInDuck = ( player->GetFlags() & FL_DUCKING ) ? true : false;
  2534. // Starting a duck.
  2535. if ( ( nButtonsPressed & IN_DUCK ) && !bInDuck )
  2536. {
  2537. if ( !player->m_Local.m_bDucking )
  2538. {
  2539. player->m_Local.m_flDucktime = TIME_TO_DUCK_MS;
  2540. player->m_Local.m_bDucking = true;
  2541. }
  2542. else
  2543. {
  2544. // Find unduck percentage and calcluate the duck time.
  2545. float flPercentage = player->m_Local.m_flDucktime / TIME_TO_UNDUCK_MS;
  2546. player->m_Local.m_flDucktime = TIME_TO_DUCK_MS * ( 1.0f - flPercentage );
  2547. }
  2548. if ( m_pTFPlayer->m_Shared.GetAirDash() > 0 )
  2549. {
  2550. m_pTFPlayer->DoAnimationEvent( PLAYERANIMEVENT_DOUBLEJUMP_CROUCH );
  2551. }
  2552. }
  2553. // Handle the ducking.
  2554. if ( player->m_Local.m_bDucking )
  2555. {
  2556. // Finish in duck transition when transition time is over, in "duck", in air.
  2557. if ( ( player->m_Local.m_flDucktime <= 0.0f ) || bInDuck || bInAir )
  2558. {
  2559. FinishDuck();
  2560. }
  2561. else
  2562. {
  2563. // Calculate the eye offset.
  2564. float flDuckFraction = SimpleSpline( 1.0f - ( player->m_Local.m_flDucktime / TIME_TO_DUCK_MS ) );
  2565. SetDuckedEyeOffset( flDuckFraction );
  2566. }
  2567. }
  2568. }
  2569. //-----------------------------------------------------------------------------
  2570. // Purpose:
  2571. //-----------------------------------------------------------------------------
  2572. void CTFGameMovement::HandleUnDuck( int nButtonsReleased )
  2573. {
  2574. if ( !player->m_Local.m_bAllowAutoMovement )
  2575. return;
  2576. bool bInAir = ( player->GetGroundEntity() == NULL );
  2577. bool bInDuck = ( player->GetFlags() & FL_DUCKING ) ? true : false;
  2578. // Ending a duck (or trying to).
  2579. if ( nButtonsReleased & IN_DUCK )
  2580. {
  2581. if ( bInDuck )
  2582. {
  2583. player->m_Local.m_flDucktime = TIME_TO_UNDUCK_MS;
  2584. player->m_Local.m_bDucking = true;
  2585. }
  2586. else if ( player->m_Local.m_bDucking )
  2587. {
  2588. // Find unduck percentage and calcluate the duck time.
  2589. float flPercentage = player->m_Local.m_flDucktime / TIME_TO_DUCK_MS;
  2590. player->m_Local.m_flDucktime = TIME_TO_UNDUCK_MS * ( 1.0f - flPercentage );
  2591. }
  2592. }
  2593. // Check to see if we are capable of unducking given our environment.
  2594. if ( CanUnduck() )
  2595. {
  2596. if ( ( player->m_Local.m_bDucking || player->m_Local.m_bDucked ) )
  2597. {
  2598. // We are unducking now.
  2599. player->m_Local.m_bDucking = true;
  2600. // Finish ducking immediately if duck time is over or we are in the air.
  2601. if ( player->m_Local.m_flDucktime <= 0.0f || bInAir )
  2602. {
  2603. FinishUnDuck();
  2604. }
  2605. else
  2606. {
  2607. // Calculate the eye offset.
  2608. float flDuckFraction = SimpleSpline( ( player->m_Local.m_flDucktime / TIME_TO_UNDUCK_MS ) );
  2609. SetDuckedEyeOffset( flDuckFraction );
  2610. }
  2611. }
  2612. }
  2613. else
  2614. {
  2615. // Under something where we cannot unduck - rest.
  2616. if ( player->m_Local.m_flDucktime != TIME_TO_UNDUCK_MS )
  2617. {
  2618. player->m_Local.m_flDucktime = TIME_TO_UNDUCK_MS;
  2619. player->m_Local.m_bDucked = true;
  2620. player->m_Local.m_bDucking = false;
  2621. player->AddFlag( FL_DUCKING );
  2622. // Reset the eye offset.
  2623. SetDuckedEyeOffset( 1.0f );
  2624. }
  2625. }
  2626. }
  2627. void CTFGameMovement::TestDuck( )
  2628. {
  2629. // Handle buttons.
  2630. int nButtonsChanged = ( mv->m_nOldButtons ^ mv->m_nButtons );
  2631. int nButtonsPressed = nButtonsChanged & mv->m_nButtons;
  2632. int nButtonsReleased = nButtonsChanged & mv->m_nOldButtons;
  2633. if ( mv->m_nButtons & IN_DUCK )
  2634. {
  2635. mv->m_nOldButtons |= IN_DUCK;
  2636. }
  2637. else
  2638. {
  2639. mv->m_nOldButtons &= ~IN_DUCK;
  2640. }
  2641. // Handle death.
  2642. if ( IsDead() )
  2643. return;
  2644. // Slow down ducked players.
  2645. HandleDuckingSpeedCrop();
  2646. // In some ducked state - button press to duck, duck transitions, or fully ducked.
  2647. bool bInDuck = ( player->GetFlags() & FL_DUCKING ) ? true : false;
  2648. if ( ( mv->m_nButtons & IN_DUCK ) || player->m_Local.m_bDucking || bInDuck )
  2649. {
  2650. // Duck State
  2651. if ( ( mv->m_nButtons & IN_DUCK ) )
  2652. {
  2653. HandleDuck( nButtonsPressed );
  2654. }
  2655. // Unduck State.
  2656. else
  2657. {
  2658. HandleUnDuck( nButtonsReleased );
  2659. }
  2660. }
  2661. }
  2662. #endif
  2663. //-----------------------------------------------------------------------------
  2664. // Purpose:
  2665. //-----------------------------------------------------------------------------
  2666. void CTFGameMovement::DuckOverrides()
  2667. {
  2668. bool bOnGround = ( player->GetGroundEntity() != NULL );
  2669. // Don't allowing ducking in water.
  2670. if ( ( ( player->GetWaterLevel() >= WL_Feet ) && !bOnGround ) ||
  2671. player->GetWaterLevel() >= WL_Eyes )
  2672. {
  2673. mv->m_nButtons &= ~IN_DUCK;
  2674. }
  2675. if ( !tf_clamp_airducks.GetBool() )
  2676. return;
  2677. // Check the duck timer and disable the duck button.
  2678. if ( gpGlobals->curtime < m_pTFPlayer->m_Shared.GetDuckTimer() && bOnGround )
  2679. {
  2680. mv->m_nButtons &= ~IN_DUCK;
  2681. }
  2682. // If we're trying to stand up, don't let the player try to re-duck. This
  2683. // prevents what the community calls the "Quantum Crouch". The above ducktimer
  2684. // covers most of the cases where users play nice and duck and unduck while standing.
  2685. // The "Quantum Crouch" occurs when users do the following:
  2686. // 0: Get a Dispenser or other waist-high platform in front of you
  2687. // 1: Press Jump + Crouch and move towards the platform
  2688. // 2: Release Crouch while jumping
  2689. // ( this causes the duck timer to start counting down )
  2690. // 3: Land on the platform
  2691. // 4: While starting to stand up, press Crouch
  2692. // ( when the duck timer finishes, your view will be locked )
  2693. // The intent of the duck timer is to require you to stand up after you've started
  2694. // to unduck and to throttle duck spamming. This just enforces the unduck
  2695. // requirement.
  2696. if ( player->m_Local.m_bDucked && player->m_Local.m_bDucking )
  2697. {
  2698. mv->m_nButtons &= ~IN_DUCK;
  2699. }
  2700. // Only allow one duck per air event.
  2701. if ( !bOnGround && m_pTFPlayer->m_Shared.AirDuckedCount() >= TF_AIRDUCKED_COUNT )
  2702. {
  2703. mv->m_nButtons &= ~IN_DUCK;
  2704. }
  2705. }
  2706. //-----------------------------------------------------------------------------
  2707. // Purpose:
  2708. //-----------------------------------------------------------------------------
  2709. void CTFGameMovement::OnDuck( int nButtonsPressed )
  2710. {
  2711. // Check to see if we are in the air or ducking.
  2712. bool bInAir = ( player->GetGroundEntity() == NULL );
  2713. bool bInDuck = ( player->GetFlags() & FL_DUCKING ) ? true : false;
  2714. // XBOX SERVER ONLY
  2715. #if !defined(CLIENT_DLL)
  2716. if ( IsX360() && nButtonsPressed & IN_DUCK )
  2717. {
  2718. // Hinting logic
  2719. if ( player->GetToggledDuckState() && player->m_nNumCrouches < NUM_CROUCH_HINTS )
  2720. {
  2721. UTIL_HudHintText( player, "#Valve_Hint_Crouch" );
  2722. player->m_nNumCrouches++;
  2723. }
  2724. }
  2725. #endif
  2726. // Have the duck button pressed, but the player currently isn't in the duck position.
  2727. if ( ( nButtonsPressed & IN_DUCK ) && !bInDuck )
  2728. {
  2729. player->m_Local.m_flDucktime = GAMEMOVEMENT_DUCK_TIME;
  2730. player->m_Local.m_bDucking = true;
  2731. if ( m_pTFPlayer->m_Shared.GetAirDash() > 0 )
  2732. {
  2733. m_pTFPlayer->DoAnimationEvent( PLAYERANIMEVENT_DOUBLEJUMP_CROUCH );
  2734. }
  2735. }
  2736. // The player is in duck transition and not duck-jumping.
  2737. if ( player->m_Local.m_bDucking )
  2738. {
  2739. float flDuckMilliseconds = MAX( 0.0f, GAMEMOVEMENT_DUCK_TIME - ( float )player->m_Local.m_flDucktime );
  2740. float flDuckSeconds = flDuckMilliseconds * 0.001f;
  2741. // Finish in duck transition when transition time is over, in "duck", in air.
  2742. if ( ( flDuckSeconds > TIME_TO_DUCK ) || bInDuck || bInAir )
  2743. {
  2744. FinishDuck();
  2745. }
  2746. else
  2747. {
  2748. // Calc parametric time
  2749. float flDuckFraction = SimpleSpline( flDuckSeconds / TIME_TO_DUCK );
  2750. SetDuckedEyeOffset( flDuckFraction );
  2751. }
  2752. }
  2753. }
  2754. //-----------------------------------------------------------------------------
  2755. // Purpose:
  2756. //-----------------------------------------------------------------------------
  2757. void CTFGameMovement::OnUnDuck( int nButtonsReleased )
  2758. {
  2759. // Check to see if we are in the air or ducking.
  2760. bool bInAir = ( player->GetGroundEntity() == NULL );
  2761. bool bInDuck = ( player->GetFlags() & FL_DUCKING ) ? true : false;
  2762. // Once the duck button is released, start a timer. The player will not be able to engage in a duck
  2763. // until the timer expires. In addition, set that we have ducked in air (will be allowed only once
  2764. // while in air).
  2765. if ( nButtonsReleased & IN_DUCK )
  2766. {
  2767. m_pTFPlayer->m_Shared.SetDuckTimer( gpGlobals->curtime + TF_TIME_TO_DUCK );
  2768. if ( bInAir )
  2769. {
  2770. // Increment the number of times we have ducked in air.
  2771. int nCount = m_pTFPlayer->m_Shared.AirDuckedCount() + 1;
  2772. m_pTFPlayer->m_Shared.SetAirDucked( nCount );
  2773. }
  2774. }
  2775. // Try to unduck unless automovement is not allowed
  2776. // NOTE: When not onground, you can always unduck
  2777. if ( player->m_Local.m_bAllowAutoMovement || bInAir || player->m_Local.m_bDucking )
  2778. {
  2779. // We released the duck button, we aren't in "duck" and we are not in the air - start unduck transition.
  2780. if ( ( nButtonsReleased & IN_DUCK ) )
  2781. {
  2782. if ( bInDuck )
  2783. {
  2784. player->m_Local.m_flDucktime = GAMEMOVEMENT_DUCK_TIME;
  2785. }
  2786. else if ( player->m_Local.m_bDucking && !player->m_Local.m_bDucked )
  2787. {
  2788. // Invert time if release before fully ducked!!!
  2789. float unduckMilliseconds = 1000.0f * TIME_TO_UNDUCK;
  2790. float duckMilliseconds = 1000.0f * TIME_TO_DUCK;
  2791. float elapsedMilliseconds = GAMEMOVEMENT_DUCK_TIME - player->m_Local.m_flDucktime;
  2792. float fracDucked = elapsedMilliseconds / duckMilliseconds;
  2793. float remainingUnduckMilliseconds = fracDucked * unduckMilliseconds;
  2794. player->m_Local.m_flDucktime = GAMEMOVEMENT_DUCK_TIME - unduckMilliseconds + remainingUnduckMilliseconds;
  2795. }
  2796. }
  2797. // Check to see if we are capable of unducking.
  2798. if ( CanUnduck() )
  2799. {
  2800. // or unducking
  2801. if ( ( player->m_Local.m_bDucking || player->m_Local.m_bDucked ) )
  2802. {
  2803. float flDuckMilliseconds = MAX( 0.0f, GAMEMOVEMENT_DUCK_TIME - (float)player->m_Local.m_flDucktime );
  2804. float flDuckSeconds = flDuckMilliseconds * 0.001f;
  2805. // Finish ducking immediately if duck time is over or not on ground
  2806. if ( flDuckSeconds > TIME_TO_UNDUCK || bInAir )
  2807. {
  2808. FinishUnDuck();
  2809. }
  2810. else
  2811. {
  2812. // Calc parametric time
  2813. float flDuckFraction = SimpleSpline( 1.0f - ( flDuckSeconds / TIME_TO_UNDUCK ) );
  2814. SetDuckedEyeOffset( flDuckFraction );
  2815. player->m_Local.m_bDucking = true;
  2816. }
  2817. }
  2818. }
  2819. else
  2820. {
  2821. // Still under something where we can't unduck, so make sure we reset this timer so
  2822. // that we'll unduck once we exit the tunnel, etc.
  2823. if ( player->m_Local.m_flDucktime != GAMEMOVEMENT_DUCK_TIME )
  2824. {
  2825. SetDuckedEyeOffset(1.0f);
  2826. player->m_Local.m_flDucktime = GAMEMOVEMENT_DUCK_TIME;
  2827. player->m_Local.m_bDucked = true;
  2828. player->m_Local.m_bDucking = false;
  2829. player->AddFlag( FL_DUCKING );
  2830. }
  2831. }
  2832. }
  2833. }
  2834. //-----------------------------------------------------------------------------
  2835. // Purpose: Crop the speed of the player when ducking and on the ground.
  2836. //-----------------------------------------------------------------------------
  2837. void CTFGameMovement::HandleDuckingSpeedCrop( void )
  2838. {
  2839. BaseClass::HandleDuckingSpeedCrop();
  2840. if ( m_iSpeedCropped & SPEED_CROPPED_DUCK )
  2841. {
  2842. if ( m_pTFPlayer->m_Shared.IsLoser() )
  2843. {
  2844. mv->m_flForwardMove *= 0;
  2845. mv->m_flSideMove *= 0;
  2846. mv->m_flUpMove *= 0;
  2847. }
  2848. }
  2849. }
  2850. //-----------------------------------------------------------------------------
  2851. // Purpose: See if duck button is pressed and do the appropriate things
  2852. //-----------------------------------------------------------------------------
  2853. void CTFGameMovement::Duck( void )
  2854. {
  2855. // Check duck overrides.
  2856. DuckOverrides();
  2857. // Calculate the button state.
  2858. int buttonsChanged = ( mv->m_nOldButtons ^ mv->m_nButtons ); // These buttons have changed this frame
  2859. int buttonsPressed = buttonsChanged & mv->m_nButtons; // The changed ones still down are "pressed"
  2860. int buttonsReleased = buttonsChanged & mv->m_nOldButtons; // The changed ones which were previously down are "released"
  2861. if ( mv->m_nButtons & IN_DUCK )
  2862. {
  2863. mv->m_nOldButtons |= IN_DUCK;
  2864. }
  2865. else
  2866. {
  2867. mv->m_nOldButtons &= ~IN_DUCK;
  2868. }
  2869. // Handle death.
  2870. if ( IsDead() )
  2871. {
  2872. // Reset view offset when dead
  2873. Vector vecStandViewOffset = GetPlayerViewOffset( false );
  2874. Vector vecOffset = player->GetViewOffset();
  2875. if ( vecOffset.z != vecStandViewOffset.z )
  2876. {
  2877. vecOffset.z = vecStandViewOffset.z;
  2878. player->SetViewOffset( vecOffset );
  2879. }
  2880. return;
  2881. }
  2882. // Slow down ducked players.
  2883. HandleDuckingSpeedCrop();
  2884. // If the player is holding down the duck button, the player is in duck transition, ducking, or duck-jumping.
  2885. bool bFirstTimePredicted = true; // Assumes we never rerun commands on the server.
  2886. #ifdef CLIENT_DLL
  2887. bFirstTimePredicted = prediction->IsFirstTimePredicted();
  2888. #endif
  2889. bool bInDuck = ( player->GetFlags() & FL_DUCKING ) ? true : false;
  2890. if ( ( mv->m_nButtons & IN_DUCK ) || player->m_Local.m_bDucking || bInDuck )
  2891. {
  2892. if ( ( mv->m_nButtons & IN_DUCK ) )
  2893. {
  2894. // DUCK
  2895. OnDuck( buttonsPressed );
  2896. }
  2897. else
  2898. {
  2899. // UNDUCK (or attempt to...)
  2900. OnUnDuck( buttonsReleased );
  2901. }
  2902. }
  2903. // HACK: (jimd 5/25/2006) we have a reoccuring bug (#50063 in Tracker) where the player's
  2904. // view height gets left at the ducked height while the player is standing, but we haven't
  2905. // been able to repro it to find the cause. It may be fixed now due to a change I'm
  2906. // also making in UpdateDuckJumpEyeOffset but just in case, this code will sense the
  2907. // problem and restore the eye to the proper position. It doesn't smooth the transition,
  2908. // but it is preferable to leaving the player's view too low.
  2909. //
  2910. // If the player is still alive and not an observer, check to make sure that
  2911. // his view height is at the standing height.
  2912. else if ( bFirstTimePredicted && !IsDead() && !player->IsObserver() && !player->IsInAVehicle() && !( TFGameRules() && TFGameRules()->ShowMatchSummary() ) )
  2913. {
  2914. float flOffsetDelta = player->GetViewOffset().z - GetPlayerViewOffset( false ).z;
  2915. if ( ( fabs( flOffsetDelta ) > 0.1 ) )
  2916. {
  2917. // we should rarely ever get here, so assert so a coder knows when it happens
  2918. AssertMsg2( 0, "Restoring player view height at %i %0.3f\n", gpGlobals->tickcount, gpGlobals->curtime );
  2919. DevMsg( 1, "Restoring player view height at %i %0.3f. Delta: %f.\n", gpGlobals->tickcount, gpGlobals->curtime, flOffsetDelta );
  2920. // set the eye height to the non-ducked height
  2921. SetDuckedEyeOffset(0.0f);
  2922. }
  2923. }
  2924. if ( tf_duck_debug_spew.GetBool() )
  2925. {
  2926. #ifdef GAME_DLL
  2927. engine->Con_NPrintf( 0, "SERVER" );
  2928. engine->Con_NPrintf( 1, "m_flDucktime %3.2f", player->m_Local.m_flDucktime.Get() );
  2929. engine->Con_NPrintf( 2, "m_flDuckJumpTime %3.2f", player->m_Local.m_flDuckJumpTime.Get() );
  2930. engine->Con_NPrintf( 3, "m_bDucked %d", player->m_Local.m_bDucked.Get() );
  2931. engine->Con_NPrintf( 4, "m_bDucking %d", player->m_Local.m_bDucking.Get() );
  2932. engine->Con_NPrintf( 5, "m_bInDuckJump %d", player->m_Local.m_bInDuckJump.Get() );
  2933. engine->Con_NPrintf( 6, "viewoffset %3.2f, %3.2f, %3.2f", player->GetViewOffset().x, player->GetViewOffset().y, player->GetViewOffset().z );
  2934. engine->Con_NPrintf( 7, "IN_DUCK %d", mv->m_nButtons & IN_DUCK );
  2935. engine->Con_NPrintf( 8, "GetDuckTimer %3.2f", Max( 0.f, m_pTFPlayer->m_Shared.GetDuckTimer() - gpGlobals->curtime ) );
  2936. #else
  2937. engine->Con_NPrintf( 10 + 0, "CLIENT" );
  2938. engine->Con_NPrintf( 10 + 1, "m_flDucktime %3.2f", player->m_Local.m_flDucktime );
  2939. engine->Con_NPrintf( 10 + 2, "m_flDuckJumpTime %3.2f", player->m_Local.m_flDuckJumpTime );
  2940. engine->Con_NPrintf( 10 + 3, "m_bDucked %d", player->m_Local.m_bDucked );
  2941. engine->Con_NPrintf( 10 + 4, "m_bDucking %d", player->m_Local.m_bDucking );
  2942. engine->Con_NPrintf( 10 + 5, "m_bInDuckJump %d", player->m_Local.m_bInDuckJump );
  2943. engine->Con_NPrintf( 10 + 6, "viewoffset %3.2f, %3.2f, %3.2f", player->GetViewOffset().x, player->GetViewOffset().y, player->GetViewOffset().z );
  2944. engine->Con_NPrintf( 10 + 7, "IN_DUCK %d", mv->m_nButtons & IN_DUCK );
  2945. engine->Con_NPrintf( 10 + 8, "GetDuckTimer %3.2f", Max( 0.f, m_pTFPlayer->m_Shared.GetDuckTimer() - gpGlobals->curtime ) );
  2946. #endif
  2947. }
  2948. }
  2949. #ifdef STAGING_ONLY
  2950. //-----------------------------------------------------------------------------
  2951. // Purpose: See if the player's double tapped movement keys
  2952. //-----------------------------------------------------------------------------
  2953. void CTFGameMovement::CheckForDoubleTap( void )
  2954. {
  2955. float flMaxDoubleTapTimeDelta = tf_movement_doubletap_window.GetFloat();
  2956. static const int aMoveType[4] =
  2957. {
  2958. IN_MOVELEFT,
  2959. IN_MOVERIGHT,
  2960. IN_FORWARD,
  2961. IN_BACK,
  2962. // Add movetypes here
  2963. };
  2964. for ( int i = 0; i < ARRAYSIZE( aMoveType ); ++i )
  2965. {
  2966. // Record when they let go of the key
  2967. if ( ( mv->m_nOldButtons & aMoveType[i] ) && !( mv->m_nButtons & aMoveType[i] ) )
  2968. {
  2969. int index = m_MoveKeyDownTimes.Find( aMoveType[i] );
  2970. if ( index != m_MoveKeyDownTimes.InvalidIndex() )
  2971. {
  2972. m_MoveKeyDownTimes[index] = gpGlobals->curtime;
  2973. }
  2974. else
  2975. {
  2976. // Init
  2977. m_MoveKeyDownTimes.Insert( aMoveType[i], gpGlobals->curtime );
  2978. }
  2979. }
  2980. // If the button is down now, and wasn't before...
  2981. else if ( ( mv->m_nButtons & aMoveType[i] ) && !( mv->m_nOldButtons & aMoveType[i] ) )
  2982. {
  2983. int index = m_MoveKeyDownTimes.Find( aMoveType[i] );
  2984. if ( index != m_MoveKeyDownTimes.InvalidIndex() )
  2985. {
  2986. // ...check the time delta - if it's within range, consider it a double-tap.
  2987. if ( gpGlobals->curtime - m_MoveKeyDownTimes[index] <= flMaxDoubleTapTimeDelta )
  2988. {
  2989. OnDoubleTapped( aMoveType[i] );
  2990. }
  2991. }
  2992. }
  2993. }
  2994. }
  2995. //-----------------------------------------------------------------------------
  2996. // Purpose: See if the player's double tapped movement keys
  2997. //-----------------------------------------------------------------------------
  2998. void CTFGameMovement::OnDoubleTapped( int nKey )
  2999. {
  3000. int iTeleportMove = 0;
  3001. CALL_ATTRIB_HOOK_INT_ON_OTHER( m_pTFPlayer, iTeleportMove, ability_doubletap_teleport );
  3002. if ( iTeleportMove )
  3003. {
  3004. Vector vecDir, vecForward, vecRight;
  3005. AngleVectors( m_pTFPlayer->GetAbsAngles(), &vecForward, &vecRight, NULL );
  3006. if ( nKey == IN_MOVELEFT )
  3007. {
  3008. vecRight.Negate();
  3009. TeleportMove( vecRight, 192.f );
  3010. }
  3011. else if ( nKey == IN_MOVERIGHT )
  3012. {
  3013. TeleportMove( vecRight, 192.f );
  3014. }
  3015. else if ( nKey == IN_FORWARD )
  3016. {
  3017. TeleportMove( vecForward, 192.f );
  3018. }
  3019. else if ( nKey == IN_BACK )
  3020. {
  3021. vecForward.Negate();
  3022. TeleportMove( vecForward, 192.f );
  3023. }
  3024. }
  3025. // DevMsg( "Double Tap! (%i)\n", nKey );
  3026. }
  3027. //-----------------------------------------------------------------------------
  3028. // Purpose:
  3029. //-----------------------------------------------------------------------------
  3030. void CTFGameMovement::TeleportMove( Vector &vecDirection, float flDist )
  3031. {
  3032. if ( m_flNextDoubleTapTeleportTime > gpGlobals->curtime )
  3033. return;
  3034. trace_t result;
  3035. CTraceFilterIgnoreTeammates traceFilter( m_pTFPlayer, COLLISION_GROUP_PLAYER_MOVEMENT, m_pTFPlayer->GetTeamNumber() );
  3036. unsigned int nMask = m_pTFPlayer->GetTeamNumber() == TF_TEAM_RED ? CONTENTS_BLUETEAM : CONTENTS_REDTEAM;
  3037. nMask |= MASK_PLAYERSOLID;
  3038. // Try full distance
  3039. Vector vecPos = mv->GetAbsOrigin() + vecDirection * flDist;
  3040. UTIL_TraceHull( mv->GetAbsOrigin(), vecPos, VEC_HULL_MIN_SCALED( m_pTFPlayer ), VEC_HULL_MAX_SCALED( m_pTFPlayer ), nMask, &traceFilter, &result );
  3041. if ( result.DidHit() )
  3042. {
  3043. if ( result.fraction <= 0.2f )
  3044. return;
  3045. vecPos = mv->GetAbsOrigin() + ( ( vecPos - mv->GetAbsOrigin() ) * result.fraction );
  3046. // NDebugOverlay::SweptBox( mv->GetAbsOrigin(), vecPos, VEC_HULL_MIN_SCALED( m_pTFPlayer ), VEC_HULL_MAX_SCALED( m_pTFPlayer ), m_pTFPlayer->GetAbsAngles(), 255, 0, 0, 40, 5.f );
  3047. }
  3048. // Go there
  3049. mv->SetAbsOrigin( vecPos );
  3050. #ifdef GAME_DLL
  3051. // Screen flash
  3052. color32 fadeColor = { 255, 255, 255, 50 };
  3053. UTIL_ScreenFade( m_pTFPlayer, fadeColor, 0.25f, 0.4f, FFADE_IN );
  3054. if ( TFGameRules() )
  3055. {
  3056. TFGameRules()->HaveAllPlayersSpeakConceptIfAllowed( MP_CONCEPT_PLAYER_SPELL_TELEPORT, ( m_pTFPlayer->GetTeamNumber() == TF_TEAM_RED ) ? TF_TEAM_BLUE : TF_TEAM_RED );
  3057. }
  3058. #endif // GAME_DLL
  3059. // Cooldown
  3060. m_flNextDoubleTapTeleportTime = gpGlobals->curtime + 2.f;
  3061. }
  3062. #endif // STAGING_ONLY