Counter Strike : Global Offensive Source Code
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.

5245 lines
187 KiB

  1. //========= Copyright (c) 1996-2005, Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: Special handling for Portal usable ladders
  4. //
  5. //=============================================================================//
  6. #include "cbase.h"
  7. #include "portal_gamemovement.h"
  8. #include "in_buttons.h"
  9. #include "utlrbtree.h"
  10. #include "movevars_shared.h"
  11. #include "portal_shareddefs.h"
  12. #include "portal_collideable_enumerator.h"
  13. #include "portal_base2d_shared.h"
  14. #include "rumble_shared.h"
  15. #include "portal_mp_gamerules.h"
  16. #include "tier0/stacktools.h"
  17. #include "portal_util_shared.h"
  18. #include "iclient.h"
  19. #if defined( CLIENT_DLL )
  20. #include "c_portal_player.h"
  21. #include "c_rumble.h"
  22. #include "prediction.h"
  23. #include "c_weapon_portalgun.h"
  24. #include "c_projectedwallentity.h"
  25. #define CRecipientFilter C_RecipientFilter
  26. #else
  27. #include "portal_player.h"
  28. #include "env_player_surface_trigger.h"
  29. #include "portal_gamestats.h"
  30. #include "physicsshadowclone.h"
  31. #include "recipientfilter.h"
  32. #include "SoundEmitterSystem/isoundemittersystembase.h"
  33. #include "weapon_portalgun.h"
  34. #include "projectedwallentity.h"
  35. #include "paint_power_info.h"
  36. #include "particle_parse.h"
  37. #endif
  38. #include "coordsize.h" // for DIST_EPSILON
  39. // memdbgon must be the last include file in a .cpp file!!!
  40. #include "tier0/memdbgon.h"
  41. ConVar sv_player_trace_through_portals("sv_player_trace_through_portals", "1", FCVAR_REPLICATED | FCVAR_CHEAT, "Causes player movement traces to trace through portals." );
  42. ConVar sv_player_funnel_into_portals("sv_player_funnel_into_portals", "1", FCVAR_REPLICATED, "Causes the player to auto correct toward the center of floor portals." );
  43. ConVar sv_player_funnel_snap_threshold("sv_player_funnel_snap_threshold", "10.f", FCVAR_REPLICATED);
  44. ConVar sv_player_funnel_speed_bonus("sv_player_funnel_speed_bonus", "2.f", FCVAR_REPLICATED | FCVAR_CHEAT);
  45. ConVar sv_player_well_above_height("sv_player_funnel_well_above", "256.f", FCVAR_REPLICATED);
  46. ConVar sv_player_funnel_height_adjust("sv_player_funnel_height_adjust", "128.f", FCVAR_REPLICATED);
  47. ConVar sv_player_funnel_gimme_dot("sv_player_funnel_gimme_dot", "0.9", FCVAR_REPLICATED);
  48. // Convars for paint powerups
  49. ConVar sv_speed_normal("sv_speed_normal", "175.f", FCVAR_REPLICATED | FCVAR_CHEAT, "For tweaking the normal speed when off speed paint.");
  50. ConVar sv_speed_paint_max("sv_speed_paint_max", "800.0f", FCVAR_REPLICATED | FCVAR_CHEAT, "For tweaking the max speed for speed paint.");
  51. ConVar sv_speed_paint_side_move_factor("sv_speed_paint_side_move_factor", "0.5f", FCVAR_REPLICATED | FCVAR_CHEAT);
  52. ConVar speed_funnelling_enabled("speed_funnelling_enabled", "1", FCVAR_REPLICATED, "Toggle whether the player is funneled into portals while running on speed paint.");
  53. ConVar sv_paintairacceleration("sv_paintairacceleration", "5.0f", FCVAR_REPLICATED | FCVAR_CHEAT, "Air acceleration in Paint");
  54. ConVar eggbot_sink_speed("eggbot_sink_speed", "120.0f", FCVAR_REPLICATED | FCVAR_CHEAT | FCVAR_DEVELOPMENTONLY );
  55. ConVar ballbot_sink_speed("ballbot_sink_speed", "120.0f", FCVAR_REPLICATED | FCVAR_CHEAT | FCVAR_DEVELOPMENTONLY );
  56. ConVar coop_sink_speed_decay("coop_sink_speed_decay","0.02f", FCVAR_REPLICATED | FCVAR_CHEAT | FCVAR_DEVELOPMENTONLY );
  57. ConVar coop_impact_velocity_threshold("coop_impact_velocity_threshold", "250.0", FCVAR_REPLICATED | FCVAR_CHEAT );
  58. #define sv_can_carry_both_guns 0 //extern ConVar sv_can_carry_both_guns;
  59. ConVar sv_portal_new_player_trace( "sv_portal_new_player_trace", "1", FCVAR_REPLICATED | FCVAR_CHEAT );
  60. ConVar portal_funnel_debug( "portal_funnel_debug", "0", FCVAR_REPLICATED | FCVAR_CHEAT );
  61. //DIST_EPSILON == 0.03125
  62. ConVar portal_player_interaction_quadtest_epsilon( "portal_player_interaction_quadtest_epsilon", "-0.03125", FCVAR_REPLICATED | FCVAR_CHEAT );
  63. #if defined( CLIENT_DLL )
  64. ConVar cl_vertical_elevator_fix( "cl_vertical_elevator_fix", "1" );
  65. #endif
  66. class CReservePlayerSpot;
  67. #define PORTAL_FUNNEL_AMOUNT 6.0f
  68. #define MAX_CLIP_PLANES 5
  69. //#define DEBUG_FLINGS
  70. // Roughly how often we want to update the info about the ground surface we're on.
  71. // We don't need to do this very often.
  72. #define CATEGORIZE_GROUND_SURFACE_INTERVAL 0.3f
  73. #define CATEGORIZE_GROUND_SURFACE_TICK_INTERVAL ( (int)( CATEGORIZE_GROUND_SURFACE_INTERVAL / TICK_INTERVAL ) )
  74. #define CHECK_STUCK_INTERVAL 1.0f
  75. #define CHECK_STUCK_TICK_INTERVAL ( (int)( CHECK_STUCK_INTERVAL / TICK_INTERVAL ) )
  76. #define CHECK_STUCK_INTERVAL_SP 0.2f
  77. #define CHECK_STUCK_TICK_INTERVAL_SP ( (int)( CHECK_STUCK_INTERVAL_SP / TICK_INTERVAL ) )
  78. #define CHECK_LADDER_INTERVAL 0.2f
  79. #define CHECK_LADDER_TICK_INTERVAL ( (int)( CHECK_LADDER_INTERVAL / TICK_INTERVAL ) )
  80. #define NUM_CROUCH_HINTS 3
  81. #define CRITICAL_SLOPE 0.7f
  82. #define PLAYER_FLING_HELPER_MIN_SPEED 200.0f
  83. const float COS_PI_OVER_SIX = 0.86602540378443864676372317075294f; // cos( 30 degrees ) in radians
  84. extern bool g_bMovementOptimizations;
  85. extern bool g_bAllowForcePortalTrace;
  86. extern bool g_bForcePortalTrace;
  87. // Unnamed namespace is the C++ way of specifying static at file scope.
  88. // It prevents this function from leaking out of this translation unit.
  89. namespace
  90. {
  91. inline void DoTrace( ITraceListData *pTraceListData, const Ray_t &ray, uint32 fMask, ITraceFilter *filter, trace_t *ptr, int *counter )
  92. {
  93. ++*counter;
  94. if ( pTraceListData && pTraceListData->CanTraceRay(ray) )
  95. {
  96. enginetrace->TraceRayAgainstLeafAndEntityList( ray, pTraceListData, fMask, filter, ptr );
  97. }
  98. else
  99. {
  100. enginetrace->TraceRay( ray, fMask, filter, ptr );
  101. }
  102. }
  103. } // Unnamed namespace
  104. #if defined( DEBUG_FLINGS )
  105. class CDebugCrouchOverlay : public CAutoGameSystemPerFrame
  106. {
  107. public:
  108. CDebugCrouchOverlay( void )
  109. {
  110. pPlayer = NULL;
  111. pPortal = NULL;
  112. vCenterNudge = vTeleportVel = vTeleportPos = vUnduckVel = vUnduckPos = vUnduckJumpPos = vUnduckJumpVel = vec3_origin;
  113. bWatchFlingVelocity = false;
  114. }
  115. #if defined( CLIENT_DLL )
  116. virtual void PreRender ()
  117. #else
  118. virtual void PreClientUpdate()
  119. #endif
  120. {
  121. if( pPlayer )
  122. {
  123. Vector vDuckMins = pPlayer->GetDuckHullMins();
  124. Vector vDuckMaxs = pPlayer->GetDuckHullMaxs();
  125. Vector vDuckExtents = (vDuckMaxs - vDuckMins) * 0.5f;
  126. Vector vStandMins = pPlayer->GetStandHullMins();
  127. Vector vStandMaxs = pPlayer->GetStandHullMaxs();
  128. Vector vStandExtents = (vStandMaxs - vStandMins) * 0.5f;
  129. if( vTeleportPos != vec3_origin )
  130. {
  131. NDebugOverlay::Box( vTeleportPos, -vTeleportExtents, vTeleportExtents, 255, 0, 0, 100, 0.0f );
  132. NDebugOverlay::HorzArrow( vTeleportPos, vTeleportPos + vTeleportVel * 2.0f, 2.0f, 255, 0, 0, 100, true, 0.0f );
  133. if( vCenterNudge != vec3_origin )
  134. {
  135. NDebugOverlay::HorzArrow( vTeleportPos - vCenterNudge, vTeleportPos, 2.0f, 0, 255, 0, 100, true, 0.0f );
  136. }
  137. }
  138. if( vUnduckPos != vec3_origin )
  139. {
  140. NDebugOverlay::Box( vUnduckPos, -vStandExtents, vStandExtents, 0, 255, 0, 100, 0.0f );
  141. NDebugOverlay::HorzArrow( vUnduckPos, vUnduckPos + vUnduckVel * 2.0f, 2.0f, 0, 255, 0, 100, true, 0.0f );
  142. }
  143. if( vUnduckJumpPos != vec3_origin )
  144. {
  145. NDebugOverlay::Box( vUnduckJumpPos, -vStandExtents, vStandExtents, 0, 0, 255, 100, 0.0f );
  146. NDebugOverlay::HorzArrow( vUnduckJumpPos, vUnduckJumpPos + vUnduckJumpVel * 2.0f, 2.0f, 0, 0, 255, 100, true, 0.0f );
  147. }
  148. }
  149. }
  150. CPortal_Base2D *pPortal;
  151. CPortal_Player *pPlayer;
  152. Vector vTeleportPos;
  153. Vector vTeleportVel;
  154. Vector vTeleportExtents;
  155. Vector vCenterNudge;
  156. Vector vUnduckPos;
  157. Vector vUnduckVel;
  158. Vector vUnduckJumpPos;
  159. Vector vUnduckJumpVel;
  160. bool bWatchFlingVelocity;
  161. };
  162. static CDebugCrouchOverlay s_MovementDebug;
  163. #endif
  164. //trace that has special understanding of how to handle portals
  165. CTrace_PlayerAABB_vs_Portals::CTrace_PlayerAABB_vs_Portals( void )
  166. {
  167. UTIL_ClearTrace( *this );
  168. m_bContactedPortalTransitionRamp = false;
  169. }
  170. bool CTrace_PlayerAABB_vs_Portals::HitPortalRamp( const Vector &vUp )
  171. {
  172. return sv_portal_new_player_trace.GetBool() && (m_bContactedPortalTransitionRamp && DidHit() && (plane.normal.Dot( vUp ) > 0.0f));
  173. }
  174. void PortalTracePlayerBBoxForGround( const Vector& start, const Vector& end, const Vector& minsSrc,
  175. const Vector& maxsSrc, IHandleEntity *player, unsigned int fMask,
  176. int collisionGroup, CTrace_PlayerAABB_vs_Portals& pm, const Vector& vStickNormal );
  177. static inline CBaseEntity *TranslateGroundEntity( CBaseEntity *pGroundEntity )
  178. {
  179. #ifndef CLIENT_DLL
  180. CPhysicsShadowClone *pClone = dynamic_cast<CPhysicsShadowClone *>(pGroundEntity);
  181. if( pClone && pClone->IsUntransformedClone() )
  182. {
  183. CBaseEntity *pSource = pClone->GetClonedEntity();
  184. if( pSource )
  185. return pSource;
  186. }
  187. #endif //#ifndef CLIENT_DLL
  188. return pGroundEntity;
  189. }
  190. //should force player to crouch for this transition
  191. bool ShouldPortalTransitionCrouch( const CPortal_Base2D *pEnterPortal )
  192. {
  193. const float flUpDot = fabs( pEnterPortal->m_matrixThisToLinked.m[2][2] ); //How much does zUp still look like zUp after going through this portal
  194. return (flUpDot < COS_PI_OVER_SIX && (sv_portal_new_player_trace.GetBool() || (flUpDot >= EQUAL_EPSILON)));
  195. }
  196. //if player is already crouched, do NOT automatically uncrouch. You don't actually have to check that the player is exiting the portal, but we assume that's the intent
  197. bool ShouldMaintainFlingAssistCrouch( const CPortal_Base2D *pExitPortal, const Vector &vExitVelocity )
  198. {
  199. return (pExitPortal->m_vForward.z > 0.1f && pExitPortal->m_vForward.z < 0.9) && (vExitVelocity.z > 1.0f) && (vExitVelocity.Dot( pExitPortal->m_vForward ) > PLAYER_FLING_HELPER_MIN_SPEED);
  200. }
  201. #if defined( CLIENT_DLL )
  202. void CPortalGameMovement::ClientVerticalElevatorFixes( CBasePlayer *pPlayer, CMoveData *pMove )
  203. {
  204. //find root move parent of our ground entity
  205. CBaseEntity *pRootMoveParent = pPlayer->GetGroundEntity();
  206. while( pRootMoveParent )
  207. {
  208. C_BaseEntity *pTestParent = pRootMoveParent->GetMoveParent();
  209. if( !pTestParent )
  210. break;
  211. pRootMoveParent = pTestParent;
  212. }
  213. //if it's a C_BaseToggle (func_movelinear / func_door) then enable prediction if it chooses to
  214. bool bRootMoveParentIsLinearMovingBaseToggle = false;
  215. bool bAdjustedRootZ = false;
  216. if( pRootMoveParent && !pRootMoveParent->IsWorld() )
  217. {
  218. C_BaseToggle *pPredictableGroundEntity = dynamic_cast<C_BaseToggle *>(pRootMoveParent);
  219. if( pPredictableGroundEntity && (pPredictableGroundEntity->m_movementType == MOVE_TOGGLE_LINEAR) )
  220. {
  221. bRootMoveParentIsLinearMovingBaseToggle = true;
  222. if( !pPredictableGroundEntity->GetPredictable() )
  223. {
  224. pPredictableGroundEntity->SetPredictionEligible( true );
  225. pPredictableGroundEntity->m_hPredictionOwner = pPlayer;
  226. }
  227. else if( cl_vertical_elevator_fix.GetBool() )
  228. {
  229. Vector vNewOrigin = pPredictableGroundEntity->PredictPosition( player->PredictedServerTime() + TICK_INTERVAL );
  230. if( (vNewOrigin - pPredictableGroundEntity->GetLocalOrigin()).LengthSqr() > 0.01f )
  231. {
  232. bAdjustedRootZ = (vNewOrigin.z != pPredictableGroundEntity->GetLocalOrigin().z);
  233. pPredictableGroundEntity->SetLocalOrigin( vNewOrigin );
  234. //invalidate abs transforms for upcoming traces
  235. C_BaseEntity *pParent = pPlayer->GetGroundEntity();
  236. while( pParent )
  237. {
  238. pParent->AddEFlags( EFL_DIRTY_ABSTRANSFORM );
  239. pParent = pParent->GetMoveParent();
  240. }
  241. }
  242. }
  243. }
  244. }
  245. //re-seat player on vertical elevators
  246. if( bRootMoveParentIsLinearMovingBaseToggle &&
  247. cl_vertical_elevator_fix.GetBool() &&
  248. bAdjustedRootZ )
  249. {
  250. trace_t trElevator;
  251. TracePlayerBBox( pMove->GetAbsOrigin(), pMove->GetAbsOrigin() - Vector( 0.0f, 0.0f, GetPlayerMaxs().z ), MASK_PLAYERSOLID, COLLISION_GROUP_PLAYER_MOVEMENT, trElevator );
  252. if( trElevator.startsolid )
  253. {
  254. //started in solid, and we think it's an elevator. Pop up the player if at all possible
  255. //trace up, ignoring the ground entity hierarchy
  256. Ray_t playerRay;
  257. playerRay.Init( pMove->GetAbsOrigin(), pMove->GetAbsOrigin() + Vector( 0.0f, 0.0f, GetPlayerMaxs().z ), GetPlayerMins(), GetPlayerMaxs() );
  258. CTraceFilterSimpleList ignoreGroundEntityHeirarchy( COLLISION_GROUP_PLAYER_MOVEMENT );
  259. {
  260. ignoreGroundEntityHeirarchy.AddEntityToIgnore( pPlayer );
  261. C_BaseEntity *pParent = pPlayer->GetGroundEntity();
  262. while( pParent )
  263. {
  264. ignoreGroundEntityHeirarchy.AddEntityToIgnore( pParent );
  265. pParent = pParent->GetMoveParent();
  266. }
  267. }
  268. enginetrace->TraceRay( playerRay, MASK_PLAYERSOLID, &ignoreGroundEntityHeirarchy, &trElevator );
  269. if( !trElevator.startsolid ) //success
  270. {
  271. //now trace back down
  272. Vector vStart = trElevator.endpos;
  273. TracePlayerBBox( vStart, pMove->GetAbsOrigin(), MASK_PLAYERSOLID, COLLISION_GROUP_PLAYER_MOVEMENT, trElevator );
  274. if( !trElevator.startsolid &&
  275. (trElevator.m_pEnt == pPlayer->GetGroundEntity()) )
  276. {
  277. //if we landed back on the ground entity, call it good
  278. pMove->SetAbsOrigin( trElevator.endpos );
  279. pPlayer->SetNetworkOrigin( trElevator.endpos ); //paint code loads from network origin after handling paint powers
  280. }
  281. }
  282. }
  283. else if( (trElevator.endpos.z < pMove->GetAbsOrigin().z) && (trElevator.m_pEnt == pPlayer->GetGroundEntity()) )
  284. {
  285. //re-seat on ground entity
  286. pMove->SetAbsOrigin( trElevator.endpos );
  287. pPlayer->SetNetworkOrigin( trElevator.endpos ); //paint code loads from network origin after handling paint powers
  288. }
  289. }
  290. }
  291. #endif
  292. //-----------------------------------------------------------------------------
  293. // Purpose:
  294. //-----------------------------------------------------------------------------
  295. CPortalGameMovement::CPortalGameMovement()
  296. {
  297. }
  298. //-----------------------------------------------------------------------------
  299. // Purpose:
  300. //-----------------------------------------------------------------------------
  301. inline CPortal_Player* CPortalGameMovement::GetPortalPlayer() const
  302. {
  303. return assert_cast< CPortal_Player* >( player );
  304. }
  305. //-----------------------------------------------------------------------------
  306. // Purpose:
  307. // Input : *pMove -
  308. //-----------------------------------------------------------------------------
  309. void CPortalGameMovement::ProcessMovement( CBasePlayer *pPlayer, CMoveData *pMove )
  310. {
  311. AssertMsg( pPlayer && pMove, "Null pointers are bad, and you should feel bad." );
  312. if ( pPlayer && pMove )
  313. {
  314. float flStoreFrametime = gpGlobals->frametime;
  315. // Save the current paint player and move data
  316. player = pPlayer;
  317. mv = pMove;
  318. #if defined( CLIENT_DLL )
  319. ClientVerticalElevatorFixes( pPlayer, pMove ); //fixup vertical elevator discrepancies between client and server as best we can
  320. #endif
  321. m_vMoveStartPosition = mv->GetAbsOrigin();
  322. // Get the paint player
  323. CPortal_Player *pPlayer = GetPortalPlayer();
  324. //!!HACK HACK: Adrian - slow down all player movement by this factor.
  325. //!!Blame Yahn for this one.
  326. gpGlobals->frametime *= pPlayer->GetLaggedMovementValue();
  327. // Reset point contents for water check.
  328. ResetGetWaterContentsForPointCache();
  329. // Cropping movement speed scales mv->m_fForwardSpeed etc. globally
  330. // Once we crop, we don't want to recursively crop again, so we set the crop
  331. // flag globally here once per usercmd cycle.
  332. m_bSpeedCropped = false;
  333. m_bInPortalEnv = (((CPortal_Player *)pPlayer)->m_hPortalEnvironment != NULL);
  334. g_bAllowForcePortalTrace = m_bInPortalEnv;
  335. g_bForcePortalTrace = m_bInPortalEnv;
  336. // Figure out move direction
  337. const Vector& stickNormal = pPlayer->GetPortalPlayerLocalData().m_StickNormal;
  338. Vector vForward, vRight;
  339. Vector vPlayerUp = pPlayer->GetPortalPlayerLocalData().m_Up;
  340. AngleVectors( mv->m_vecViewAngles, &vForward, &vRight, NULL ); // Determine movement angles
  341. const Vector worldUp( 0, 0, 1 );
  342. bool shouldProjectInputVectorsOntoGround = pPlayer->GetGroundEntity() != NULL;
  343. if( shouldProjectInputVectorsOntoGround )
  344. {
  345. vForward -= DotProduct( vForward, stickNormal ) * stickNormal;
  346. vRight -= DotProduct( vRight, stickNormal ) * stickNormal;
  347. vForward.NormalizeInPlace();
  348. vRight.NormalizeInPlace();
  349. }
  350. Vector vWishVel = vForward*mv->m_flForwardMove + vRight*mv->m_flSideMove;
  351. vWishVel -= vPlayerUp * DotProduct( vWishVel, vPlayerUp );
  352. // Make sure the gravity direction is the negation of the current stick normal
  353. // Note: This is cached off before the powers are used only because, if stick
  354. // deactivates because of a jump, the jump direction must be the surface
  355. // (stick) normal.
  356. m_vGravityDirection = -stickNormal;
  357. // Figure out paint power
  358. pPlayer->SetInputVector( vWishVel );
  359. pPlayer->UpdatePaintPowers();
  360. // Using the paint power may change the velocity
  361. mv->m_vecVelocity = player->GetAbsVelocity();
  362. // The stick power may cause the abs origin and collision bounds to change
  363. #if defined( GAME_DLL )
  364. mv->SetAbsOrigin( player->GetAbsOrigin() );
  365. #else
  366. mv->SetAbsOrigin( player->GetNetworkOrigin() ); //GetAbsOrigin() introduces prediction delay
  367. #endif
  368. // If the player is sticking this frame, make sure she stays on the surface
  369. // Note: Currently, the only case where stick is the power and the player is
  370. // not on the surface is when we expand the box to try to find sticky
  371. // surfaces immediately after teleporting.
  372. //StayStuckToSurface();
  373. // Use this player's max speed (dependent on whether he's on speed paint)
  374. const float maxSpeed = pPlayer->MaxSpeed();
  375. mv->m_flClientMaxSpeed = mv->m_flMaxSpeed = maxSpeed;
  376. // Run the command.
  377. PlayerMove();
  378. HandlePortalling();
  379. FinishMove();
  380. //if( gpGlobals->frametime != 0.0f )
  381. //{
  382. // DevMsg( "Max Speed: %f\n", maxSpeed );
  383. // DevMsg( "Speed: %f\n", mv->m_vecVelocity.Length() );
  384. // DevMsg( "XY Speed: %f\n", mv->m_vecVelocity.Length2D() );
  385. // DevMsg( "XY Player Speed: %f\n", player->GetLocalVelocity().Length2D() );
  386. // DevMsg( "Eye Offset: (%f, %f, %f)\n", XYZ( pPlayer->GetViewOffset() ) );
  387. // DevMsg( "Final Player Velocity: (%f, %f, %f)\n", XYZ( player->GetAbsVelocity() ) );
  388. //}
  389. #ifndef CLIENT_DLL
  390. pPlayer->UnforceButtons( IN_DUCK );
  391. pPlayer->UnforceButtons( IN_JUMP );
  392. // Try to collide with thrown weapons if we don't currently have a weapon
  393. if( pPlayer->HasWeapons() == false || sv_can_carry_both_guns )
  394. {
  395. // Trace for the portal and paint guns
  396. trace_t pm;
  397. TracePlayerBBox( player->GetAbsOrigin(), player->GetAbsOrigin() + gpGlobals->frametime * player->GetAbsVelocity(), MASK_PLAYERSOLID, COLLISION_GROUP_WEAPON, pm );
  398. if ( pm.m_pEnt && ( FClassnameIs( pm.m_pEnt, "weapon_portalgun" ) || FClassnameIs( pm.m_pEnt, "weapon_paintgun" ) ) )
  399. {
  400. CBasePortalCombatWeapon *pWeap = dynamic_cast< CBasePortalCombatWeapon* >( pm.m_pEnt );
  401. // Only pick up the weapon if there's been enough time since it was thrown
  402. if ( pWeap && pWeap->EnoughTimeSinceThrown() )
  403. {
  404. pPlayer->BumpWeapon( pWeap );
  405. CWeaponPortalgun* pPortalgun = dynamic_cast< CWeaponPortalgun* >( pWeap );
  406. unsigned char iPortalLinkage = 0;
  407. if( pPortalgun )
  408. {
  409. iPortalLinkage = pPortalgun->GetLinkageGroupID();
  410. }
  411. pWeap->OnPickedUp( pPlayer ); // We bump into weapons instead of weapons bumping into us, so call OnPickedUp on the gun's behalf
  412. if( pPortalgun )
  413. {
  414. pPortalgun->SetLinkageGroupID( iPortalLinkage );
  415. }
  416. }
  417. }
  418. }
  419. #endif
  420. //This is probably not needed, but just in case.
  421. gpGlobals->frametime = flStoreFrametime;
  422. }
  423. }
  424. //-----------------------------------------------------------------------------
  425. // Purpose: Base jump behavior, plus an anim event
  426. // Input : -
  427. // Output : Returns true on success, false on failure.
  428. //-----------------------------------------------------------------------------
  429. bool CPortalGameMovement::CheckJumpButton()
  430. {
  431. if (player->pl.deadflag)
  432. return false;
  433. // Cannot jump will ducked.
  434. if ( player->GetFlags() & FL_DUCKING )
  435. return false;
  436. // No more effect
  437. if (player->GetGroundEntity() == NULL || player->GetGroundEntity()->IsPlayer() )
  438. {
  439. mv->m_nOldButtons |= IN_JUMP;
  440. return false; // in air, so no effect
  441. }
  442. if ( mv->m_nOldButtons & IN_JUMP )
  443. return false; // don't pogo stick
  444. // Cannot jump will in the unduck transition.
  445. if ( player->m_Local.m_bDucking && ( player->GetFlags() & FL_DUCKING ) )
  446. return false;
  447. // Still updating the eye position.
  448. if ( player->m_Local.m_nDuckJumpTimeMsecs > 0 )
  449. return false;
  450. // Start jump animation and player sound (specific TF animation and flags).
  451. GetPortalPlayer()->DoAnimationEvent( PLAYERANIMEVENT_JUMP, 0 );
  452. player->PlayStepSound( (Vector &)mv->GetAbsOrigin(), player->m_pSurfaceData, 1.0, true );
  453. // In the air now.
  454. GetPortalPlayer()->SetJumpedThisFrame( true );
  455. SetGroundEntity( NULL );
  456. player->PlayStepSound( (Vector &)mv->GetAbsOrigin(), player->m_pSurfaceData, 1.0, true );
  457. MoveHelper()->PlayerSetAnimation( PLAYER_JUMP );
  458. float flGroundFactor = 1.0f;
  459. if (player->m_pSurfaceData)
  460. {
  461. flGroundFactor = player->m_pSurfaceData->game.jumpFactor;
  462. }
  463. float flMul = sqrt( 2 * sv_gravity.GetFloat() * 45.f );
  464. // Acclerate upward
  465. // If we are ducking...
  466. Vector vPreJumpVel = mv->m_vecVelocity;
  467. if ( ( player->m_Local.m_bDucking ) || ( player->GetFlags() & FL_DUCKING ) )
  468. {
  469. // d = 0.5 * g * t^2 - distance traveled with linear accel
  470. // t = sqrt(2.0 * 45 / g) - how long to fall 45 units
  471. // v = g * t - velocity at the end (just invert it to jump up that high)
  472. // v = g * sqrt(2.0 * 45 / g )
  473. // v^2 = g * g * 2.0 * 45 / g
  474. // v = sqrt( g * 2.0 * 45 )
  475. //mv->m_vecVelocity[2] = flGroundFactor * flMul; // 2 * gravity * height
  476. mv->m_vecVelocity -= m_vGravityDirection * DotProduct( mv->m_vecVelocity, m_vGravityDirection );
  477. mv->m_vecVelocity -= m_vGravityDirection * flGroundFactor * flMul;
  478. }
  479. else
  480. {
  481. //mv->m_vecVelocity[2] += flGroundFactor * flMul; // 2 * gravity * height
  482. mv->m_vecVelocity -= m_vGravityDirection * flGroundFactor * flMul;
  483. }
  484. FinishGravity();
  485. mv->m_outJumpVel += mv->m_vecVelocity - vPreJumpVel;
  486. mv->m_outStepHeight += 0.15f;
  487. #if !defined( PORTAL2 )
  488. bool bSetDuckJump = (gpGlobals->maxClients == 1); //most games we only set duck jump if the game is single player
  489. #else
  490. //const bool bSetDuckJump = true; //in portal 2, do it for both single and multiplayer
  491. const bool bSetDuckJump = false; // FIX THIS?: This is set to false as a temp fix for camera snapping when ducking in the air due to player maintain the center of the box ( NO DUCKJUMP for now )
  492. #endif
  493. // Set jump time.
  494. if ( bSetDuckJump )
  495. {
  496. player->m_Local.m_nJumpTimeMsecs = GAMEMOVEMENT_JUMP_TIME;
  497. player->m_Local.m_bInDuckJump = true;
  498. }
  499. // Flag that we jumped.
  500. mv->m_nOldButtons |= IN_JUMP; // don't jump again until released
  501. return true;
  502. }
  503. //-----------------------------------------------------------------------------
  504. // Purpose:
  505. // Input : wishdir -
  506. // accel -
  507. //-----------------------------------------------------------------------------
  508. void CPortalGameMovement::AirAccelerate( Vector& wishdir, float wishspeed, float accel )
  509. {
  510. int i;
  511. float addspeed, accelspeed, currentspeed;
  512. float wishspd;
  513. wishspd = wishspeed;
  514. if (player->pl.deadflag)
  515. return;
  516. if (player->m_flWaterJumpTime)
  517. return;
  518. // Cap speed
  519. if (wishspd > 60.0f)
  520. wishspd = 60.0f;
  521. // Determine veer amount
  522. currentspeed = mv->m_vecVelocity.Dot(wishdir);
  523. // See how much to add
  524. addspeed = wishspd - currentspeed;
  525. #if defined CLIENT_DLL
  526. RANDOM_CEG_TEST_SECRET();
  527. #endif
  528. // If not adding any, done.
  529. if (addspeed <= 0)
  530. return;
  531. CPortal_Player* pPortalPlayer = GetPortalPlayer();
  532. if( pPortalPlayer == NULL )
  533. return;
  534. float fInputScale = pPortalPlayer->GetPortalPlayerLocalData().m_flAirInputScale;
  535. // Determine acceleration speed after acceleration
  536. accelspeed = accel * wishspeed * gpGlobals->frametime * player->m_surfaceFriction * fInputScale;
  537. // Cap it
  538. if (accelspeed > addspeed)
  539. accelspeed = addspeed;
  540. // Adjust pmove vel.
  541. for (i=0 ; i<3 ; i++)
  542. {
  543. mv->m_vecVelocity[i] += accelspeed * wishdir[i];
  544. mv->m_outWishVel[i] += accelspeed * wishdir[i];
  545. }
  546. }
  547. void CPortalGameMovement::TBeamMove( void )
  548. {
  549. CPortal_Player *pPortalPlayer = GetPortalPlayer();
  550. if ( !pPortalPlayer )
  551. return;
  552. CTrigger_TractorBeam *pTractorBeam = pPortalPlayer->m_PortalLocal.m_hTractorBeam.Get();
  553. if ( !pTractorBeam )
  554. return;
  555. if ( gpGlobals->frametime > 0.0f )
  556. {
  557. Vector vLinear;
  558. AngularImpulse angAngular;
  559. vLinear.Init();
  560. angAngular.Init();
  561. pTractorBeam->CalculateFrameMovement( NULL, pPortalPlayer, gpGlobals->frametime, vLinear, angAngular );
  562. mv->m_vecVelocity += vLinear * gpGlobals->frametime;
  563. }
  564. TryPlayerMove( 0, 0 );
  565. }
  566. //-----------------------------------------------------------------------------
  567. // Purpose:
  568. //-----------------------------------------------------------------------------
  569. void CPortalGameMovement::AirMove( void )
  570. {
  571. // Determine movement angles
  572. Vector forward, right, up;
  573. AngleVectors(mv->m_vecViewAngles, &forward, &right, &up);
  574. // Copy movement amounts
  575. float fmove = mv->m_flForwardMove;
  576. float smove = mv->m_flSideMove;
  577. {
  578. // Disregard the player's air movement if they're in a phys controller
  579. CBasePlayer *pPlayer = GetPortalPlayer();
  580. if ( pPlayer->HasPhysicsFlag( PFLAG_VPHYSICS_MOTIONCONTROLLER ) )
  581. {
  582. fmove = 0;
  583. smove = 0;
  584. }
  585. }
  586. // Looking mostly straight forward?
  587. if( forward[2] < 0.5f && forward[2] > -0.5f )
  588. {
  589. // Normalize forward vector if they are looking mostly forward
  590. forward -= DotProduct( forward, m_vGravityDirection ) * m_vGravityDirection;
  591. VectorNormalize(forward);
  592. }
  593. else
  594. {
  595. // Do not normalize if they are not. This prevents the player from screwing up their momentum after
  596. // exiting floor portals or jumping off sticky ceilings while looking straight up/down.
  597. forward -= DotProduct( forward, m_vGravityDirection ) * m_vGravityDirection;
  598. }
  599. // Zero out components of movement vectors in the direction of gravity
  600. right -= DotProduct( right, m_vGravityDirection ) * m_vGravityDirection;
  601. // Normalize remainder of vectors
  602. VectorNormalize(right);
  603. Vector targetVel = fmove * forward + smove * right;
  604. // Zero out velocity in gravity direction
  605. targetVel -= DotProduct( targetVel, m_vGravityDirection ) * m_vGravityDirection;
  606. Vector wishdir;
  607. VectorCopy( targetVel, wishdir );
  608. Vector vFunnelForce;
  609. vFunnelForce.Zero();
  610. //
  611. // Don't let the player screw their fling because of adjusting into a floor portal
  612. //
  613. if ( mv->m_vecVelocity[ 0 ] * mv->m_vecVelocity[ 0 ] + mv->m_vecVelocity[ 1 ] * mv->m_vecVelocity[ 1 ] > MIN_FLING_SPEED * MIN_FLING_SPEED )
  614. {
  615. if ( mv->m_vecVelocity[ 0 ] > MIN_FLING_SPEED * 0.5f && wishdir[ 0 ] < 0.0f )
  616. wishdir[ 0 ] = 0.0f;
  617. else if ( mv->m_vecVelocity[ 0 ] < -MIN_FLING_SPEED * 0.5f && wishdir[ 0 ] > 0.0f )
  618. wishdir[ 0 ] = 0.0f;
  619. if ( mv->m_vecVelocity[ 1 ] > MIN_FLING_SPEED * 0.5f && wishdir[ 1 ] < 0.0f )
  620. wishdir[ 1 ] = 0.0f;
  621. else if ( mv->m_vecVelocity[ 1 ] < -MIN_FLING_SPEED * 0.5f && wishdir[ 1 ] > 0.0f )
  622. wishdir[ 1 ] = 0.0f;
  623. }
  624. //
  625. // Try to autocorrect the player to fall into the middle of the portal
  626. //
  627. else if ( sv_player_funnel_into_portals.GetBool() )
  628. {
  629. vFunnelForce = PortalFunnel( wishdir );
  630. }
  631. // See if we're suppressing air control
  632. CPortal_Player *pPlayer = GetPortalPlayer();
  633. if ( pPlayer && pPlayer->IsSuppressingAirControl() )
  634. {
  635. wishdir.Zero();
  636. }
  637. // Add in funnel force after wishdir potentionally gets nuked. We still want
  638. // to funnel, even if the player isnt allowed to move themself
  639. wishdir += vFunnelForce;
  640. // Compute speed and direction from velocity
  641. float targetSpeed = VectorNormalize(wishdir);
  642. // Clamp to server defined max speed
  643. if ( targetSpeed != 0 && (targetSpeed > mv->m_flMaxSpeed) )
  644. {
  645. VectorScale(targetVel, mv->m_flMaxSpeed/targetSpeed, targetVel);
  646. targetSpeed = mv->m_flMaxSpeed;
  647. }
  648. AirAccelerate( wishdir, targetSpeed, sv_paintairacceleration.GetFloat() );
  649. // Add in any base velocity to the current velocity.
  650. VectorAdd(mv->m_vecVelocity, player->GetBaseVelocity(), mv->m_vecVelocity );
  651. TryPlayerMove( 0, 0 );
  652. // 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?)
  653. VectorSubtract( mv->m_vecVelocity, player->GetBaseVelocity(), mv->m_vecVelocity );
  654. }
  655. bool CPortalGameMovement::IsInPortalFunnelVolume( const Vector& vPlayerToPortal, const CPortal_Base2D* pPortal, const float flExtentX, const float flExtentY ) const
  656. {
  657. const Vector& portalNormal = pPortal->m_plane_Origin.normal;
  658. Vector vPortalRight = pPortal->m_PortalSimulator.GetInternalData().Placement.vRight;
  659. vPortalRight -= DotProduct( vPortalRight, portalNormal ) * portalNormal;
  660. VectorNormalize( vPortalRight );
  661. float fTestDist = flExtentX;
  662. fTestDist *= fTestDist;
  663. float flDist = ( vPlayerToPortal.Dot( vPortalRight ) * vPortalRight ).LengthSqr();
  664. // Make sure we're in the 2D portal rectangle
  665. if ( flDist > fTestDist )
  666. return false;
  667. Vector vPortalUp = pPortal->m_PortalSimulator.GetInternalData().Placement.vUp;
  668. vPortalUp -= DotProduct( vPortalUp, portalNormal ) * portalNormal;
  669. VectorNormalize( vPortalUp );
  670. fTestDist = flExtentY;
  671. fTestDist *= fTestDist;
  672. flDist = ( vPlayerToPortal.Dot( vPortalUp ) * vPortalUp ).LengthSqr();
  673. if ( flDist > fTestDist )
  674. return false;
  675. return true;
  676. }
  677. bool CPortalGameMovement::PlayerShouldFunnel( const CPortal_Base2D* pPortal, const Vector& vPlayerLook, const Vector& wishdir ) const
  678. {
  679. // In the air
  680. if( player->GetGroundEntity() == NULL )
  681. {
  682. // We are more liberal about funneling into a ceiling portal - All we care about is if we are going up, in general we aren't going
  683. // to be hitting these by accident so we can do the funnel without the requirement that we are looking at the portal
  684. const bool bFunnelUp = mv->m_vecVelocity[ 2 ] > 165.0f;
  685. if( ( fabsf( wishdir[ 0 ] ) > 64.0f || fabsf( wishdir[ 1 ] ) > 64.0f ) ||
  686. !( bFunnelUp || ( vPlayerLook.z < -0.7f && mv->m_vecVelocity[ 2 ] < -165.0f ) ) )
  687. {
  688. return false;
  689. }
  690. // Make sure it's a floor or ceiling portal
  691. const Vector vPlayerToPortal = pPortal->WorldSpaceCenter() - player->WorldSpaceCenter();
  692. if( ( bFunnelUp && !pPortal->IsCeilingPortal() ) ||
  693. ( !bFunnelUp && !pPortal->IsFloorPortal() ) )
  694. {
  695. return false;
  696. }
  697. // Make sure that the portal isn't too far away and we aren't past it.
  698. const float fPeakRelativeHeight = (mv->m_vecVelocity[2] * mv->m_vecVelocity[2]) / (2.0f * sv_gravity.GetFloat());
  699. if( ( bFunnelUp && ( (vPlayerToPortal.z > 1024.0f) || (vPlayerToPortal.z <= 0.0f) || (vPlayerToPortal.z > fPeakRelativeHeight) ) ) ||
  700. ( !bFunnelUp && ( (vPlayerToPortal.z < -1024.0f) || (vPlayerToPortal.z >= 0.0f) ) ) )
  701. {
  702. return false;
  703. }
  704. float flConeScale = RemapValClamped( fabs(vPlayerToPortal.z), 256.f, 1024.f, 1.5, 3.f );
  705. // Make sure we're directly in line with the portal
  706. if( !IsInPortalFunnelVolume( vPlayerToPortal,
  707. pPortal,
  708. pPortal->m_PortalSimulator.GetInternalData().Placement.fHalfWidth * flConeScale,
  709. pPortal->m_PortalSimulator.GetInternalData().Placement.fHalfHeight * flConeScale) )
  710. {
  711. return false;
  712. }
  713. }
  714. // On the ground and speed funnelling enabled
  715. else if( speed_funnelling_enabled.GetBool() )
  716. {
  717. // The player's max speed isn't larger than walk speed
  718. if( player->MaxSpeed() <= sv_speed_normal.GetFloat() )
  719. return false;
  720. // Not moving toward the portal
  721. const Vector vPlayerToPortal = pPortal->WorldSpaceCenter() - player->WorldSpaceCenter();
  722. const Vector& portalNormal = pPortal->m_plane_Origin.normal;
  723. if( fabs( DotProduct( player->Forward(), portalNormal ) ) < STEEP_SLOPE ||
  724. DotProduct( player->GetAbsVelocity().Normalized(), portalNormal ) >= -STEEP_SLOPE ||
  725. DotProduct( wishdir.Normalized(), portalNormal ) >= -STEEP_SLOPE ||
  726. !IsInPortalFunnelVolume( vPlayerToPortal,
  727. pPortal,
  728. pPortal->m_PortalSimulator.GetInternalData().Placement.fHalfWidth * 1.5f,
  729. pPortal->m_PortalSimulator.GetInternalData().Placement.fHalfHeight * 1.5f ) )
  730. {
  731. return false;
  732. }
  733. }
  734. else
  735. {
  736. return false;
  737. }
  738. return true;
  739. }
  740. Vector CPortalGameMovement::PortalFunnel( const Vector& wishdir )
  741. {
  742. Vector vPlayerLook;
  743. player->EyeVectors( &vPlayerLook );
  744. const CPortal_Base2D *pFunnelInto = NULL;
  745. Vector vPlayerToFunnelPortal;
  746. float fClosestFunnelPortalDistSqr = FLT_MAX;
  747. CPortal_Base2D **pPortals = CPortal_Base2D_Shared::AllPortals.Base();
  748. const int iPortalCount = CPortal_Base2D_Shared::AllPortals.Count();
  749. for( int i = 0; i != iPortalCount; ++i )
  750. {
  751. CPortal_Base2D *pTempPortal = pPortals[i];
  752. if( pTempPortal->IsActivedAndLinked() )
  753. {
  754. const Vector vPlayerToPortal = pTempPortal->WorldSpaceCenter() - player->WorldSpaceCenter();
  755. const float fDistSqr = vPlayerToPortal.LengthSqr();
  756. if( PlayerShouldFunnel( pTempPortal, vPlayerLook, wishdir ) &&
  757. fDistSqr < fClosestFunnelPortalDistSqr )
  758. {
  759. fClosestFunnelPortalDistSqr = fDistSqr;
  760. pFunnelInto = pTempPortal;
  761. vPlayerToFunnelPortal = vPlayerToPortal;
  762. }
  763. }
  764. }
  765. Vector vFunnelDir;
  766. vFunnelDir.Zero();
  767. if( pFunnelInto )
  768. {
  769. // In the air
  770. if( player->GetGroundEntity() == NULL )
  771. {
  772. const float flHeightFromPortal = -vPlayerToFunnelPortal.z - sv_player_funnel_height_adjust.GetFloat();
  773. // Extra funnel force based on how fast we're going
  774. float flExtraFunnelForce = RemapValClamped( mv->m_vecVelocity[2], 0.f, 1065.f, 1.f, sv_player_funnel_speed_bonus.GetFloat() );
  775. // Figure out how long until we hit the portal
  776. const float flVerticalSpeed = mv->m_vecVelocity.z;
  777. const float flGravity = player->GetGravity() != 0 ? player->GetGravity() * sv_gravity.GetFloat() : sv_gravity.GetFloat();
  778. // Solve when we hit
  779. float flRoot1, flRoot2;
  780. if( SolveQuadratic( -flGravity, 2.f * flVerticalSpeed, 2.f * flHeightFromPortal, flRoot1, flRoot2 ) )
  781. {
  782. float flTimeToPortal = flRoot1 > 0.f ? flRoot1 : flRoot2;
  783. if( flRoot2 < flRoot1 && flRoot2 >= 0.f && flRoot1 >= 0.f )
  784. flTimeToPortal = flRoot2;
  785. AirPortalFunnel( vFunnelDir, vPlayerToFunnelPortal, flExtraFunnelForce, flTimeToPortal );
  786. }
  787. }
  788. // On the ground
  789. else
  790. {
  791. const float flExtraFunnelForce = 1.f + (player->MaxSpeed() / 800.0f) * sv_player_funnel_speed_bonus.GetFloat() - 1.f;
  792. // Compute how long it will take to reach the portal, assuming the player moves at max speed
  793. const Vector& portalNormal = pFunnelInto->m_plane_Origin.normal;
  794. const float distanceFromPortal = DotProduct( player->WorldSpaceCenter(), portalNormal ) - pFunnelInto->m_plane_Origin.dist;
  795. const float flTimeToPortal = distanceFromPortal / player->MaxSpeed();
  796. GroundPortalFunnel( vFunnelDir, vPlayerToFunnelPortal, pFunnelInto->m_plane_Origin.normal, flExtraFunnelForce, flTimeToPortal );
  797. }
  798. }//if funnelling
  799. return vFunnelDir;
  800. }
  801. void CPortalGameMovement::AirPortalFunnel( Vector& wishdir,
  802. const Vector& vPlayerToFunnelPortal,
  803. float flExtraFunnelForce,
  804. float flTimeToPortal )
  805. {
  806. float flExponent = RemapValClamped( fabs(vPlayerToFunnelPortal.z), 128.f, 1024.f, 0.01f, 0.15 );
  807. const float flDecay = ExponentialDecay( flExponent, gpGlobals->frametime );
  808. for( int i = 0; i < 2; ++i )
  809. {
  810. // Find out if we'll make it to our destination in time
  811. bool bMakeItInTime = false;
  812. if( mv->m_vecVelocity[i] )
  813. {
  814. const float flTimeToCenter = vPlayerToFunnelPortal[i] / mv->m_vecVelocity[i];
  815. bMakeItInTime = flTimeToCenter < flTimeToPortal;
  816. }
  817. // If we're not going to make it, then accellerate
  818. if( !bMakeItInTime )
  819. {
  820. // Funnel toward the portal
  821. const float flFunnel = vPlayerToFunnelPortal[i] * flExtraFunnelForce * PORTAL_FUNNEL_AMOUNT - mv->m_vecVelocity[i];
  822. wishdir[i] += flFunnel;
  823. }
  824. else
  825. {
  826. if( fabs( mv->m_vecVelocity[i] ) > sv_player_funnel_snap_threshold.GetFloat() )
  827. {
  828. // Decay speed in XY plane if we're moving too quick
  829. mv->m_vecVelocity[i] = mv->m_vecVelocity[i] * flDecay;
  830. }
  831. else
  832. {
  833. // Just stop if below threshold
  834. mv->m_vecVelocity[i] = 0.f;
  835. }
  836. }
  837. if ( portal_funnel_debug.GetBool() )
  838. {
  839. char* idx[] = { "x", "y" };
  840. Msg( "%s: Make in time? %s velocity %f wish %f\n", idx[i],
  841. (bMakeItInTime) ? ("yes") : ("no"),
  842. mv->m_vecVelocity[i],
  843. wishdir[i] );
  844. }
  845. }//for
  846. if ( portal_funnel_debug.GetBool() )
  847. {
  848. Msg( "Extra force: %f Time till portal: %f\n", flExtraFunnelForce, flTimeToPortal );
  849. }
  850. }
  851. void CPortalGameMovement::GroundPortalFunnel( Vector& wishdir,
  852. const Vector& vPlayerToFunnelPortal,
  853. const Vector& vPortalNormal,
  854. float flExtraFunnelForce,
  855. float flTimeToPortal )
  856. {
  857. // Compute the velocity tangent to the portal plane in the horizontal direction
  858. const Vector tangentialVelocity = mv->m_vecVelocity - DotProduct( mv->m_vecVelocity, vPortalNormal ) * vPortalNormal;
  859. Vector horizontalVelocity = tangentialVelocity;
  860. horizontalVelocity -= DotProduct( horizontalVelocity, vPortalNormal ) * vPortalNormal;
  861. horizontalVelocity.z = 0.0f;
  862. // Compute how long it'll take for the player to be centered at the current horizontal speed
  863. Vector vPlayerToPortalHorizontal = vPlayerToFunnelPortal;
  864. vPlayerToPortalHorizontal -= DotProduct( vPlayerToPortalHorizontal, vPortalNormal ) * vPortalNormal;
  865. vPlayerToPortalHorizontal.z = 0.0f;
  866. Vector horizontalVelocityDir = horizontalVelocity;
  867. Vector horizontalPlayerToPortalDir = vPlayerToPortalHorizontal;
  868. const float horizontalSpeed = horizontalVelocityDir.NormalizeInPlace();
  869. const float horizontalDistance = horizontalPlayerToPortalDir.NormalizeInPlace();
  870. const float flTimeToCenter = horizontalDistance / horizontalSpeed;
  871. // If the player won't be centered by the time she reaches the portal
  872. if( flTimeToCenter > flTimeToPortal )
  873. {
  874. // Figure out if velocity and funnel impulse are opposite each other
  875. const float flVelocityCrossNormal = horizontalVelocityDir.x * vPortalNormal.y - horizontalVelocityDir.y * vPortalNormal.x;
  876. const float flToCenterCrossNormal = horizontalPlayerToPortalDir.x * vPortalNormal.y - horizontalPlayerToPortalDir.y * vPortalNormal.x;
  877. const float flSign = Sign( flVelocityCrossNormal * flToCenterCrossNormal );
  878. // Funnel the player toward the center
  879. const float flFunnel = fpmax( horizontalDistance * flExtraFunnelForce - flSign * horizontalSpeed, 0.0f );
  880. Vector vFunnelVector = flFunnel * horizontalPlayerToPortalDir;
  881. vFunnelVector -= DotProduct( vPortalNormal, vFunnelVector ) * vPortalNormal;
  882. wishdir += vFunnelVector;
  883. //NDebugOverlay::HorzArrow( player->WorldSpaceCenter(), player->WorldSpaceCenter() + flFunnel * horizontalPlayerToPortalDir, 5.0f, 255, 0, 0, 128, true, 5.0f );
  884. //DevMsg( "Funnel %s: %f\n", DotProduct( player->Left(), horizontalPlayerToPortalDir ) > 0 ? "Left" : "Right", flFunnel );
  885. //DevMsg( "Horizontal Distance: %f\n", horizontalDistance );
  886. //DevMsg( "Horizontal Speed: %f\n", horizontalSpeed );
  887. //DevMsg( "Normal Funneling: %f\n", DotProduct( vPortalNormal, vFunnelVector) );
  888. }
  889. }
  890. CEG_NOINLINE void CPortalGameMovement::PlayerRoughLandingEffects( float fvol )
  891. {
  892. if ( fvol > 0.0 )
  893. {
  894. //
  895. // Play landing sound right away.
  896. player->m_flStepSoundTime = 400;
  897. // Play step sound for current texture.
  898. player->PlayStepSound( (Vector &)mv->GetAbsOrigin(), player->m_pSurfaceData, MAX( fvol, 0.1f ), true );
  899. // //
  900. // // Knock the screen around a little bit, temporary effect.
  901. // //
  902. // player->m_Local.m_vecPunchAngle.Set( ROLL, player->m_Local.m_flFallVelocity * 0.013 );
  903. //
  904. // if ( player->m_Local.m_vecPunchAngle[PITCH] > 8 )
  905. // {
  906. // player->m_Local.m_vecPunchAngle.Set( PITCH, 8 );
  907. // }
  908. //
  909. //#if !defined( CLIENT_DLL )
  910. // player->RumbleEffect( ( fvol > 0.85f ) ? ( RUMBLE_FALL_LONG ) : ( RUMBLE_FALL_SHORT ), 0, RUMBLE_FLAGS_NONE );
  911. //#endif
  912. }
  913. if ( fvol >= 1.0 )
  914. {
  915. // Play the future shoes sound
  916. CRecipientFilter filter;
  917. filter.UsePredictionRules();
  918. filter.AddRecipientsByPAS( player->GetAbsOrigin() );
  919. CSoundParameters params;
  920. if ( CBaseEntity::GetParametersForSound( "PortalPlayer.FallRecover", params, NULL ) )
  921. {
  922. EmitSound_t ep( params );
  923. ep.m_nPitch = 125.0f - player->m_Local.m_flFallVelocity * 0.03f; // lower pitch the harder they land
  924. ep.m_flVolume = MIN( player->m_Local.m_flFallVelocity * 0.00075f - 0.38, 1.0f ); // louder the harder they land
  925. #if defined GAME_DLL
  926. CEG_PROTECT_MEMBER_FUNCTION( CPortalGameMovement_PlayerRoughLandingEffects );
  927. #endif
  928. CBaseEntity::EmitSound( filter, player->entindex(), ep );
  929. }
  930. }
  931. }
  932. void CPortalGameMovement::PlayerWallImpactEffects( float fvol, float normalVelocity )
  933. {
  934. if ( fvol > 0.0 )
  935. {
  936. //
  937. // Play landing sound right away.
  938. player->m_flStepSoundTime = 400;
  939. // Play step sound for current texture.
  940. player->PlayStepSound( (Vector &)mv->GetAbsOrigin(), player->m_pSurfaceData, MAX( fvol, 0.1f ), true );
  941. }
  942. if ( fvol >= 1.0 )
  943. {
  944. // Play the impact sound
  945. CRecipientFilter filter;
  946. filter.UsePredictionRules();
  947. filter.AddRecipientsByPAS( player->GetAbsOrigin() );
  948. CSoundParameters params;
  949. if ( CBaseEntity::GetParametersForSound( "JumpLand.HighVelocityImpact", params, NULL ) )
  950. {
  951. EmitSound_t ep( params );
  952. ep.m_nPitch = 125.0f - 2.0f * normalVelocity * 0.03f; // lower pitch the harder they land
  953. ep.m_flVolume = MIN( 2.0f * (normalVelocity * 0.00075f - 0.38), 1.0f ); // louder the harder they land
  954. CBaseEntity::EmitSound( filter, player->entindex(), ep );
  955. }
  956. }
  957. }
  958. void CPortalGameMovement::PlayerCeilingImpactEffects( float fvol )
  959. {
  960. //if ( fvol > 0.0 )
  961. //{
  962. // //
  963. // // Play landing sound right away.
  964. // player->m_flStepSoundTime = 400;
  965. // // Play step sound for current texture.
  966. // player->PlayStepSound( (Vector &)mv->GetAbsOrigin(), player->m_pSurfaceData, MAX( fvol, 0.1f ), true );
  967. //}
  968. if ( fvol >= 1.0 )
  969. {
  970. // Play the impact sound
  971. CRecipientFilter filter;
  972. filter.UsePredictionRules();
  973. filter.AddRecipientsByPAS( player->GetAbsOrigin() );
  974. CSoundParameters params;
  975. if ( CBaseEntity::GetParametersForSound( "JumpLand.HighVelocityImpactCeiling", params, NULL ) )
  976. {
  977. const float fallVelocity = fabs( player->m_Local.m_flFallVelocity );
  978. EmitSound_t ep( params );
  979. ep.m_nPitch = 125.0f - 2.0f * fallVelocity * 0.03f; // lower pitch the harder they land
  980. ep.m_flVolume = MIN( 4.0f * (fallVelocity * 0.00075f - 0.38), 1.0f ); // louder the harder they land
  981. #if defined CLIENT_DLL
  982. RANDOM_CEG_TEST_SECRET_PERIOD( 3, 7 );
  983. #endif
  984. CBaseEntity::EmitSound( filter, player->entindex(), ep );
  985. }
  986. }
  987. }
  988. //-----------------------------------------------------------------------------
  989. // Purpose:
  990. // Input : &input -
  991. //-----------------------------------------------------------------------------
  992. void CPortalGameMovement::CategorizePosition( void )
  993. {
  994. //DiffPrint( "Categorize() %f %f %f %f %f %f", XYZ( mv->GetAbsOrigin() ), XYZ( mv->m_vecVelocity ) );
  995. // TODO: If we decide to turn friction down while on speed paint, this needs to change
  996. // Reset this each time we-recategorize, otherwise we have bogus friction when we jump into water and plunge downward really quickly
  997. player->m_surfaceFriction = 1.0f;
  998. // if the player hull point one unit down is solid, the player
  999. // is on ground
  1000. // see if standing on something solid
  1001. // Doing this before we move may introduce a potential latency in water detection, but
  1002. // doing it after can get us stuck on the bottom in water if the amount we move up
  1003. // is less than the 1 pixel 'threshold' we're about to snap to. Also, we'll call
  1004. // this several times per frame, so we really need to avoid sticking to the bottom of
  1005. // water on each call, and the converse case will correct itself if called twice.
  1006. CheckWater();
  1007. // observers don't have a ground entity
  1008. if ( player->IsObserver() )
  1009. return;
  1010. const float GROUND_OFFSET = 2.0f;
  1011. const Vector& stickNormal = GetPortalPlayer()->GetPortalPlayerLocalData().m_StickNormal;
  1012. Vector point = mv->GetAbsOrigin() + GROUND_OFFSET * m_vGravityDirection;
  1013. Vector bumpOrigin = mv->GetAbsOrigin();
  1014. // Shooting up really fast. Definitely not on ground.
  1015. // On ladder moving up, so not on ground either
  1016. // NOTE: 145 is a jump.
  1017. const float NON_JUMP_VELOCITY = 140.0f;
  1018. CPortal_Player* pPortalPlayer = GetPortalPlayer();
  1019. const float normalVel = DotProduct( mv->m_vecVelocity, stickNormal );
  1020. const bool bMovingUp = normalVel > 0.0f;
  1021. bool bMovingUpRapidly = normalVel > NON_JUMP_VELOCITY && ((player->GetGroundEntity() == NULL) || IsInactivePower( pPortalPlayer->GetPaintPower( SPEED_POWER )));
  1022. float flGroundEntityNormalVel = 0.0f;
  1023. if ( bMovingUpRapidly )
  1024. {
  1025. // Tracker 73219, 75878: ywb 8/2/07
  1026. // After save/restore (and maybe at other times), we can get a case where we were saved on a lift and
  1027. // after restore we'll have a high local velocity due to the lift making our abs velocity appear high.
  1028. // We need to account for standing on a moving ground object in that case in order to determine if we really
  1029. // are moving away from the object we are standing on at too rapid a speed. Note that CheckJump already sets
  1030. // ground entity to NULL, so this wouldn't have any effect unless we are moving up rapidly not from the jump button.
  1031. CBaseEntity *ground = player->GetGroundEntity();
  1032. if ( ground )
  1033. {
  1034. flGroundEntityNormalVel = DotProduct( ground->GetAbsVelocity(), stickNormal );
  1035. bMovingUpRapidly = ( normalVel - flGroundEntityNormalVel ) > NON_JUMP_VELOCITY;
  1036. }
  1037. }
  1038. // 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
  1039. const bool bUnderwater = ( player->GetWaterLevel() >= WL_Eyes );
  1040. bool bMoveToEndPos = false;
  1041. if ( player->GetMoveType() == MOVETYPE_WALK &&
  1042. player->GetGroundEntity() != NULL && !bUnderwater )
  1043. {
  1044. // if walking and still think we're on ground, we'll extend trace down by stepsize so we don't bounce down slopes
  1045. // We scale by the ratio of max speed to normal speed to account for speed paint.
  1046. bMoveToEndPos = true;
  1047. point += ( player->MaxSpeed() / sv_speed_normal.GetFloat() ) * player->m_Local.m_flStepSize * m_vGravityDirection;
  1048. }
  1049. // Was on ground, but now suddenly am not
  1050. CTrace_PlayerAABB_vs_Portals pm;
  1051. if ( bMovingUpRapidly ||
  1052. ( bMovingUp && player->GetMoveType() == MOVETYPE_LADDER ) )
  1053. {
  1054. SetGroundEntity( NULL );
  1055. bMoveToEndPos = false;
  1056. }
  1057. else
  1058. {
  1059. // Try to move down.
  1060. TracePlayerBBox( bumpOrigin, point, PlayerSolidMask(), COLLISION_GROUP_PLAYER_MOVEMENT, pm );
  1061. const Vector vPrevGroundNormal = pPortalPlayer->GetPrevGroundNormal();
  1062. float fDot = DotProduct( vPrevGroundNormal, pm.plane.normal );
  1063. float fSpeed = mv->m_vecVelocity.Length();
  1064. bool bRampLaunch = false;
  1065. // Fast enough to launch and surface slopes have a sharp enough change?
  1066. if( pPortalPlayer->GetGroundEntity() && // We're on the ground this frame
  1067. fSpeed > sv_maxspeed.GetFloat() && // Our speed is greater than the normal (ie. we're on speed paint)
  1068. ( fDot < 1.f || !pm.DidHit() ) ) // And the trace did not hit or there was a significant change in surface normals
  1069. {
  1070. // Find out if the slope went up or down
  1071. Vector vVelCrossUp = CrossProduct( mv->m_vecVelocity.Normalized(), -m_vGravityDirection );
  1072. Vector vNewNormCrossOldNorm = CrossProduct( pm.plane.normal, vPrevGroundNormal );
  1073. float fCrossDot = DotProduct( vVelCrossUp, vNewNormCrossOldNorm );
  1074. // If it was a downward change then launch off of it
  1075. if( fCrossDot > EQUAL_EPSILON || ( vPrevGroundNormal.Length2DSqr() && !pm.DidHit() ) )
  1076. {
  1077. bRampLaunch = true;
  1078. #if defined CLIENT_DLL
  1079. STEAMWORKS_TESTSECRET();
  1080. #endif
  1081. // Compute normalized forward direction in tangent plane of the ramp
  1082. const Vector vWishDirection = mv->m_vecVelocity;
  1083. const Vector vTangentRight = CrossProduct( vWishDirection, vPrevGroundNormal );
  1084. const Vector vNormTangentForward = CrossProduct( vPrevGroundNormal, vTangentRight ).Normalized();
  1085. mv->m_vecVelocity = vNormTangentForward * mv->m_vecVelocity.Length();
  1086. }
  1087. }
  1088. // Was on ground, but now suddenly am not. If we hit a steep plane, we are not on ground
  1089. const float flStandableAngle = CRITICAL_SLOPE;
  1090. const float traceNormalAngle = DotProduct( pm.plane.normal, stickNormal );
  1091. if ( !pm.m_pEnt || (( traceNormalAngle < flStandableAngle ) && !pm.HitPortalRamp(stickNormal)) || bRampLaunch )
  1092. {
  1093. //DiffPrint( "Categorize() Lost ground ent %i %i", player->GetGroundEntity() ? player->GetGroundEntity()->entindex() : -1, pm.m_pEnt ? pm.m_pEnt->entindex() : -1 );
  1094. // Test four sub-boxes, to see if any of them would have found shallower slope we could actually stand on
  1095. ITraceFilter *pFilter = LockTraceFilter( COLLISION_GROUP_PLAYER_MOVEMENT );
  1096. PortalTracePlayerBBoxForGround( bumpOrigin, point, GetPlayerMins(), GetPlayerMaxs(),
  1097. mv->m_nPlayerHandle.Get(), MASK_PLAYERSOLID, COLLISION_GROUP_PLAYER_MOVEMENT,
  1098. pm, -m_vGravityDirection );
  1099. UnlockTraceFilter( pFilter );
  1100. if ( !pm.m_pEnt || (( traceNormalAngle < flStandableAngle ) && !pm.HitPortalRamp(stickNormal)) || bRampLaunch )
  1101. {
  1102. //DiffPrint( "Categorize() failed to find new one" );
  1103. SetGroundEntity( NULL );
  1104. // probably want to add a check for a +z velocity too!
  1105. if ( ( normalVel > 0.0f ) &&
  1106. ( player->GetMoveType() != MOVETYPE_NOCLIP ) )
  1107. {
  1108. player->m_surfaceFriction = 0.25f;
  1109. }
  1110. bMoveToEndPos = false;
  1111. }
  1112. else
  1113. {
  1114. //DiffPrint( "Categorize() found new one" );
  1115. SetGroundEntity( &pm );
  1116. }
  1117. }
  1118. else
  1119. {
  1120. SetGroundEntity( &pm ); // Otherwise, point to index of ent under us.
  1121. }
  1122. #ifndef CLIENT_DLL
  1123. //Adrian: vehicle code handles for us.
  1124. if ( player->IsInAVehicle() == false )
  1125. {
  1126. // If our gamematerial has changed, tell any player surface triggers that are watching
  1127. IPhysicsSurfaceProps *physprops = MoveHelper()->GetSurfaceProps();
  1128. surfacedata_t *pSurfaceProp = physprops->GetSurfaceData( pm.surface.surfaceProps );
  1129. char cCurrGameMaterial = pSurfaceProp->game.material;
  1130. if ( !player->GetGroundEntity() )
  1131. {
  1132. cCurrGameMaterial = 0;
  1133. }
  1134. // Changed?
  1135. if ( player->m_chPreviousTextureType != cCurrGameMaterial )
  1136. {
  1137. CEnvPlayerSurfaceTrigger::SetPlayerSurface( player, cCurrGameMaterial );
  1138. }
  1139. player->m_chPreviousTextureType = cCurrGameMaterial;
  1140. }
  1141. #endif
  1142. }
  1143. // YWB: This logic block essentially lifted from StayOnGround implementation
  1144. if ( bMoveToEndPos &&
  1145. !pm.startsolid && // not sure we need this check as fraction would == 0.0f?
  1146. pm.fraction > 0.0f && // must go somewhere
  1147. pm.fraction < 1.0f && // must hit something
  1148. gpGlobals->frametime != 0.f ) // can't divide by zero
  1149. {
  1150. mv->SetAbsOrigin( pm.endpos );
  1151. const float flDelta = DotProduct( mv->GetAbsOrigin() - pm.endpos, m_vGravityDirection );
  1152. // Set the implicit vertical speed keeping the player on the surface
  1153. // Ignore this one if it's on an angled surface, the player is moving, and we get a zero.
  1154. // The values cycle back to zero occasionally while moving on sloped surfaces, which doesn't accurately reflect this implicit speed.
  1155. if( (gpGlobals->frametime != 0.0f) && (flDelta != 0.0f || AlmostEqual( pm.plane.normal.z, 1.0f ) || ( mv->m_flSideMove == 0.0f && mv->m_flForwardMove == 0.0f )) )
  1156. GetPortalPlayer()->SetImplicitVerticalStepSpeed( flDelta / gpGlobals->frametime );
  1157. }
  1158. // Save the normal of the surface if we hit something
  1159. if( pPortalPlayer->GetGroundEntity() != NULL && pm.DidHit() )
  1160. {
  1161. pPortalPlayer->SetPrevGroundNormal( pm.plane.normal );
  1162. }
  1163. }
  1164. int CPortalGameMovement::CheckStuck( void )
  1165. {
  1166. if( BaseClass::CheckStuck() )
  1167. {
  1168. CPortal_Player *pPortalPlayer = GetPortalPlayer();
  1169. #if !defined( CLIENT_DLL ) && !defined( _GAMECONSOLE ) && !defined( NO_STEAM )
  1170. if( pPortalPlayer->IsAlive() )
  1171. g_PortalGameStats.Event_PlayerStuck( pPortalPlayer );
  1172. #endif
  1173. //try to fix it, then recheck
  1174. Vector vIndecisive;
  1175. if( pPortalPlayer->m_hPortalEnvironment )
  1176. {
  1177. pPortalPlayer->m_hPortalEnvironment->GetVectors( &vIndecisive, NULL, NULL );
  1178. }
  1179. else
  1180. {
  1181. vIndecisive.Init( 0.0f, 0.0f, 1.0f );
  1182. }
  1183. Vector ptOldOrigin = pPortalPlayer->GetAbsOrigin();
  1184. if( pPortalPlayer->m_hPortalEnvironment )
  1185. {
  1186. if( !FindClosestPassableSpace( pPortalPlayer, vIndecisive, MASK_PLAYERSOLID ) )
  1187. {
  1188. #ifndef CLIENT_DLL
  1189. DevMsg( "Hurting the player for FindClosestPassableSpaceFailure!" );
  1190. CTakeDamageInfo info( pPortalPlayer, pPortalPlayer, vec3_origin, vec3_origin, 1e10, DMG_CRUSH );
  1191. pPortalPlayer->OnTakeDamage( info );
  1192. #endif
  1193. }
  1194. //make sure we didn't get put behind the portal >_<
  1195. Vector ptCurrentOrigin = pPortalPlayer->GetAbsOrigin();
  1196. if( vIndecisive.Dot( ptCurrentOrigin - ptOldOrigin ) < 0.0f )
  1197. {
  1198. pPortalPlayer->SetAbsOrigin( ptOldOrigin + (vIndecisive * 5.0f) ); //this is an anti-bug hack, since this would have probably popped them out of the world, we're just going to move them forward a few units
  1199. }
  1200. }
  1201. mv->SetAbsOrigin( pPortalPlayer->GetAbsOrigin() );
  1202. return BaseClass::CheckStuck();
  1203. }
  1204. else
  1205. {
  1206. return 0;
  1207. }
  1208. }
  1209. void CPortalGameMovement::SetGroundEntity( trace_t *pm )
  1210. {
  1211. CBaseEntity *newGround = pm ? pm->m_pEnt : NULL;
  1212. CBaseEntity *oldGround = player->GetGroundEntity();
  1213. Vector vecBaseVelocity = player->GetBaseVelocity();
  1214. if ( !oldGround && newGround )
  1215. {
  1216. // Subtract ground velocity at instant we hit ground jumping
  1217. vecBaseVelocity -= newGround->GetAbsVelocity();
  1218. Vector vecBaseDotGrav = m_vGravityDirection * DotProduct( m_vGravityDirection, vecBaseVelocity );
  1219. Vector vecAbsDotGrav = m_vGravityDirection * DotProduct( m_vGravityDirection, newGround->GetAbsVelocity() );
  1220. vecBaseVelocity += vecAbsDotGrav - vecBaseDotGrav;
  1221. #ifdef GAME_DLL
  1222. CPortal_Player* pPlayer = ToPortalPlayer( player );
  1223. if( pPlayer )
  1224. {
  1225. pPlayer->ResetBounceCount();
  1226. pPlayer->ResetAirTauntCount();
  1227. pPlayer->OnPlayerLanded();
  1228. }
  1229. IGameEvent* pEvent = gameeventmanager->CreateEvent("player_landed");
  1230. if( pEvent )
  1231. {
  1232. pEvent->SetInt("userid", player->GetUserID() );
  1233. gameeventmanager->FireEvent( pEvent );
  1234. }
  1235. #endif
  1236. }
  1237. else if ( oldGround && !newGround )
  1238. {
  1239. // Add in ground velocity at instant we started jumping
  1240. vecBaseVelocity += oldGround->GetAbsVelocity();
  1241. Vector vecBaseDotGrav = m_vGravityDirection * DotProduct( m_vGravityDirection, vecBaseVelocity );
  1242. Vector vecAbsDotGrav = m_vGravityDirection * DotProduct( m_vGravityDirection, oldGround->GetAbsVelocity() );
  1243. vecBaseVelocity += vecAbsDotGrav - vecBaseDotGrav;
  1244. }
  1245. player->SetBaseVelocity( vecBaseVelocity );
  1246. player->SetGroundEntity( newGround );
  1247. // If we are on something...
  1248. if ( newGround )
  1249. {
  1250. CategorizeGroundSurface( *pm );
  1251. // Then we are not in water jump sequence
  1252. player->m_flWaterJumpTime = 0;
  1253. // Standing on an entity other than the world, so signal that we are touching something.
  1254. if ( !pm->DidHitWorld() )
  1255. {
  1256. MoveHelper()->AddToTouched( *pm, mv->m_vecVelocity );
  1257. }
  1258. if( player->GetMoveType() != MOVETYPE_NOCLIP )
  1259. {
  1260. mv->m_vecVelocity -= m_vGravityDirection * DotProduct( mv->m_vecVelocity, m_vGravityDirection );
  1261. }
  1262. }
  1263. }
  1264. //#define TRACE_DEBUG_ENABLED //uncomment for trace debugging goodness
  1265. #if defined( TRACE_DEBUG_ENABLED )
  1266. bool g_bTraceDebugging = false;
  1267. struct StackPair_t
  1268. {
  1269. void *pStack[32];
  1270. int iCount;
  1271. CPortal_Base2D *pPortal;
  1272. Vector vStart;
  1273. Vector vEnd;
  1274. Ray_t ray;
  1275. trace_t realTrace;
  1276. trace_t portalTrace1;
  1277. trace_t portalTrace2;
  1278. trace_t remoteTubeTrace;
  1279. trace_t angledWallTrace;
  1280. trace_t finalTrace;
  1281. bool bCopiedRemoteTubeTrace;
  1282. bool bCopiedAngledWallTrace;
  1283. bool bCopiedPortal2Trace;
  1284. };
  1285. struct StackPairWithBuf_t : public StackPair_t
  1286. {
  1287. char szStackTranslated[2048];
  1288. };
  1289. StackPairWithBuf_t g_TraceRealigned[32]; //for debugging
  1290. #define TRACE_DEBUG_ONLY(x) x
  1291. #else
  1292. #define TRACE_DEBUG_ONLY(x)
  1293. #endif
  1294. /*bool g_bSpewTraceStuck = false;
  1295. class CSpewTraceStuckContext
  1296. {
  1297. public:
  1298. CSpewTraceStuckContext( void ) { g_bSpewTraceStuck = true; }
  1299. ~CSpewTraceStuckContext( void ) { g_bSpewTraceStuck = false; }
  1300. };*/
  1301. class CTraceTypeWrapper : public ITraceFilter
  1302. {
  1303. public:
  1304. CTraceTypeWrapper( TraceType_t type, ITraceFilter *pBaseFilter )
  1305. : m_Type( type ), m_pBase( pBaseFilter )
  1306. { }
  1307. virtual bool ShouldHitEntity( IHandleEntity *pEntity, int contentsMask )
  1308. {
  1309. return m_pBase->ShouldHitEntity( pEntity, contentsMask );
  1310. }
  1311. virtual TraceType_t GetTraceType() const
  1312. {
  1313. return m_Type;
  1314. }
  1315. private:
  1316. TraceType_t m_Type;
  1317. ITraceFilter *m_pBase;
  1318. };
  1319. Vector CalculateExtentShift( const Vector &vLocalExtents, const Vector &vLocalNormal, const Vector &vRemoteExtents, const Vector &vRemoteNormal )
  1320. {
  1321. //for visualization of what fClosestExtentDiff is for.....
  1322. //if walking into the floor (standing hull) and coming out a wall (duck hull but doesn't matter). fClosestExtentsDiff will be negative.
  1323. //when this negative value is applied to the linked portal plane normal, it shifts it relatively backwards to it, but closer to us in local space
  1324. //the net effect is that the local ray's foot is exactly on the portal plane, and the exit ray's side is exactly on the portal plane.
  1325. //without the modifier, the exit ray would be roughly 20 units behind the exit plane in this hypothetical
  1326. #if 1
  1327. float fClosestExtentDiff = 0.0f;
  1328. for( int i = 0; i != 3; ++i )
  1329. {
  1330. fClosestExtentDiff -= fabs( vLocalNormal[i] * vLocalExtents[i] );
  1331. fClosestExtentDiff += fabs( vRemoteNormal[i] * vRemoteExtents[i] );
  1332. }
  1333. #else
  1334. float fMaxLocalExtentDiff = fabs( vLocalNormal[0] * vLocalExtents[0] );
  1335. int iMaxLocalExtent = 0;
  1336. float fMaxRemoteExtentDiff = fabs( vRemoteNormal[0] * vRemoteExtents[0] );
  1337. int iMaxRemoteExtent = 0;
  1338. for( int i = 1; i != 3; ++i )
  1339. {
  1340. float fLocalExtentDiff = fabs( vLocalNormal[i] * vLocalNormal[i] );
  1341. if( fLocalExtentDiff > fMaxLocalExtentDiff )
  1342. {
  1343. fMaxLocalExtentDiff = fLocalExtentDiff;
  1344. iMaxLocalExtent = i;
  1345. }
  1346. float fRemoteExtentDiff = fabs( vRemoteNormal[i] * vRemoteExtents[i] );
  1347. if( fRemoteExtentDiff > fMaxRemoteExtentDiff )
  1348. {
  1349. fMaxRemoteExtentDiff = fRemoteExtentDiff;
  1350. iMaxRemoteExtent = i;
  1351. }
  1352. }
  1353. #if 0
  1354. float fClosestExtentDiff = fMaxRemoteExtentDiff - fMaxLocalExtentDiff;
  1355. #else
  1356. float fClosestExtentDiff = vRemoteNormal[iMaxRemoteExtent] - vLocalExtents[iMaxLocalExtent];
  1357. #endif
  1358. #endif
  1359. return (vRemoteNormal * fClosestExtentDiff);
  1360. }
  1361. ConVar sv_portal_new_player_trace_vs_remote_ents( "sv_portal_new_player_trace_vs_remote_ents", "0", FCVAR_REPLICATED | FCVAR_CHEAT );
  1362. #define DEBUG_PORTAL_TRACE_BOXES //uncomment to draw collision debug info near portals
  1363. #if defined( DEBUG_PORTAL_TRACE_BOXES )
  1364. ConVar sv_portal_new_trace_debugboxes( "sv_portal_new_trace_debugboxes", "0", FCVAR_REPLICATED );
  1365. #endif
  1366. void TracePortalPlayerAABB( CPortal_Player *pPortalPlayer, CPortal_Base2D *pPortal, const Ray_t &ray_local, const Ray_t &ray_remote, const Vector &vRemoteShift, unsigned int fMask, int collisionGroup, CTrace_PlayerAABB_vs_Portals &pm, bool bSkipRemoteTubeCheck = false )
  1367. {
  1368. #ifdef CLIENT_DLL
  1369. CTraceFilterSimple traceFilter( pPortalPlayer, collisionGroup );
  1370. #else
  1371. CTraceFilterSimple baseFilter( pPortalPlayer, collisionGroup );
  1372. CTraceFilterTranslateClones traceFilter( &baseFilter );
  1373. //verify agreement on whether the player is in a portal simulator or not
  1374. Assert( (pPortalPlayer->m_hPortalEnvironment.Get() != NULL) || (CPortalSimulator::GetSimulatorThatOwnsEntity( pPortalPlayer ) == NULL) );
  1375. #endif
  1376. pm.m_bContactedPortalTransitionRamp = false;
  1377. if( sv_portal_new_player_trace.GetBool() )
  1378. {
  1379. #if defined( TRACE_DEBUG_ENABLED )
  1380. static StackPair_t s_LastTrace[32];
  1381. static int s_iLastTrace = 0;
  1382. StackPair_t &traceDebugEntry = s_LastTrace[s_iLastTrace];
  1383. if( g_bTraceDebugging )
  1384. {
  1385. for( int i = 0; i < 32; ++i )
  1386. {
  1387. int iOldestIterate = (s_iLastTrace + i) % 32; //s_iLastTrace starts pointing at the oldest entry
  1388. memcpy( &g_TraceRealigned[31 - i], &s_LastTrace[iOldestIterate], sizeof( StackPair_t ) );
  1389. }
  1390. for( int i = 0; i < 32; ++i )
  1391. {
  1392. TranslateStackInfo( g_TraceRealigned[i].pStack, g_TraceRealigned[i].iCount, g_TraceRealigned[i].szStackTranslated, sizeof( g_TraceRealigned[i].szStackTranslated ), "\n" );
  1393. }
  1394. _asm nop;
  1395. }
  1396. else
  1397. {
  1398. traceDebugEntry.vStart = ray_local.m_Start + ray_local.m_StartOffset;
  1399. traceDebugEntry.vEnd = ray_local.m_Start + ray_local.m_Delta + ray_local.m_StartOffset;
  1400. traceDebugEntry.pPortal = pPortalPlayer->m_hPortalEnvironment;
  1401. traceDebugEntry.iCount = GetCallStack_Fast( traceDebugEntry.pStack, 32, 0 );
  1402. traceDebugEntry.ray = ray_local;
  1403. UTIL_ClearTrace( traceDebugEntry.realTrace );
  1404. UTIL_ClearTrace( traceDebugEntry.portalTrace1 );
  1405. UTIL_ClearTrace( traceDebugEntry.portalTrace2 );
  1406. UTIL_ClearTrace( traceDebugEntry.remoteTubeTrace );
  1407. UTIL_ClearTrace( traceDebugEntry.angledWallTrace );
  1408. UTIL_ClearTrace( traceDebugEntry.finalTrace );
  1409. traceDebugEntry.bCopiedRemoteTubeTrace = false;
  1410. traceDebugEntry.bCopiedAngledWallTrace = false;
  1411. traceDebugEntry.bCopiedPortal2Trace = false;
  1412. }
  1413. ++s_iLastTrace;
  1414. s_iLastTrace = s_iLastTrace % 32;
  1415. #endif
  1416. trace_t RealTrace;
  1417. UTIL_ClearTrace( RealTrace );
  1418. enginetrace->TraceRay( ray_local, fMask, &traceFilter, &RealTrace );
  1419. TRACE_DEBUG_ONLY( traceDebugEntry.realTrace = RealTrace );
  1420. /*RayInPortalHoleResult_t rayPortalIntersection = RIPHR_NOT_TOUCHING_HOLE;
  1421. if( pPortal && pPortal->m_PortalSimulator.IsReadyToSimulate() )
  1422. {
  1423. rayPortalIntersection = pPortal->m_PortalSimulator.IsRayInPortalHole( ray_local );
  1424. }
  1425. if( rayPortalIntersection != RIPHR_NOT_TOUCHING_HOLE )*/
  1426. if( pPortal && pPortal->IsActivedAndLinked() && pPortal->m_PortalSimulator.IsReadyToSimulate() && (RealTrace.startsolid || (ray_local.m_IsSwept && (RealTrace.fraction < 1.0f))) )
  1427. {
  1428. #if defined( DEBUG_PORTAL_TRACE_BOXES )
  1429. color24 debugBoxColor = { 0, 0, 0 };
  1430. #endif
  1431. trace_t PortalTrace;
  1432. UTIL_ClearTrace( PortalTrace );
  1433. UTIL_Portal_TraceRay( pPortal, ray_local, fMask, &traceFilter, &PortalTrace, true );
  1434. TRACE_DEBUG_ONLY( traceDebugEntry.portalTrace1 = PortalTrace );
  1435. //Assert( RealTrace.startsolid || !PortalTrace.startsolid || (RealTrace.plane.dist == PortalTrace.plane.dist) );
  1436. if( RealTrace.startsolid || (!PortalTrace.startsolid && (ray_local.m_IsSwept && (PortalTrace.fraction >= RealTrace.fraction))) ) //portal trace provides a better option
  1437. {
  1438. #if defined( DEBUG_PORTAL_TRACE_BOXES )
  1439. if( !PortalTrace.startsolid )
  1440. {
  1441. debugBoxColor.g = 255;
  1442. }
  1443. #endif
  1444. if( RealTrace.m_pEnt && RealTrace.m_pEnt->IsWorld() )
  1445. {
  1446. RealTrace.m_pEnt = pPortal->m_PortalSimulator.GetInternalData().Simulation.hCollisionEntity;
  1447. }
  1448. const PS_InternalData_t &portalSimulator = pPortal->m_PortalSimulator.GetInternalData();
  1449. CPortal_Base2D *pLinkedPortal = pPortal->m_hLinkedPortal;
  1450. //const PS_InternalData_t &linkedPortalSimulator = pLinkedPortal->m_PortalSimulator.GetInternalData();
  1451. bool bContactedTransitionRamp = false;
  1452. trace_t PortalTrace2;
  1453. UTIL_ClearTrace( PortalTrace2 );
  1454. #define REMOTE_TRACE_WALL_IS_ONLY_TUBE
  1455. //trace as an AABB against only the world in the remote space. We don't trace entities because of the projected floor to wall dillemma where we can ledge walk in the middle of the portal
  1456. CTraceTypeWrapper worldOnlyTraceFilterWrapper( TRACE_WORLD_ONLY, &traceFilter );
  1457. ITraceFilter *pRemoteTraceFilter;
  1458. if( sv_portal_new_player_trace_vs_remote_ents.GetBool() ) //weird, couldn't use ternary operator for this assignment
  1459. {
  1460. pRemoteTraceFilter = &traceFilter;
  1461. }
  1462. else
  1463. {
  1464. pRemoteTraceFilter = &worldOnlyTraceFilterWrapper;
  1465. }
  1466. #if defined( REMOTE_TRACE_WALL_IS_ONLY_TUBE )
  1467. UTIL_Portal_TraceRay( pLinkedPortal, ray_remote, fMask, pRemoteTraceFilter, &PortalTrace2, false );
  1468. #else
  1469. UTIL_Portal_TraceRay( pLinkedPortal, ray_remote, fMask, pRemoteTraceFilter, &PortalTrace2, true );
  1470. #endif
  1471. TRACE_DEBUG_ONLY( traceDebugEntry.portalTrace2 = PortalTrace2 );
  1472. //slightly angled portal transitions can present their remote-space collision as an extremely steep slope.
  1473. //Step code fails on that kind of step because it's both non-standable and just barely too far forward to step onto
  1474. //this next bit is to detect those cases and present the slope (no matter how steep) as standable
  1475. if( portalSimulator.Placement.pAABBAngleTransformCollideable )
  1476. {
  1477. trace_t AngleTrace;
  1478. UTIL_ClearTrace( AngleTrace );
  1479. physcollision->TraceBox( ray_remote, portalSimulator.Placement.pAABBAngleTransformCollideable, portalSimulator.Placement.ptaap_ThisToLinked.ptOriginTransform, portalSimulator.Placement.ptaap_ThisToLinked.qAngleTransform, &AngleTrace );
  1480. TRACE_DEBUG_ONLY( traceDebugEntry.angledWallTrace = AngleTrace );
  1481. //allow super steep slope climbs if the player is contacting the transition ramp
  1482. if( AngleTrace.startsolid || (ray_remote.m_IsSwept && (AngleTrace.fraction <= PortalTrace.fraction) && (AngleTrace.fraction < 1.0f)) ) //note, only has to go as far as local trace to work
  1483. {
  1484. bContactedTransitionRamp = true;
  1485. #if defined( DEBUG_PORTAL_TRACE_BOXES )
  1486. debugBoxColor.r = 255;
  1487. #endif
  1488. }
  1489. #if 0 //on second thought, maybe you shouldn't actually walk on this magic ramp
  1490. if( ray_remote.m_IsSwept && (AngleTrace.fraction < PortalTrace2.fraction) )
  1491. {
  1492. PortalTrace2 = AngleTrace;
  1493. bContactedTransitionRamp = true;
  1494. TRACE_DEBUG_ONLY( traceDebugEntry.bCopiedAngledWallTrace = true );
  1495. }
  1496. #endif
  1497. }
  1498. #if defined( REMOTE_TRACE_WALL_IS_ONLY_TUBE )
  1499. //This trace is SUPER IMPORTANT. While the AABB will always be consistent. It can be relatively different when portalling.
  1500. //We need to test that the player will fit through the portal in both configurations.
  1501. //To do that, we'll create a super AABB which will be a local-space maximal AABB of the local AABB and the transformed OBB
  1502. if( !bSkipRemoteTubeCheck && portalSimulator.Simulation.Static.Wall.Local.Tube.pCollideable )
  1503. {
  1504. trace_t TubeTrace;
  1505. UTIL_ClearTrace( TubeTrace );
  1506. physcollision->TraceBox( ray_remote, portalSimulator.Simulation.Static.Wall.Local.Tube.pCollideable, portalSimulator.Placement.ptaap_ThisToLinked.ptOriginTransform, portalSimulator.Placement.ptaap_ThisToLinked.qAngleTransform, &TubeTrace );
  1507. TRACE_DEBUG_ONLY( traceDebugEntry.remoteTubeTrace = TubeTrace );
  1508. if( TubeTrace.startsolid || (ray_remote.m_IsSwept && (TubeTrace.fraction < PortalTrace2.fraction)) )
  1509. {
  1510. PortalTrace2 = TubeTrace;
  1511. PortalTrace2.surface = portalSimulator.Simulation.Static.SurfaceProperties.surface;
  1512. PortalTrace2.contents = portalSimulator.Simulation.Static.SurfaceProperties.contents;
  1513. PortalTrace2.m_pEnt = portalSimulator.Simulation.Static.SurfaceProperties.pEntity;
  1514. TRACE_DEBUG_ONLY( traceDebugEntry.bCopiedRemoteTubeTrace = true );
  1515. }
  1516. }
  1517. #endif
  1518. //if the remote portal trace yielded collision results, translate them back to local space
  1519. if( PortalTrace2.startsolid || (ray_local.m_IsSwept && (PortalTrace2.fraction < PortalTrace.fraction)) )
  1520. {
  1521. #if defined( DBGFLAG_ASSERT )
  1522. Vector vTranslatedStart = (pLinkedPortal->m_matrixThisToLinked * (PortalTrace2.startpos - vRemoteShift - ray_remote.m_StartOffset)) + ray_local.m_StartOffset;
  1523. Assert( RealTrace.startsolid || ((vTranslatedStart - RealTrace.startpos).Length() < 0.1f) );
  1524. #endif
  1525. //transformed trace hit something sooner than local trace. Have to respect it, we'd be hitting it if we teleported this frame
  1526. PortalTrace = PortalTrace2;
  1527. PortalTrace.m_pEnt = portalSimulator.Simulation.hCollisionEntity;
  1528. PortalTrace.startpos = RealTrace.startpos;
  1529. PortalTrace.endpos = (pLinkedPortal->m_matrixThisToLinked * (PortalTrace2.endpos - vRemoteShift - ray_remote.m_StartOffset)) + ray_local.m_StartOffset;
  1530. //PortalTrace.endpos = RealTrace.startpos + (ray_local.m_Delta * PortalTrace2.fraction);
  1531. PortalTrace.plane.normal = pLinkedPortal->m_matrixThisToLinked.ApplyRotation( PortalTrace2.plane.normal );
  1532. Vector vTranslatedPointOnPlane = pLinkedPortal->m_matrixThisToLinked * ((PortalTrace2.plane.normal * PortalTrace2.plane.dist) - vRemoteShift);
  1533. PortalTrace.plane.dist = PortalTrace.plane.normal.Dot( vTranslatedPointOnPlane );
  1534. TRACE_DEBUG_ONLY( traceDebugEntry.bCopiedPortal2Trace = true );
  1535. #if defined( DEBUG_PORTAL_TRACE_BOXES )
  1536. debugBoxColor.b = 255;
  1537. //NDebugOverlay::Box( (PortalTrace2.endpos - ray_remote.m_StartOffset - vRemoteShift), -ray_remote.m_Extents, ray_remote.m_Extents, 0, 0, 255, 100, 0.0f );
  1538. //NDebugOverlay::Box( (pLinkedPortal->m_matrixThisToLinked * (PortalTrace2.endpos - ray_remote.m_StartOffset)), -ray_local.m_Extents, ray_local.m_Extents, 0, 0, 255, 100, 0.0f );
  1539. #endif
  1540. }
  1541. //verify that the portal trace is still the better option.
  1542. //Several cases can fail this part
  1543. // 1. We're simply "near" the portal and hitting remote collision geometry that overlaps our local space in non-euclidean ways
  1544. // 2. The collision represention of the wall isn't as precise as we'd like and juts out a bit from the actual wall
  1545. // 3. The translated portal tube or collision juts out from real space just a bit. If we' switch over to portal traces, we have to commit 100%
  1546. if( RealTrace.startsolid || (!PortalTrace.startsolid && (ray_local.m_IsSwept && (PortalTrace.fraction >= RealTrace.fraction))) )
  1547. {
  1548. *((trace_t *)&pm) = PortalTrace;
  1549. pm.m_bContactedPortalTransitionRamp = bContactedTransitionRamp;
  1550. }
  1551. else //portal trace results got crappy after incorporating AABB vs remote environment results, and real trace still valid
  1552. {
  1553. *((trace_t *)&pm) = RealTrace;
  1554. pm.m_bContactedPortalTransitionRamp = false;
  1555. }
  1556. }
  1557. else //real trace gives us better results
  1558. {
  1559. *((trace_t *)&pm) = RealTrace;
  1560. pm.m_bContactedPortalTransitionRamp = false;
  1561. }
  1562. #if defined( DEBUG_PORTAL_TRACE_BOXES )
  1563. if( sv_portal_new_trace_debugboxes.GetBool() )
  1564. {
  1565. NDebugOverlay::Box( ray_remote.m_Start, -ray_remote.m_Extents, ray_remote.m_Extents, debugBoxColor.r, debugBoxColor.g, debugBoxColor.b, 100, 0.0f );
  1566. NDebugOverlay::Box( ray_local.m_Start, -ray_local.m_Extents, ray_local.m_Extents, debugBoxColor.r, debugBoxColor.g, debugBoxColor.b, 100, 0.0f );
  1567. }
  1568. #endif
  1569. }
  1570. else //ray not touching portal hole
  1571. {
  1572. *((trace_t *)&pm) = RealTrace;
  1573. pm.m_bContactedPortalTransitionRamp = false;
  1574. }
  1575. TRACE_DEBUG_ONLY( traceDebugEntry.finalTrace = *((trace_t *)&pm) );
  1576. }
  1577. else
  1578. {
  1579. //old trace behavior
  1580. UTIL_Portal_TraceRay_With( pPortal, ray_local, fMask, &traceFilter, &pm );
  1581. // If we're moving through a portal and failed to hit anything with the above ray trace
  1582. // Use UTIL_Portal_TraceEntity to test this movement through a portal and override the trace with the result
  1583. if ( pm.fraction == 1.0f && UTIL_DidTraceTouchPortals( ray_local, pm ) && sv_player_trace_through_portals.GetBool() )
  1584. {
  1585. trace_t tempTrace;
  1586. Vector start = ray_local.m_Start + ray_local.m_StartOffset;
  1587. UTIL_Portal_TraceEntity( pPortalPlayer, start, start + ray_local.m_Delta, fMask, &traceFilter, &tempTrace );
  1588. if ( tempTrace.DidHit() && tempTrace.fraction < pm.fraction && !tempTrace.startsolid && !tempTrace.allsolid )
  1589. {
  1590. *((trace_t *)&pm) = tempTrace;
  1591. pm.m_bContactedPortalTransitionRamp = false;
  1592. }
  1593. }
  1594. }
  1595. }
  1596. void CPortalGameMovement::TracePlayerBBox( const Vector& start, const Vector& end, unsigned int fMask, int collisionGroup, CTrace_PlayerAABB_vs_Portals& pm )
  1597. {
  1598. VPROF( "CGameMovement::TracePlayerBBox" );
  1599. //UTIL_ClearTrace( pm );
  1600. //memset( &pm, 0, sizeof( trace_t ) );
  1601. CPortal_Player *pPortalPlayer = (CPortal_Player *)((CBaseEntity *)mv->m_nPlayerHandle.Get());
  1602. CPortal_Base2D *pPortal = pPortalPlayer->m_hPortalEnvironment;
  1603. Ray_t ray_local;
  1604. ray_local.Init( start, end, GetPlayerMins(), GetPlayerMaxs() );
  1605. if( pPortal && pPortal->IsActivedAndLinked() )
  1606. {
  1607. const PS_InternalData_t &portalSimulator = pPortal->m_PortalSimulator.GetInternalData();
  1608. CPortal_Base2D *pLinkedPortal = pPortal->m_hLinkedPortal;
  1609. const PS_InternalData_t &linkedPortalSimulator = pLinkedPortal->m_PortalSimulator.GetInternalData();
  1610. Vector vTeleportExtents = ray_local.m_Extents; //if we would adjust our extents due to a portal teleport, that change should be reflected here
  1611. //Need to take AABB extent changes from possible ducking into account
  1612. //bool bForcedDuck = false;
  1613. bool bDuckToFit = ShouldPortalTransitionCrouch( pPortal );
  1614. bool bDuckToFling = ShouldMaintainFlingAssistCrouch( pLinkedPortal, pPortal->m_matrixThisToLinked.ApplyRotation( mv->m_vecVelocity ) );
  1615. if( bDuckToFit || bDuckToFling )
  1616. {
  1617. //significant change in up axis
  1618. //bForcedDuck = true;
  1619. vTeleportExtents = (pPortalPlayer->GetDuckHullMaxs() - pPortalPlayer->GetDuckHullMins()) * 0.5f;
  1620. }
  1621. Vector vRemoteShift = CalculateExtentShift( ray_local.m_Extents, portalSimulator.Placement.PortalPlane.m_Normal, vTeleportExtents, linkedPortalSimulator.Placement.PortalPlane.m_Normal );
  1622. //this is roughly the ray we'd use if we had just teleported, the exception being the centered startoffset
  1623. Ray_t ray_remote = ray_local;
  1624. ray_remote.m_Start = (pPortal->m_matrixThisToLinked * ray_local.m_Start) + vRemoteShift;
  1625. ray_remote.m_Delta = pPortal->m_matrixThisToLinked.ApplyRotation( ray_local.m_Delta );
  1626. ray_remote.m_StartOffset = vec3_origin;
  1627. //ray_remote.m_StartOffset.z -= vTeleportExtents.z;
  1628. ray_remote.m_Extents = vTeleportExtents;
  1629. TracePortalPlayerAABB( pPortalPlayer, pPortal, ray_local, ray_remote, vRemoteShift, fMask, collisionGroup, pm, bDuckToFling );
  1630. }
  1631. else
  1632. {
  1633. TracePortalPlayerAABB( pPortalPlayer, NULL, ray_local, ray_local, vec3_origin, fMask, collisionGroup, pm );
  1634. }
  1635. }
  1636. void CPortalGameMovement::TracePlayerBBox( const Vector& start, const Vector& end, unsigned int fMask, int collisionGroup, trace_t& pm )
  1637. {
  1638. CTrace_PlayerAABB_vs_Portals pm2;
  1639. *(trace_t *)&pm2 = pm;
  1640. TracePlayerBBox( start, end, fMask, collisionGroup, pm2 );
  1641. pm = *(trace_t *)&pm2;
  1642. }
  1643. void PortalTracePlayerBBoxForGround( const Vector& start, const Vector& end, const Vector& minsSrc,
  1644. const Vector& maxsSrc, IHandleEntity *player, unsigned int fMask,
  1645. int collisionGroup, CTrace_PlayerAABB_vs_Portals& pm, const Vector& vStickNormal )
  1646. {
  1647. VPROF( "PortalTracePlayerBBoxForGround" );
  1648. CPortal_Player *pPortalPlayer = dynamic_cast<CPortal_Player *>(player->GetRefEHandle().Get());
  1649. CPortal_Base2D *pPlayerPortal = pPortalPlayer->m_hPortalEnvironment;
  1650. pm.m_bContactedPortalTransitionRamp = false;
  1651. #ifndef CLIENT_DLL
  1652. if( pPlayerPortal && pPlayerPortal->m_PortalSimulator.IsReadyToSimulate() == false )
  1653. pPlayerPortal = NULL;
  1654. #endif
  1655. Vector vTransformedStart, vTransformedEnd;
  1656. Vector transformed_minsSrc, transformed_maxsSrc;
  1657. Vector vRemoteShift = vec3_origin;
  1658. bool bSkipRemoteTube = false;
  1659. if( pPlayerPortal && pPlayerPortal->IsActivedAndLinked() && sv_portal_new_player_trace.GetBool() )
  1660. {
  1661. const PS_InternalData_t &portalSimulator = pPlayerPortal->m_PortalSimulator.GetInternalData();
  1662. CPortal_Base2D *pLinkedPortal = pPlayerPortal->m_hLinkedPortal;
  1663. const PS_InternalData_t &linkedPortalSimulator = pLinkedPortal->m_PortalSimulator.GetInternalData();
  1664. Vector vOriginToCenter = (maxsSrc + minsSrc) * 0.5f;
  1665. vTransformedStart = pPlayerPortal->m_matrixThisToLinked * (start + vOriginToCenter);
  1666. vTransformedEnd = pPlayerPortal->m_matrixThisToLinked * (end + vOriginToCenter);
  1667. transformed_minsSrc = minsSrc;
  1668. transformed_maxsSrc = maxsSrc;
  1669. bool bDuckToFit = ShouldPortalTransitionCrouch( pPlayerPortal );
  1670. bool bDuckToFling = ShouldMaintainFlingAssistCrouch( pLinkedPortal, pLinkedPortal->m_matrixThisToLinked.ApplyRotation( pPortalPlayer->GetAbsVelocity() ) );
  1671. //Need to take AABB extent changes from possible ducking into account
  1672. if( bDuckToFit || bDuckToFling )
  1673. {
  1674. //significant change in up axis
  1675. transformed_minsSrc = pPortalPlayer->GetDuckHullMins();
  1676. transformed_maxsSrc = pPortalPlayer->GetDuckHullMaxs();
  1677. bSkipRemoteTube = bDuckToFling;
  1678. }
  1679. Vector vLocalExtents = (maxsSrc - minsSrc) * 0.5f;
  1680. Vector vTeleportExtents = (transformed_maxsSrc - transformed_minsSrc) * 0.5f;
  1681. vRemoteShift = CalculateExtentShift( vLocalExtents, portalSimulator.Placement.PortalPlane.m_Normal, vTeleportExtents, linkedPortalSimulator.Placement.PortalPlane.m_Normal );
  1682. //just recompute origin offset for extent shifting again in case it changed
  1683. vOriginToCenter = (transformed_maxsSrc + transformed_minsSrc) * 0.5f;
  1684. //transformed_minsSrc -= vOriginToCenter;
  1685. //transformed_maxsSrc -= vOriginToCenter;
  1686. vTransformedStart -= vOriginToCenter;
  1687. vTransformedEnd -= vOriginToCenter;
  1688. }
  1689. else
  1690. {
  1691. vTransformedStart = start;
  1692. vTransformedEnd = end;
  1693. transformed_minsSrc = minsSrc;
  1694. transformed_maxsSrc = maxsSrc;
  1695. }
  1696. Ray_t ray_local, ray_remote;
  1697. Vector mins, maxs;
  1698. float fraction = pm.fraction;
  1699. Vector endpos = pm.endpos;
  1700. // Check the -x, -y quadrant
  1701. mins = minsSrc;
  1702. maxs.Init( MIN( 0, maxsSrc.x ), MIN( 0, maxsSrc.y ), maxsSrc.z );
  1703. ray_local.Init( start, end, mins, maxs );
  1704. mins = transformed_minsSrc;
  1705. maxs.Init( MIN( 0, transformed_maxsSrc.x ), MIN( 0, transformed_maxsSrc.y ), transformed_maxsSrc.z );
  1706. ray_remote.Init( vTransformedStart, vTransformedEnd, mins, maxs );
  1707. ray_remote.m_Start += vRemoteShift;
  1708. ray_remote.m_StartOffset += vRemoteShift;
  1709. if( pPlayerPortal )
  1710. {
  1711. if( sv_portal_new_player_trace.GetBool() )
  1712. {
  1713. TracePortalPlayerAABB( pPortalPlayer, pPlayerPortal, ray_local, ray_remote, vRemoteShift, fMask, collisionGroup, pm, bSkipRemoteTube );
  1714. }
  1715. else
  1716. {
  1717. UTIL_Portal_TraceRay( pPlayerPortal, ray_local, fMask, player, collisionGroup, &pm );
  1718. }
  1719. }
  1720. else
  1721. {
  1722. UTIL_TraceRay( ray_local, fMask, player, collisionGroup, &pm );
  1723. }
  1724. float flNormalCos = DotProduct( pm.plane.normal, vStickNormal );
  1725. if ( pm.m_pEnt && ((flNormalCos >= 0.7f) || pm.HitPortalRamp(Vector( 0.0f, 0.0f, 1.0f ))) )
  1726. {
  1727. pm.fraction = fraction;
  1728. pm.endpos = endpos;
  1729. return;
  1730. }
  1731. // Check the +x, +y quadrant
  1732. mins.Init( MAX( 0, minsSrc.x ), MAX( 0, minsSrc.y ), minsSrc.z );
  1733. maxs = maxsSrc;
  1734. ray_local.Init( start, end, mins, maxs );
  1735. mins.Init( MAX( 0, transformed_minsSrc.x ), MAX( 0, transformed_minsSrc.y ), transformed_minsSrc.z );
  1736. maxs = transformed_maxsSrc;
  1737. ray_remote.Init( vTransformedStart, vTransformedEnd, mins, maxs );
  1738. ray_remote.m_Start += vRemoteShift;
  1739. ray_remote.m_StartOffset += vRemoteShift;
  1740. if( pPlayerPortal )
  1741. {
  1742. if( sv_portal_new_player_trace.GetBool() )
  1743. {
  1744. TracePortalPlayerAABB( pPortalPlayer, pPlayerPortal, ray_local, ray_remote, vRemoteShift, fMask, collisionGroup, pm, bSkipRemoteTube );
  1745. }
  1746. else
  1747. {
  1748. UTIL_Portal_TraceRay( pPlayerPortal, ray_local, fMask, player, collisionGroup, &pm );
  1749. }
  1750. }
  1751. else
  1752. {
  1753. UTIL_TraceRay( ray_local, fMask, player, collisionGroup, &pm );
  1754. }
  1755. flNormalCos = DotProduct( pm.plane.normal, vStickNormal );
  1756. if ( pm.m_pEnt && ((flNormalCos >= 0.7f) || pm.HitPortalRamp(Vector( 0.0f, 0.0f, 1.0f ))) )
  1757. {
  1758. pm.fraction = fraction;
  1759. pm.endpos = endpos;
  1760. return;
  1761. }
  1762. // Check the -x, +y quadrant
  1763. mins.Init( minsSrc.x, MAX( 0, minsSrc.y ), minsSrc.z );
  1764. maxs.Init( MIN( 0, maxsSrc.x ), maxsSrc.y, maxsSrc.z );
  1765. ray_local.Init( start, end, mins, maxs );
  1766. mins.Init( transformed_minsSrc.x, MAX( 0, transformed_minsSrc.y ), transformed_minsSrc.z );
  1767. maxs.Init( MIN( 0, transformed_maxsSrc.x ), transformed_maxsSrc.y, transformed_maxsSrc.z );
  1768. ray_remote.Init( vTransformedStart, vTransformedEnd, mins, maxs );
  1769. ray_remote.m_Start += vRemoteShift;
  1770. ray_remote.m_StartOffset += vRemoteShift;
  1771. if( pPlayerPortal )
  1772. {
  1773. if( sv_portal_new_player_trace.GetBool() )
  1774. {
  1775. TracePortalPlayerAABB( pPortalPlayer, pPlayerPortal, ray_local, ray_remote, vRemoteShift, fMask, collisionGroup, pm, bSkipRemoteTube );
  1776. }
  1777. else
  1778. {
  1779. UTIL_Portal_TraceRay( pPlayerPortal, ray_local, fMask, player, collisionGroup, &pm );
  1780. }
  1781. }
  1782. else
  1783. {
  1784. UTIL_TraceRay( ray_local, fMask, player, collisionGroup, &pm );
  1785. }
  1786. flNormalCos = DotProduct( pm.plane.normal, vStickNormal );
  1787. if ( pm.m_pEnt && ((flNormalCos >= 0.7f) || pm.HitPortalRamp(Vector( 0.0f, 0.0f, 1.0f ))) )
  1788. {
  1789. pm.fraction = fraction;
  1790. pm.endpos = endpos;
  1791. return;
  1792. }
  1793. // Check the +x, -y quadrant
  1794. mins.Init( MAX( 0, minsSrc.x ), minsSrc.y, minsSrc.z );
  1795. maxs.Init( maxsSrc.x, MIN( 0, maxsSrc.y ), maxsSrc.z );
  1796. ray_local.Init( start, end, mins, maxs );
  1797. mins.Init( MAX( 0, transformed_minsSrc.x ), transformed_minsSrc.y, transformed_minsSrc.z );
  1798. maxs.Init( transformed_maxsSrc.x, MIN( 0, transformed_maxsSrc.y ), transformed_maxsSrc.z );
  1799. ray_remote.Init( vTransformedStart, vTransformedEnd, mins, maxs );
  1800. ray_remote.m_Start += vRemoteShift;
  1801. ray_remote.m_StartOffset += vRemoteShift;
  1802. if( pPlayerPortal )
  1803. {
  1804. if( sv_portal_new_player_trace.GetBool() )
  1805. {
  1806. TracePortalPlayerAABB( pPortalPlayer, pPlayerPortal, ray_local, ray_remote, vRemoteShift, fMask, collisionGroup, pm, bSkipRemoteTube );
  1807. }
  1808. else
  1809. {
  1810. UTIL_Portal_TraceRay( pPlayerPortal, ray_local, fMask, player, collisionGroup, &pm );
  1811. }
  1812. }
  1813. else
  1814. {
  1815. UTIL_TraceRay( ray_local, fMask, player, collisionGroup, &pm );
  1816. }
  1817. flNormalCos = DotProduct( pm.plane.normal, vStickNormal );
  1818. if ( pm.m_pEnt && ((flNormalCos >= 0.7f) || pm.HitPortalRamp(Vector( 0.0f, 0.0f, 1.0f ))) )
  1819. {
  1820. pm.fraction = fraction;
  1821. pm.endpos = endpos;
  1822. return;
  1823. }
  1824. pm.fraction = fraction;
  1825. pm.endpos = endpos;
  1826. }
  1827. CBaseHandle CPortalGameMovement::TestPlayerPosition( const Vector& pos, int collisionGroup, trace_t& pm )
  1828. {
  1829. TracePlayerBBox( pos, pos, MASK_PLAYERSOLID, collisionGroup, pm ); //hook into the existing portal special trace functionality
  1830. //Ray_t ray;
  1831. //ray.Init( pos, pos, GetPlayerMins(), GetPlayerMaxs() );
  1832. //UTIL_TraceRay( ray, MASK_PLAYERSOLID, mv->m_nPlayerHandle.Get(), collisionGroup, &pm );
  1833. if( pm.startsolid && pm.m_pEnt && (pm.contents & MASK_PLAYERSOLID) )
  1834. {
  1835. #ifdef _DEBUG
  1836. Warning( "The player got stuck on something. Break in portal_gamemovement.cpp on line " __LINE__AS_STRING " to investigate\n" ); //happens enough to just leave in a perma-debugger
  1837. //now that you're here, go up and re-run the TracePlayerBBox() above
  1838. #endif
  1839. #if defined( TRACE_DEBUG_ENABLED )
  1840. g_bTraceDebugging = true;
  1841. TracePlayerBBox( pos, pos, MASK_PLAYERSOLID, collisionGroup, pm ); //hook into the existing portal special trace functionality
  1842. #endif
  1843. return pm.m_pEnt->GetRefEHandle();
  1844. }
  1845. #ifndef CLIENT_DLL
  1846. else if ( pm.startsolid && pm.m_pEnt && CPSCollisionEntity::IsPortalSimulatorCollisionEntity( pm.m_pEnt ) )
  1847. {
  1848. // Stuck in a portal environment object, so unstick them!
  1849. CPortal_Player *pPortalPlayer = (CPortal_Player *)((CBaseEntity *)mv->m_nPlayerHandle.Get());
  1850. pPortalPlayer->SetStuckOnPortalCollisionObject();
  1851. return INVALID_EHANDLE_INDEX;
  1852. }
  1853. #endif
  1854. else
  1855. {
  1856. return INVALID_EHANDLE_INDEX;
  1857. }
  1858. }
  1859. void CPortalGameMovement::HandlePortalling( void )
  1860. {
  1861. matrix3x4_t matAngleTransformIn, matAngleTransformOut; //temps for angle transformation
  1862. CPortal_Player *pPortalPlayer = ToPortalPlayer( player );
  1863. #if ( PLAYERPORTALDEBUGSPEW == 1 )
  1864. # if defined( CLIENT_DLL )
  1865. const char *szDLLName = "client";
  1866. # else
  1867. const char *szDLLName = "--server--";
  1868. # endif
  1869. #endif
  1870. #if defined( CLIENT_DLL )
  1871. pPortalPlayer->UnrollPredictedTeleportations( player->m_pCurrentCommand->command_number );
  1872. pPortalPlayer->CheckPlayerAboutToTouchPortal();
  1873. #endif
  1874. Vector vOriginToCenter = (pPortalPlayer->GetHullMaxs() + pPortalPlayer->GetHullMins()) * 0.5f;
  1875. Vector vMoveCenter = mv->GetAbsOrigin() + vOriginToCenter;
  1876. Vector vPrevMoveCenter = m_vMoveStartPosition + vOriginToCenter; //whatever the origin was before we ran this move
  1877. Vector vPlayerExtentsFromCenter = (pPortalPlayer->GetHullMaxs() - pPortalPlayer->GetHullMins()) * 0.5f;
  1878. //Vector vAppliedMoveCenter = pPortalPlayer->GetAbsOrigin() + vOriginToCenter;
  1879. #if defined( TRACE_DEBUG_ENABLED )
  1880. CheckStuck();
  1881. #endif
  1882. #if defined( DEBUG_FLINGS )
  1883. if( pPortalPlayer->GetGroundEntity() != NULL )
  1884. {
  1885. s_MovementDebug.bWatchFlingVelocity = false;
  1886. }
  1887. #endif
  1888. CPortal_Base2D *pPortalEnvironment = pPortalPlayer->m_hPortalEnvironment.Get(); //the portal they were touching last move
  1889. CPortal_Base2D *pPortal = NULL;
  1890. {
  1891. Ray_t ray;
  1892. ray.Init( m_vMoveStartPosition, mv->GetAbsOrigin(), pPortalPlayer->GetHullMins(), pPortalPlayer->GetHullMaxs() );
  1893. //ray.Init( mv->GetAbsOrigin(), mv->GetAbsOrigin(), pPortalPlayer->GetHullMins(), pPortalPlayer->GetHullMaxs() );
  1894. int iPortalCount = CPortal_Base2D_Shared::AllPortals.Count();
  1895. CPortal_Base2D **pAllPortals = CPortal_Base2D_Shared::AllPortals.Base();
  1896. float fMaxDistSquared = FLT_MAX;
  1897. for( int i = 0; i != iPortalCount; ++i )
  1898. {
  1899. if( pAllPortals[i]->IsActivedAndLinked() )
  1900. {
  1901. trace_t tr;
  1902. UTIL_ClearTrace( tr );
  1903. if( pAllPortals[i]->TestCollision( ray, CONTENTS_SOLID, tr ) )
  1904. {
  1905. const PS_InternalData_t &portalSimulator = pAllPortals[i]->m_PortalSimulator.GetInternalData();
  1906. if( portalSimulator.Placement.PortalPlane.m_Normal.Dot( vPrevMoveCenter ) <= portalSimulator.Placement.PortalPlane.m_Dist )
  1907. {
  1908. //old origin must be in front of the plane
  1909. if( pAllPortals[i] != pPortalEnvironment ) //special exception if we were pushed past the plane but did not move past it
  1910. continue;
  1911. }
  1912. float fCenterDist = portalSimulator.Placement.PortalPlane.m_Normal.Dot( vMoveCenter ) - portalSimulator.Placement.PortalPlane.m_Dist;
  1913. if( fCenterDist < 0.0f )
  1914. {
  1915. //if new origin is behind the plane, require that the player center hover over the portal quad to be considered
  1916. Vector vPortalPlayerOriginDiff = vMoveCenter - portalSimulator.Placement.ptCenter;
  1917. vPortalPlayerOriginDiff -= vPortalPlayerOriginDiff.Dot( portalSimulator.Placement.PortalPlane.m_Normal ) * portalSimulator.Placement.PortalPlane.m_Normal;
  1918. if( (fabs( vPortalPlayerOriginDiff.Dot( portalSimulator.Placement.vRight ) ) > portalSimulator.Placement.fHalfWidth) ||
  1919. (fabs( vPortalPlayerOriginDiff.Dot( portalSimulator.Placement.vUp ) ) > portalSimulator.Placement.fHalfHeight) )
  1920. {
  1921. continue;
  1922. }
  1923. }
  1924. else
  1925. {
  1926. //require that a line from the center of the player to their most-penetrating extent passes through the portal quad
  1927. //Avoids case where you can butt up against a portal side on an angled panel
  1928. Vector vTestExtent = vMoveCenter;
  1929. vTestExtent.x -= Sign( portalSimulator.Placement.PortalPlane.m_Normal.x ) * vPlayerExtentsFromCenter.x;
  1930. vTestExtent.y -= Sign( portalSimulator.Placement.PortalPlane.m_Normal.y ) * vPlayerExtentsFromCenter.y;
  1931. vTestExtent.z -= Sign( portalSimulator.Placement.PortalPlane.m_Normal.z ) * vPlayerExtentsFromCenter.z;
  1932. float fTestDist = portalSimulator.Placement.PortalPlane.m_Normal.Dot( vTestExtent ) - portalSimulator.Placement.PortalPlane.m_Dist;
  1933. if( fTestDist < portal_player_interaction_quadtest_epsilon.GetFloat() )
  1934. {
  1935. float fTotalDist = fCenterDist - fTestDist;
  1936. if( fTotalDist != 0.0f )
  1937. {
  1938. Vector vPlanePoint = (vTestExtent * (fCenterDist/fTotalDist)) - (vMoveCenter * (fTestDist/fTotalDist));
  1939. Vector vPortalCenterToPlanePoint = vPlanePoint - portalSimulator.Placement.ptCenter;
  1940. if( (fabs( vPortalCenterToPlanePoint.Dot( portalSimulator.Placement.vRight ) ) > portalSimulator.Placement.fHalfWidth + 1.0f) ||
  1941. (fabs( vPortalCenterToPlanePoint.Dot( portalSimulator.Placement.vUp ) ) > portalSimulator.Placement.fHalfHeight + 1.0f) )
  1942. {
  1943. continue;
  1944. }
  1945. }
  1946. }
  1947. }
  1948. Vector vDiff = pAllPortals[i]->m_ptOrigin - vMoveCenter;
  1949. float fDistSqr = vDiff.LengthSqr();
  1950. if( fDistSqr < fMaxDistSquared )
  1951. {
  1952. pPortal = pAllPortals[i];
  1953. fMaxDistSquared = fDistSqr;
  1954. }
  1955. }
  1956. }
  1957. }
  1958. }
  1959. //CPortal_Base2D *pPortal = UTIL_IntersectEntityExtentsWithPortal( player );
  1960. CPortal_Base2D *pPlayerTouchingPortal = pPortal;
  1961. if( pPortal != NULL )
  1962. {
  1963. CPortal_Base2D *pExitPortal = pPortal->m_hLinkedPortal;
  1964. bool bMobile = pPortal->IsMobile() || pPortal->m_hLinkedPortal->IsMobile();
  1965. float fPlaneDist = pPortal->m_plane_Origin.normal.Dot( vMoveCenter ) - pPortal->m_plane_Origin.dist;
  1966. if( (fPlaneDist < -FLT_EPSILON) || //behind the portal plane
  1967. (bMobile && (pPortal->m_plane_Origin.normal.Dot( mv->m_vecVelocity ) < -FLT_EPSILON)) ) //trying to enter a mobile portal
  1968. {
  1969. // Capture our eye position before we start portalling
  1970. Vector vOldEyePos = mv->GetAbsOrigin() + pPortalPlayer->GetViewOffset();
  1971. #if (PLAYERPORTALDEBUGSPEW == 1)
  1972. bool bStartedDucked = ((player->GetFlags() & FL_DUCKING) != 0);
  1973. #endif
  1974. //compute when exactly we intersected the portal's plane
  1975. float fIntersectionPercentage;
  1976. float fPostPortalFrameTime;
  1977. {
  1978. float fOldPlaneDist = pPortal->m_plane_Origin.normal.Dot( vPrevMoveCenter ) - pPortal->m_plane_Origin.dist;
  1979. float fTotalDist = (fOldPlaneDist - fPlaneDist); //fPlaneDist is known to be negative
  1980. fIntersectionPercentage = (fTotalDist != 0.0f) ? (fOldPlaneDist / fTotalDist) : 0.5f; //but sometimes fOldPlaneDist is too, some kind of physics penetration seems to be the cause (bugbait #61331)
  1981. fPostPortalFrameTime = (1.0f - fIntersectionPercentage ) * gpGlobals->frametime;
  1982. }
  1983. #if 0 //debug spew
  1984. # if defined( CLIENT_DLL )
  1985. if( pPortalPlayer->m_PredictedPortalTeleportations.Count() > 1 )
  1986. {
  1987. Warning( "==================CPortalGameMovement::HandlePortalling(client) has built up more than 1 teleportation=====================\n" );
  1988. }
  1989. # endif
  1990. #endif
  1991. #if defined( GAME_DLL )
  1992. {
  1993. pPortal->PreTeleportTouchingEntity( player );
  1994. if ( pPortalPlayer->IsUsingVMGrab() )
  1995. {
  1996. CBaseEntity *pEntity = GetPlayerHeldEntity( pPortalPlayer );
  1997. if ( pEntity && pPortal )
  1998. {
  1999. Vector vCurPos = pEntity->GetLocalOrigin();
  2000. QAngle vCurAngles = pEntity->GetLocalAngles();
  2001. Vector vNewPos;
  2002. QAngle vNewAngles;
  2003. UTIL_Portal_PointTransform( pPortal->MatrixThisToLinked(), vCurPos, vNewPos );
  2004. UTIL_Portal_AngleTransform( pPortal->MatrixThisToLinked(), vCurAngles, vNewAngles );
  2005. pEntity->Teleport( &vNewPos, &vNewAngles, NULL );
  2006. }
  2007. }
  2008. // Notify the entity that it's being teleported
  2009. // Tell the teleported entity of the portal it has just arrived at
  2010. notify_teleport_params_t paramsTeleport;
  2011. paramsTeleport.prevOrigin = mv->GetAbsOrigin();
  2012. paramsTeleport.prevAngles = mv->m_vecViewAngles;
  2013. paramsTeleport.physicsRotate = true;
  2014. notify_system_event_params_t eventParams ( &paramsTeleport );
  2015. player->NotifySystemEvent( pPortal, NOTIFY_EVENT_TELEPORT, eventParams );
  2016. }
  2017. #if 0 //debug info for which portal teleported me
  2018. {
  2019. NDebugOverlay::EntityBounds( pPortal, 255, 0, 0, 100, 10.0f ); //portal that's teleporting us in red
  2020. NDebugOverlay::Box( pPortalPlayer->GetPreviouslyPredictedOrigin(), pPortalPlayer->GetHullMins(), pPortalPlayer->GetHullMaxs(), 0, 0, 255, 100, 10.0f ); //last player position in blue
  2021. NDebugOverlay::Box( mv->GetAbsOrigin(), pPortalPlayer->GetHullMins(), pPortalPlayer->GetHullMaxs(), 0, 255, 0, 100, 10.0f ); //current player position in green
  2022. NDebugOverlay::Box( (vOldMoveCenter * (1.0f - fIntersectionPercentage)) + (vMoveCenter * fIntersectionPercentage), -Vector( 1.0f, 1.0f, 1.0f ), Vector( 1.0f, 1.0f, 1.0f ), 0, 255, 255, 255, 10.0f );
  2023. }
  2024. #endif
  2025. #endif
  2026. matrix3x4_t matTransform = pPortal->MatrixThisToLinked().As3x4();
  2027. bool bWasOnGround = (player->GetFlags() & FL_ONGROUND) != 0;
  2028. player->SetGroundEntity( NULL );
  2029. //velocity
  2030. {
  2031. Vector vPostPortalGravityVelocity = bWasOnGround ? vec3_origin :
  2032. (m_vGravityDirection * (sv_gravity.GetFloat() * fPostPortalFrameTime * ((player->GetGravity() != 0) ? player->GetGravity() : 1.0f)));
  2033. Vector vTransformedVelocity;
  2034. Vector vVelocity = mv->m_vecVelocity;
  2035. //subtract out gravity for the portion of the frame after we portalled.
  2036. vVelocity -= vPostPortalGravityVelocity;
  2037. // Take the implicit vertical speed we get when walking along a sloped surface into account,
  2038. // since player velocity is only ever in the xy-plane while on the ground.
  2039. // Ignore this if we're entering a floor portal.
  2040. const float COS_PI_OVER_SIX = 0.86602540378443864676372317075294f; // cos( 30 degrees ) in radians
  2041. if( pPortal->m_plane_Origin.normal.z <= COS_PI_OVER_SIX )
  2042. {
  2043. # ifdef PORTAL2
  2044. vVelocity += pPortalPlayer->GetImplicitVerticalStepSpeed() * pPortalPlayer->GetPortalPlayerLocalData().m_StickNormal;
  2045. # else
  2046. vVelocity.z += pPortalPlayer->GetImplicitVerticalStepSpeed();
  2047. # endif
  2048. }
  2049. VectorRotate( vVelocity, matTransform, vTransformedVelocity );
  2050. //now add gravity for the post-portal portion of the frame back on, but to the transformed velocity
  2051. vTransformedVelocity += vPostPortalGravityVelocity * 1.008f; //Apply slightly more gravity on exit so that floor/floor portals trend towards decaying velocity. 1.008 is a magic number found through experimentation
  2052. //velocity hacks
  2053. {
  2054. float fExitMin, fExitMax;
  2055. Vector vTransformedMoveCenter;
  2056. VectorTransform( vMoveCenter, matTransform, vTransformedMoveCenter );
  2057. CPortal_Base2D::GetExitSpeedRange( pPortal, true, fExitMin, fExitMax, vTransformedMoveCenter, player );
  2058. float fForwardSpeed = vTransformedVelocity.Dot( pExitPortal->m_vForward );
  2059. if( fForwardSpeed < fExitMin )
  2060. {
  2061. vTransformedVelocity += pExitPortal->m_vForward * (fExitMin - fForwardSpeed);
  2062. }
  2063. else
  2064. {
  2065. float fSpeed = vTransformedVelocity.Length();
  2066. if( (fSpeed > fExitMax) && (fSpeed != 0.0f) )
  2067. {
  2068. vTransformedVelocity *= (fExitMax / fSpeed);
  2069. }
  2070. }
  2071. }
  2072. //cap it to the same absolute maximum that CGameMovement::CheckVelocity() would, but be quiet about it.
  2073. const float fAbsoluteMaxVelocity = sv_maxvelocity.GetFloat();
  2074. for( int i = 0; i != 3; ++i )
  2075. {
  2076. if (vTransformedVelocity[i] > fAbsoluteMaxVelocity)
  2077. {
  2078. vTransformedVelocity[i] = fAbsoluteMaxVelocity;
  2079. }
  2080. else if (vTransformedVelocity[i] < -fAbsoluteMaxVelocity)
  2081. {
  2082. vTransformedVelocity[i] = -fAbsoluteMaxVelocity;
  2083. }
  2084. }
  2085. mv->m_vecVelocity = vTransformedVelocity;
  2086. }
  2087. /* pPlayerTouchingPortal->TransformPlayerTeleportingPlayerOrigin( pPortalPlayer, vMoveCenter, vOriginToCenter, mv->m_vecVelocity );
  2088. mv->SetAbsOrigin( vMoveCenter );*/
  2089. bool bForceDuckToFit = ShouldPortalTransitionCrouch( pPlayerTouchingPortal );
  2090. bool bForceDuckToFlingAssist = ShouldMaintainFlingAssistCrouch( pPlayerTouchingPortal->m_hLinkedPortal, mv->m_vecVelocity );
  2091. bool bForceDuck = bForceDuckToFit || bForceDuckToFlingAssist;
  2092. //reduced version of forced duckjump
  2093. if( bForceDuck )
  2094. {
  2095. if( (player->GetFlags() & FL_DUCKING) == 0 ) //not already ducking
  2096. {
  2097. //DevMsg( "HandlePortalling: Force Duck\n" );
  2098. player->m_Local.m_bInDuckJump = true;
  2099. player->m_Local.m_nDuckTimeMsecs = GAMEMOVEMENT_DUCK_TIME;
  2100. FinishDuck(); //be ducked RIGHT NOW. Pulls feet up by delta between hull sizes.
  2101. Vector vNewOriginToCenter = (pPortalPlayer->GetDuckHullMaxs() + pPortalPlayer->GetDuckHullMins()) * 0.5f;
  2102. //Vector vNewCenter = mv->GetAbsOrigin() + vNewOriginToCenter;
  2103. //mv->SetAbsOrigin( mv->GetAbsOrigin() + (vMoveCenter - vNewCenter) ); //We need our center to where it started
  2104. vOriginToCenter = vNewOriginToCenter;
  2105. }
  2106. }
  2107. //transform by center
  2108. {
  2109. #if defined( DEBUG_FLINGS )
  2110. s_MovementDebug.vCenterNudge = vec3_origin;
  2111. #endif
  2112. Vector vTransformedMoveCenter;
  2113. VectorTransform( vMoveCenter, matTransform, vTransformedMoveCenter );
  2114. CPortal_Base2D *pExitPortal = pPortal->m_hLinkedPortal;
  2115. if( bMobile )
  2116. {
  2117. Vector vExtents = (pPortalPlayer->GetStandHullMaxs() - pPortalPlayer->GetStandHullMins()) * 0.5f;
  2118. const Vector &vPushNormal = pExitPortal->m_plane_Origin.normal;
  2119. float fAgreeableDot = fabs(vPushNormal.x * vExtents.x) + fabs(vPushNormal.y * vExtents.y) + fabs(vPushNormal.z * vExtents.z);
  2120. vTransformedMoveCenter = pExitPortal->m_ptOrigin + (pExitPortal->m_plane_Origin.normal * fAgreeableDot);
  2121. }
  2122. else
  2123. {
  2124. if( bForceDuckToFlingAssist || (bForceDuckToFit && (mv->m_vecVelocity.Dot( pPlayerTouchingPortal->m_hLinkedPortal->m_vForward ) > PLAYER_FLING_HELPER_MIN_SPEED)) )
  2125. {
  2126. Vector vExtents = (pPortalPlayer->GetDuckHullMaxs() - pPortalPlayer->GetDuckHullMins()) * 0.5f;
  2127. //HACK: people like to fling through portals, unfortunately their bbox changes enough to cause problems on fling exit
  2128. //the real world equivalent of stubbing your toe on the exit hole results in flinging straight up.
  2129. //The hack is to fix this by preventing you from stubbing your toe, move the player towards portal center axis
  2130. Vector vPortalToCenter = vTransformedMoveCenter - pExitPortal->m_ptOrigin;
  2131. Vector vOffCenter = vPortalToCenter - (vPortalToCenter.Dot( pExitPortal->m_vForward ) * pExitPortal->m_vForward);
  2132. //find the extent which is most likely to get hooked on the edge
  2133. Vector vTestPoint = vTransformedMoveCenter; //pExitPortal->m_ptOrigin + vOffCenter;
  2134. for( int k = 0; k != 3; ++k )
  2135. {
  2136. vTestPoint[k] += vExtents[k] * Sign( vOffCenter[k] );
  2137. }
  2138. Vector vPortalToTest = vTestPoint - pExitPortal->m_ptOrigin;
  2139. //project it onto the portal plane
  2140. //vPortalToTest = vPortalToTest - (vPortalToTest.Dot( pExitPortal->m_vForward ) * pExitPortal->m_vForward);
  2141. float fRight = vPortalToTest.Dot( pExitPortal->m_vRight );
  2142. float fUp = vPortalToTest.Dot( pExitPortal->m_vUp );
  2143. Vector vNudge = vec3_origin;
  2144. float fNudgeWidth = pExitPortal->GetHalfWidth() - 5.0f;
  2145. float fNudgeHeight = pExitPortal->GetHalfHeight() - 5.0f;
  2146. if( fRight > fNudgeWidth )
  2147. {
  2148. vNudge -= pExitPortal->m_vRight * (fRight - fNudgeWidth);
  2149. }
  2150. else if( fRight < -fNudgeWidth )
  2151. {
  2152. vNudge -= pExitPortal->m_vRight * (fRight + fNudgeWidth);
  2153. }
  2154. if( fUp > fNudgeHeight )
  2155. {
  2156. vNudge -= pExitPortal->m_vUp * (fUp - fNudgeHeight);
  2157. }
  2158. else if( fUp < -fNudgeHeight )
  2159. {
  2160. vNudge -= pExitPortal->m_vUp * (fUp + fNudgeHeight);
  2161. }
  2162. vTransformedMoveCenter += vNudge;
  2163. #if defined( DEBUG_FLINGS )
  2164. s_MovementDebug.vCenterNudge = vNudge;
  2165. #endif
  2166. }
  2167. }
  2168. mv->SetAbsOrigin( vTransformedMoveCenter - vOriginToCenter );
  2169. #if defined( CLIENT_DLL )
  2170. {
  2171. C_Portal_Player::PredictedPortalTeleportation_t entry;
  2172. entry.flTime = gpGlobals->curtime;
  2173. entry.pEnteredPortal = pPortal;
  2174. entry.iCommandNumber = player->m_pCurrentCommand->command_number;
  2175. entry.fDeleteServerTimeStamp = -1.0f;
  2176. entry.matUnroll = pPortal->m_hLinkedPortal->MatrixThisToLinked();
  2177. entry.bDuckForced = bForceDuck;
  2178. pPortalPlayer->m_PredictedPortalTeleportations.AddToTail( entry );
  2179. }
  2180. #endif
  2181. # if ( PLAYERPORTALDEBUGSPEW == 1 )
  2182. Warning( "-->CPortalGameMovement::HandlePortalling(%s) teleport player, portal %d, time %f, %d, Start Center: %.2f %.2f %.2f (%s) End: %.2f %.2f %.2f (%s)\n", szDLLName, pPortal->m_bIsPortal2 ? 2 : 1, gpGlobals->curtime, player->m_pCurrentCommand->command_number, XYZ( vMoveCenter ), bStartedDucked ? "duck":"stand", XYZ( vTransformedMoveCenter ), ((player->GetFlags() & FL_DUCKING) != 0) ? "duck":"stand" );
  2183. # endif
  2184. }
  2185. #if defined( CLIENT_DLL )
  2186. //engine view angles (for mouse input smoothness)
  2187. {
  2188. QAngle qEngineAngles;
  2189. engine->GetViewAngles( qEngineAngles );
  2190. AngleMatrix( qEngineAngles, matAngleTransformIn );
  2191. ConcatTransforms( matTransform, matAngleTransformIn, matAngleTransformOut );
  2192. MatrixAngles( matAngleTransformOut, qEngineAngles );
  2193. engine->SetViewAngles( qEngineAngles );
  2194. }
  2195. //predicted view angles
  2196. {
  2197. QAngle qPredViewAngles;
  2198. prediction->GetViewAngles( qPredViewAngles );
  2199. AngleMatrix( qPredViewAngles, matAngleTransformIn );
  2200. ConcatTransforms( matTransform, matAngleTransformIn, matAngleTransformOut );
  2201. MatrixAngles( matAngleTransformOut, qPredViewAngles );
  2202. prediction->SetViewAngles( qPredViewAngles );
  2203. }
  2204. #endif
  2205. //pl.v_angle
  2206. {
  2207. AngleMatrix( player->pl.v_angle, matAngleTransformIn );
  2208. ConcatTransforms( matTransform, matAngleTransformIn, matAngleTransformOut );
  2209. MatrixAngles( matAngleTransformOut, player->pl.v_angle );
  2210. }
  2211. //player entity angle
  2212. {
  2213. QAngle qPlayerAngle;
  2214. #if defined( GAME_DLL )
  2215. qPlayerAngle = player->GetAbsAngles();
  2216. #else
  2217. qPlayerAngle = player->GetNetworkAngles();
  2218. #endif
  2219. AngleMatrix( qPlayerAngle, matAngleTransformIn );
  2220. ConcatTransforms( matTransform, matAngleTransformIn, matAngleTransformOut );
  2221. MatrixAngles( matAngleTransformOut, qPlayerAngle );
  2222. #if defined( GAME_DLL )
  2223. player->SetAbsAngles( qPlayerAngle );
  2224. #else
  2225. player->SetNetworkAngles( qPlayerAngle );
  2226. #endif
  2227. }
  2228. #if defined( GAME_DLL )
  2229. //outputs that the portal usually fires
  2230. {
  2231. // Update the portal to know something moved through it
  2232. pPortal->OnEntityTeleportedFromPortal( player );
  2233. if ( pPortal->m_hLinkedPortal )
  2234. {
  2235. pPortal->m_hLinkedPortal->OnEntityTeleportedToPortal( player );
  2236. }
  2237. }
  2238. //notify clients of the teleportation
  2239. EntityPortalled( pPortal, player, mv->GetAbsOrigin(), mv->m_vecAngles, bForceDuck );
  2240. #endif
  2241. #if defined( DEBUG_FLINGS )
  2242. //movement debugging
  2243. {
  2244. Vector vHullMins = pPortalPlayer->GetHullMins();
  2245. Vector vHullMaxs = pPortalPlayer->GetHullMaxs();
  2246. Vector vHullExtents = (vHullMaxs - vHullMins) * 0.5f;
  2247. Vector vHullCenter = mv->GetAbsOrigin() + ((vHullMaxs + vHullMins) * 0.5f);
  2248. s_MovementDebug.pPlayer = pPortalPlayer;
  2249. s_MovementDebug.pPortal = pPortal;
  2250. s_MovementDebug.vTeleportPos = vHullCenter;
  2251. s_MovementDebug.vTeleportExtents = vHullExtents;
  2252. s_MovementDebug.vTeleportVel = mv->m_vecVelocity;
  2253. s_MovementDebug.bWatchFlingVelocity = true;
  2254. }
  2255. #endif
  2256. pPlayerTouchingPortal = pPortal->m_hLinkedPortal.Get();
  2257. #if defined( GAME_DLL )
  2258. pPortalPlayer->ApplyPortalTeleportation( pPortal, mv );
  2259. pPortal->PostTeleportTouchingEntity( player );
  2260. #else
  2261. pPortalPlayer->ApplyPredictedPortalTeleportation( pPortal, mv, bForceDuck );
  2262. #endif
  2263. //Fix us up if we got stuck
  2264. //if( sv_portal_new_player_trace.GetBool() )
  2265. {
  2266. CPortal_Base2D *pPortalEnvBackup = pPortalPlayer->m_hPortalEnvironment.Get();
  2267. pPortalPlayer->m_hPortalEnvironment = pPlayerTouchingPortal; //we need to trace against the new environment now instead of waiting for it to update naturally.
  2268. trace_t portalTrace;
  2269. TracePlayerBBox( mv->GetAbsOrigin(), mv->GetAbsOrigin(), MASK_PLAYERSOLID, COLLISION_GROUP_PLAYER_MOVEMENT, portalTrace );
  2270. if( portalTrace.startsolid )
  2271. {
  2272. Vector vPlayerCenter = mv->GetAbsOrigin() + vOriginToCenter;
  2273. Vector vPortalToCenter = vPlayerCenter - pPlayerTouchingPortal->m_ptOrigin;
  2274. Vector vPortalNormal = pPlayerTouchingPortal->m_PortalSimulator.GetInternalData().Placement.PortalPlane.m_Normal;
  2275. Vector vOffCenter = vPortalToCenter - (vPortalToCenter.Dot( vPortalNormal ) * vPortalNormal);
  2276. //AABB's going through portals are likely to cause weird collision bugs. Just try to get them close
  2277. TracePlayerBBox( mv->GetAbsOrigin() - vOffCenter, mv->GetAbsOrigin(), MASK_PLAYERSOLID, COLLISION_GROUP_PLAYER_MOVEMENT, portalTrace );
  2278. if( !portalTrace.startsolid )
  2279. {
  2280. mv->SetAbsOrigin( portalTrace.endpos );
  2281. }
  2282. else
  2283. {
  2284. Vector vNewCenter = vec3_origin;
  2285. Vector vExtents = (pPortalPlayer->GetHullMaxs() - pPortalPlayer->GetHullMins()) * 0.5f;
  2286. CTraceFilterSimple traceFilter( pPortalPlayer, COLLISION_GROUP_PLAYER_MOVEMENT );
  2287. if( UTIL_FindClosestPassableSpace_InPortal_CenterMustStayInFront( pPlayerTouchingPortal, vPlayerCenter, vExtents, pPlayerTouchingPortal->m_plane_Origin.normal, &traceFilter, MASK_PLAYERSOLID, 100, vNewCenter ) &&
  2288. ((pPlayerTouchingPortal->m_plane_Origin.normal.Dot( vNewCenter ) - pPlayerTouchingPortal->m_plane_Origin.dist) >= 0.0f) )
  2289. {
  2290. TracePlayerBBox( vNewCenter - vOriginToCenter, mv->GetAbsOrigin(), MASK_PLAYERSOLID, COLLISION_GROUP_PLAYER_MOVEMENT, portalTrace );
  2291. if( !portalTrace.startsolid )
  2292. {
  2293. mv->SetAbsOrigin( portalTrace.endpos );
  2294. }
  2295. else
  2296. {
  2297. mv->SetAbsOrigin( vNewCenter - vOriginToCenter );
  2298. }
  2299. }
  2300. }
  2301. }
  2302. pPortalPlayer->m_hPortalEnvironment = pPortalEnvBackup;
  2303. // Transform our old eye position through the portals so we can set an offset of where our eye
  2304. // would have been and where it actually is
  2305. Vector vTransformedEyePos;
  2306. VectorTransform( vOldEyePos, pPortal->m_matrixThisToLinked.As3x4(), vTransformedEyePos );
  2307. Vector vNewEyePos = mv->GetAbsOrigin() + pPortalPlayer->GetViewOffset();
  2308. pPortalPlayer->SetEyeOffset( vTransformedEyePos, vNewEyePos );
  2309. }
  2310. }
  2311. #if ( PLAYERPORTALDEBUGSPEW == 1 ) && 0 //debugging spew
  2312. else
  2313. {
  2314. static float fMaxTime = 0.0f; //prediction can produce lots and lots and lots of spew if we allow it to repeat itself
  2315. if( fMaxTime < gpGlobals->curtime )
  2316. {
  2317. Warning( "CPortalGameMovement::HandlePortalling(%s) %f player touching portal %i without teleporting dist %f vel %f\n", szDLLName, gpGlobals->curtime, pPortal->m_bIsPortal2 ? 2 : 1,
  2318. (pPortal->m_plane_Origin.normal.Dot( vMoveCenter ) - pPortal->m_plane_Origin.dist), pPortal->m_plane_Origin.normal.Dot( mv->m_vecVelocity ) );
  2319. fMaxTime = gpGlobals->curtime;
  2320. }
  2321. }
  2322. #endif
  2323. if( bMobile )
  2324. {
  2325. pPlayerTouchingPortal = NULL;
  2326. }
  2327. }
  2328. pPortalEnvironment = pPortalPlayer->m_hPortalEnvironment.Get();
  2329. if( pPlayerTouchingPortal != pPortalEnvironment )
  2330. {
  2331. if( pPortalEnvironment )
  2332. {
  2333. pPortalEnvironment->m_PortalSimulator.ReleaseOwnershipOfEntity( player );
  2334. }
  2335. pPortalPlayer->m_hPortalEnvironment = pPlayerTouchingPortal;
  2336. if( pPlayerTouchingPortal )
  2337. {
  2338. pPlayerTouchingPortal->m_PortalSimulator.TakeOwnershipOfEntity( player );
  2339. }
  2340. else
  2341. {
  2342. //we're switching out the portal collision for real collision. Unlike entering from the real world to the portal, we can't wait for it to opportunistically
  2343. //find a non-stuck case to transition. Fix it now if the player is stuck (so far it's always been a floating point precision problem)
  2344. trace_t realTrace;
  2345. TracePlayerBBox( mv->GetAbsOrigin(), mv->GetAbsOrigin(), MASK_PLAYERSOLID, COLLISION_GROUP_PLAYER_MOVEMENT, realTrace );
  2346. if( realTrace.startsolid )
  2347. {
  2348. //crap
  2349. trace_t portalTrace;
  2350. pPortalPlayer->m_hPortalEnvironment = pPortalEnvironment; //try it again with the old environment to be sure
  2351. TracePlayerBBox( mv->GetAbsOrigin(), mv->GetAbsOrigin(), MASK_PLAYERSOLID, COLLISION_GROUP_PLAYER_MOVEMENT, portalTrace );
  2352. pPortalPlayer->m_hPortalEnvironment = NULL;
  2353. if( !portalTrace.startsolid ) //if we startsolid in the portal environment, leave the position alone for now on the assumption that it might be exploitable otherwise
  2354. {
  2355. //fix it
  2356. CTraceFilterSimple traceFilter( player, COLLISION_GROUP_PLAYER_MOVEMENT );
  2357. Vector vResult = vec3_origin;
  2358. Vector vExtents = (pPortalPlayer->GetHullMaxs() - pPortalPlayer->GetHullMins()) * 0.5f;
  2359. UTIL_FindClosestPassableSpace( mv->GetAbsOrigin() + vOriginToCenter, vExtents, Vector( 0.0f, 0.0f, 1.0f ), &traceFilter, MASK_PLAYERSOLID, 100, vResult );
  2360. mv->SetAbsOrigin( vResult - vOriginToCenter );
  2361. }
  2362. }
  2363. }
  2364. }
  2365. #if defined( TRACE_DEBUG_ENABLED )
  2366. CheckStuck();
  2367. #endif
  2368. #if defined( GAME_DLL ) && 0
  2369. if( pPortalEnvironment )
  2370. {
  2371. bool bShowBox = true;
  2372. #if 1 //only draw the box if we're tracing the other side
  2373. Ray_t ray;
  2374. ray.Init( mv->GetAbsOrigin(), mv->GetAbsOrigin(), GetPlayerMins(), GetPlayerMaxs() );
  2375. bShowBox = pPortalEnvironment->m_PortalSimulator.IsRayInPortalHole( ray ) == RIPHR_TOUCHING_HOLE_NOT_WALL;
  2376. #endif
  2377. if( bShowBox )
  2378. {
  2379. Vector vExtent_WH( 0.0f, pPortalEnvironment->GetHalfWidth(), pPortalEnvironment->GetHalfHeight() );
  2380. NDebugOverlay::BoxAngles( pPortalEnvironment->GetAbsOrigin(), -vExtent_WH, vExtent_WH + Vector( 2.0f, 0.0f, 0.0f ), pPortalEnvironment->GetAbsAngles(), 255, 0, 0, 100, 0.0f );
  2381. }
  2382. }
  2383. #endif
  2384. }
  2385. //-----------------------------------------------------------------------------
  2386. // Purpose:
  2387. // Input : ducked -
  2388. // Output : const Vector
  2389. //-----------------------------------------------------------------------------
  2390. const Vector& CPortalGameMovement::GetPlayerMins( bool ducked ) const
  2391. {
  2392. return ducked ? GetPortalPlayer()->GetDuckHullMins() : GetPortalPlayer()->GetStandHullMins();
  2393. }
  2394. //-----------------------------------------------------------------------------
  2395. // Purpose:
  2396. // Input : ducked -
  2397. // Output : const Vector
  2398. //-----------------------------------------------------------------------------
  2399. const Vector& CPortalGameMovement::GetPlayerMaxs( bool ducked ) const
  2400. {
  2401. return ducked ? GetPortalPlayer()->GetDuckHullMaxs() : GetPortalPlayer()->GetStandHullMaxs();
  2402. }
  2403. //-----------------------------------------------------------------------------
  2404. // Purpose:
  2405. // Input :
  2406. // Output : const Vector
  2407. //-----------------------------------------------------------------------------
  2408. const Vector& CPortalGameMovement::GetPlayerMins() const
  2409. {
  2410. return GetPlayerMins( GetPortalPlayer()->m_Local.m_bDucked );
  2411. }
  2412. //-----------------------------------------------------------------------------
  2413. // Purpose:
  2414. // Input :
  2415. // Output : const Vector
  2416. //-----------------------------------------------------------------------------
  2417. const Vector& CPortalGameMovement::GetPlayerMaxs() const
  2418. {
  2419. return GetPlayerMaxs( GetPortalPlayer()->m_Local.m_bDucked );
  2420. }
  2421. //-----------------------------------------------------------------------------
  2422. // Purpose:
  2423. // Input : ducked -
  2424. // Output : const Vector
  2425. //-----------------------------------------------------------------------------
  2426. const Vector& CPortalGameMovement::GetPlayerViewOffset( bool ducked ) const
  2427. {
  2428. return ducked ? VEC_DUCK_VIEW : VEC_VIEW;
  2429. }
  2430. //-----------------------------------------------------------------------------
  2431. // Purpose:
  2432. //-----------------------------------------------------------------------------
  2433. void CPortalGameMovement::CategorizeGroundSurface( trace_t &pm )
  2434. {
  2435. IPhysicsSurfaceProps *physprops = MoveHelper()->GetSurfaceProps();
  2436. player->m_surfaceProps = pm.surface.surfaceProps;
  2437. player->m_pSurfaceData = physprops->GetSurfaceData( player->m_surfaceProps );
  2438. physprops->GetPhysicsProperties( player->m_surfaceProps, NULL, NULL, &player->m_surfaceFriction, NULL );
  2439. // HACKHACK: Scale this to fudge the relationship between vphysics friction values and player friction values.
  2440. // A value of 0.8f feels pretty normal for vphysics, whereas 1.0f is normal for players.
  2441. // This scaling trivially makes them equivalent. REVISIT if this affects low friction surfaces too much.
  2442. player->m_surfaceFriction *= 1.25f;
  2443. if ( player->m_surfaceFriction > 1.0f )
  2444. player->m_surfaceFriction = 1.0f;
  2445. player->m_chTextureType = player->m_pSurfaceData->game.material;
  2446. }
  2447. //-----------------------------------------------------------------------------
  2448. // Purpose:
  2449. //-----------------------------------------------------------------------------
  2450. void CPortalGameMovement::CheckParameters()
  2451. {
  2452. QAngle v_angle;
  2453. if ( player->GetMoveType() != MOVETYPE_ISOMETRIC &&
  2454. player->GetMoveType() != MOVETYPE_NOCLIP &&
  2455. player->GetMoveType() != MOVETYPE_OBSERVER )
  2456. {
  2457. float maxspeed;
  2458. bool bIsOnSpeedPaint = mv->m_flMaxSpeed > sv_speed_normal.GetFloat();
  2459. maxspeed = mv->m_flClientMaxSpeed;
  2460. if ( maxspeed != 0.0 )
  2461. {
  2462. mv->m_flMaxSpeed = MIN( maxspeed, mv->m_flMaxSpeed );
  2463. }
  2464. // Slow down by the speed factor
  2465. float flSpeedFactor = 1.0f;
  2466. if (player->m_pSurfaceData)
  2467. {
  2468. flSpeedFactor = player->m_pSurfaceData->game.maxSpeedFactor;
  2469. }
  2470. // If we have a constraint, slow down because of that too.
  2471. float flConstraintSpeedFactor = ComputeConstraintSpeedFactor();
  2472. if (flConstraintSpeedFactor < flSpeedFactor)
  2473. flSpeedFactor = flConstraintSpeedFactor;
  2474. mv->m_flMaxSpeed *= flSpeedFactor;
  2475. float flSideMoveFactor = 1.0f;
  2476. float flForwardMoveFactor = 1.0f;
  2477. //If the player is on speed paint and pressing both a forward/backward and left/right movement keys
  2478. if( bIsOnSpeedPaint && player->GetGroundEntity() &&
  2479. mv->m_flForwardMove != 0.f && mv->m_flSideMove != 0.f )
  2480. {
  2481. const float flForwardSpeed = fabs( DotProduct( player->Forward(), mv->m_vecVelocity ) );
  2482. const float flSideSpeed = fabs( DotProduct( player->Left(), mv->m_vecVelocity ) );
  2483. // Figure out which direction we're more moving in: Side to side or forward/backward. then dampen our
  2484. // input in the direction we're not mostly traveling
  2485. if( flForwardSpeed > flSideSpeed )
  2486. {
  2487. flSideMoveFactor = sv_speed_paint_side_move_factor.GetFloat();
  2488. }
  2489. else
  2490. {
  2491. flForwardMoveFactor = sv_speed_paint_side_move_factor.GetFloat();
  2492. }
  2493. }
  2494. // Go faster on speed paint
  2495. if( bIsOnSpeedPaint )
  2496. {
  2497. float flSpeedPaintMultiplier = mv->m_flMaxSpeed / sv_speed_normal.GetFloat();
  2498. mv->m_flForwardMove *= flSpeedPaintMultiplier;
  2499. mv->m_flSideMove *= flSpeedPaintMultiplier;
  2500. mv->m_flUpMove *= flSpeedPaintMultiplier; // do we need this?
  2501. }
  2502. float spdsqr = ( mv->m_flForwardMove * mv->m_flForwardMove ) +
  2503. ( mv->m_flSideMove * mv->m_flSideMove ) +
  2504. ( mv->m_flUpMove * mv->m_flUpMove );
  2505. // Cap off the movement speed, if necessary
  2506. if ( spdsqr != 0.0 )
  2507. {
  2508. float flMax = bIsOnSpeedPaint ? mv->m_flMaxSpeed : sv_speed_normal.GetFloat();
  2509. if ( spdsqr > flMax*flMax )
  2510. {
  2511. float fRatio = mv->m_flMaxSpeed / sqrt( spdsqr );
  2512. mv->m_flForwardMove *= fRatio * flForwardMoveFactor;
  2513. mv->m_flSideMove *= fRatio * flSideMoveFactor;
  2514. mv->m_flUpMove *= fRatio;
  2515. }
  2516. }
  2517. }
  2518. if ( player->GetFlags() & FL_FROZEN ||
  2519. player->GetFlags() & FL_ONTRAIN ||
  2520. IsDead() )
  2521. {
  2522. mv->m_flForwardMove = 0;
  2523. mv->m_flSideMove = 0;
  2524. mv->m_flUpMove = 0;
  2525. }
  2526. DecayPunchAngle();
  2527. // Take angles from command.
  2528. if ( !IsDead() )
  2529. {
  2530. v_angle = mv->m_vecAngles;
  2531. v_angle = v_angle + player->m_Local.m_vecPunchAngle;
  2532. // Now adjust roll angle
  2533. if ( player->GetMoveType() != MOVETYPE_ISOMETRIC &&
  2534. player->GetMoveType() != MOVETYPE_NOCLIP &&
  2535. sv_rollangle.GetFloat() != 0 )
  2536. {
  2537. mv->m_vecAngles[ROLL] = CalcRoll( v_angle, mv->m_vecVelocity, sv_rollangle.GetFloat(), sv_rollspeed.GetFloat() );
  2538. }
  2539. else
  2540. {
  2541. mv->m_vecAngles[ROLL] = 0.0; // v_angle[ ROLL ];
  2542. }
  2543. mv->m_vecAngles[PITCH] = v_angle[PITCH];
  2544. mv->m_vecAngles[YAW] = v_angle[YAW];
  2545. }
  2546. else
  2547. {
  2548. mv->m_vecAngles = mv->m_vecOldAngles;
  2549. }
  2550. // Set dead player view_offset
  2551. if ( IsDead() )
  2552. {
  2553. player->SetViewOffset( VEC_DEAD_VIEWHEIGHT );
  2554. }
  2555. // Adjust client view angles to match values used on server.
  2556. if ( mv->m_vecAngles[YAW] > 180.0f )
  2557. {
  2558. mv->m_vecAngles[YAW] -= 360.0f;
  2559. }
  2560. }
  2561. // get a conservative bounds for this player's movement traces
  2562. // This allows gamemovement to optimize those traces
  2563. void CPortalGameMovement::SetupMovementBounds( CMoveData *move )
  2564. {
  2565. if ( m_pTraceListData )
  2566. {
  2567. m_pTraceListData->Reset();
  2568. }
  2569. else
  2570. {
  2571. m_pTraceListData = enginetrace->AllocTraceListData();
  2572. }
  2573. if ( !move->m_nPlayerHandle.IsValid() )
  2574. {
  2575. return;
  2576. }
  2577. CBasePlayer *pPlayer = (CBasePlayer *)move->m_nPlayerHandle.Get();
  2578. CPortal_Player *pPortalPlayer = assert_cast< CPortal_Player* >( pPlayer );
  2579. Vector moveMins, moveMaxs;
  2580. ClearBounds( moveMins, moveMaxs );
  2581. Vector start = move->GetAbsOrigin();
  2582. float radius = ((move->m_vecVelocity.Length() + move->m_flMaxSpeed) * gpGlobals->frametime) + 1.0f;
  2583. // NOTE: assumes the unducked bbox encloses the ducked bbox
  2584. Vector boxMins = pPortalPlayer->GetStandHullMins(); //GetPlayerMins(false);
  2585. Vector boxMaxs = pPortalPlayer->GetStandHullMaxs(); //GetPlayerMaxs(false);
  2586. // bloat by traveling the max velocity in all directions, plus the stepsize up/down
  2587. Vector bloat;
  2588. bloat.Init(radius, radius, radius);
  2589. bloat += pPlayer->m_Local.m_flStepSize * pPortalPlayer->GetPortalPlayerLocalData().m_StickNormal;
  2590. AddPointToBounds( start + boxMaxs + bloat, moveMins, moveMaxs );
  2591. AddPointToBounds( start + boxMins - bloat, moveMins, moveMaxs );
  2592. // now build an optimized trace within these bounds
  2593. enginetrace->SetupLeafAndEntityListBox( moveMins, moveMaxs, m_pTraceListData );
  2594. }
  2595. //-----------------------------------------------------------------------------
  2596. // Purpose:
  2597. //-----------------------------------------------------------------------------
  2598. void CPortalGameMovement::StartGravity()
  2599. {
  2600. float ent_gravity;
  2601. if (player->GetGravity())
  2602. ent_gravity = player->GetGravity();
  2603. else
  2604. ent_gravity = 1.0;
  2605. #if defined( DEBUG_MOTION_CONTROLLERS )
  2606. DiffPrint( "CPGM:SG %.2f %.2f %.2f %.2f %.2f %.2f %.2f %.2f %.2f", ent_gravity, sv_gravity.GetFloat(), gpGlobals->frametime, XYZ( m_vGravityDirection ), XYZ( player->GetBaseVelocity() ) );
  2607. #endif
  2608. // Add gravity so they'll be in the correct position during movement
  2609. // yes, this 0.5 looks wrong, but it's not.
  2610. mv->m_vecVelocity += m_vGravityDirection * ( ent_gravity * sv_gravity.GetFloat() * 0.5 * gpGlobals->frametime );
  2611. mv->m_vecVelocity += m_vGravityDirection * ( DotProduct( player->GetBaseVelocity(), m_vGravityDirection ) * gpGlobals->frametime );
  2612. Vector temp = player->GetBaseVelocity();
  2613. temp -= m_vGravityDirection * DotProduct( player->GetBaseVelocity(), m_vGravityDirection );
  2614. player->SetBaseVelocity( temp );
  2615. CheckVelocity();
  2616. }
  2617. //-----------------------------------------------------------------------------
  2618. // Purpose:
  2619. //-----------------------------------------------------------------------------
  2620. void CPortalGameMovement::AddGravity()
  2621. {
  2622. if ( player->m_flWaterJumpTime == 0 )
  2623. {
  2624. const float ent_gravity = (player->GetGravity() != 0) ? player->GetGravity() : 1.0f;
  2625. // Add gravity incorrectly
  2626. mv->m_vecVelocity += (ent_gravity * sv_gravity.GetFloat() * gpGlobals->frametime) * m_vGravityDirection;
  2627. mv->m_vecVelocity -= DotProduct( player->GetBaseVelocity(), m_vGravityDirection ) * gpGlobals->frametime * m_vGravityDirection;
  2628. Vector temp = player->GetBaseVelocity();
  2629. temp -= DotProduct( temp, m_vGravityDirection ) * m_vGravityDirection;
  2630. player->SetBaseVelocity( temp );
  2631. CheckVelocity();
  2632. }
  2633. }
  2634. //-----------------------------------------------------------------------------
  2635. // Purpose:
  2636. //-----------------------------------------------------------------------------
  2637. void CPortalGameMovement::FinishGravity()
  2638. {
  2639. if ( player->m_flWaterJumpTime == 0 )
  2640. {
  2641. const float ent_gravity = (player->GetGravity() != 0) ? player->GetGravity() : 1.0f;
  2642. // Get the correct velocity for the end of the dt
  2643. mv->m_vecVelocity += (ent_gravity * sv_gravity.GetFloat() * gpGlobals->frametime * 0.5) * m_vGravityDirection;
  2644. CheckVelocity();
  2645. }
  2646. }
  2647. //-----------------------------------------------------------------------------
  2648. // Purpose: Does the basic move attempting to climb up step heights. It uses
  2649. // the mv->GetAbsOrigin() and mv->m_vecVelocity. It returns a new
  2650. // new mv->GetAbsOrigin(), mv->m_vecVelocity, and mv->m_outStepHeight.
  2651. //-----------------------------------------------------------------------------
  2652. void CPortalGameMovement::StepMove( Vector &vecDestination, trace_t &traceIn )
  2653. {
  2654. CTrace_PlayerAABB_vs_Portals trace;
  2655. *(trace_t *)&trace = traceIn;
  2656. trace.m_bContactedPortalTransitionRamp = false;
  2657. // Save the move position and velocity in case we need to put it back later.
  2658. Vector vecPos = mv->GetAbsOrigin(),
  2659. vecVel = mv->m_vecVelocity;
  2660. // First try walking straight to where they want to go.
  2661. Vector vecEndPos = vecDestination;
  2662. TryPlayerMove( &vecEndPos, &trace );
  2663. // mv now contains where they ended up if they tried to walk straight there.
  2664. // Save those results for use later.
  2665. Vector vecDownPos = mv->GetAbsOrigin(),
  2666. vecDownVel = mv->m_vecVelocity;
  2667. // Reset original values to try some other things.
  2668. mv->SetAbsOrigin( vecPos );
  2669. mv->m_vecVelocity = vecVel;
  2670. // Move up a stair height.
  2671. // Slide forward at the same velocity but from the higher position.
  2672. vecEndPos = mv->GetAbsOrigin();
  2673. if ( player->m_Local.m_bAllowAutoMovement )
  2674. {
  2675. vecEndPos -= (player->m_Local.m_flStepSize + DIST_EPSILON) * m_vGravityDirection;
  2676. }
  2677. // Only step up as high as we have headroom to do so.
  2678. TracePlayerBBox( mv->GetAbsOrigin(), vecEndPos, PlayerSolidMask(), COLLISION_GROUP_PLAYER_MOVEMENT, trace );
  2679. if ( !trace.startsolid && !trace.allsolid )
  2680. {
  2681. mv->SetAbsOrigin( trace.endpos );
  2682. }
  2683. TryPlayerMove( 0, 0 );
  2684. // Move down a stair (attempt to).
  2685. // Slide forward at the same velocity from the lower position.
  2686. vecEndPos = mv->GetAbsOrigin();
  2687. if ( player->m_Local.m_bAllowAutoMovement )
  2688. {
  2689. vecEndPos += (player->m_Local.m_flStepSize + DIST_EPSILON) * m_vGravityDirection;
  2690. }
  2691. #if defined GAME_DLL
  2692. RANDOM_CEG_TEST_SECRET();
  2693. #endif
  2694. TracePlayerBBox( mv->GetAbsOrigin(), vecEndPos, PlayerSolidMask(), COLLISION_GROUP_PLAYER_MOVEMENT, trace );
  2695. // If we are not on the ground any more then use the original movement attempt.
  2696. // Note: Negation of dot product here because gravity direction is the negative normal direction
  2697. bool bOnGround = true;
  2698. if ( (-DotProduct( trace.plane.normal, m_vGravityDirection ) < CRITICAL_SLOPE) && !trace.HitPortalRamp(-m_vGravityDirection) )
  2699. {
  2700. mv->SetAbsOrigin( vecDownPos );
  2701. VectorCopy( vecDownVel, mv->m_vecVelocity );
  2702. bOnGround = false;
  2703. }
  2704. // If still on the ground
  2705. if( bOnGround )
  2706. {
  2707. // If the trace ended up in empty space, copy the end over to the origin.
  2708. if ( !trace.startsolid && !trace.allsolid )
  2709. {
  2710. mv->SetAbsOrigin( trace.endpos );
  2711. }
  2712. // Copy this origin to up.
  2713. Vector vecUpPos = mv->GetAbsOrigin();
  2714. // decide which one went farther
  2715. float flDownDistSqr = ( vecDownPos - vecPos ).LengthSqr();
  2716. float flUpDistSqr = ( vecUpPos - vecPos ).LengthSqr();
  2717. if ( flDownDistSqr > flUpDistSqr )
  2718. {
  2719. mv->SetAbsOrigin( vecDownPos );
  2720. mv->m_vecVelocity = vecDownVel;
  2721. }
  2722. else
  2723. {
  2724. // copy z value from slide move
  2725. mv->m_vecVelocity -= DotProduct( mv->m_vecVelocity, m_vGravityDirection );
  2726. mv->m_vecVelocity += DotProduct( vecDownVel, m_vGravityDirection ) * m_vGravityDirection;
  2727. }
  2728. }
  2729. // Compute step distance
  2730. float flStepDist = DotProduct( m_vGravityDirection, vecPos - mv->GetAbsOrigin() );
  2731. if ( flStepDist > 0 )
  2732. {
  2733. mv->m_outStepHeight += flStepDist;
  2734. }
  2735. traceIn = *(trace_t *)&trace;
  2736. }
  2737. void CPortalGameMovement::CheckWallImpact( Vector& primal_velocity )
  2738. {
  2739. // Check if they slammed into a wall
  2740. float fSlamVol = 0.0f;
  2741. Vector vPrimalVelInPlane = primal_velocity - m_vGravityDirection * DotProduct( primal_velocity, m_vGravityDirection );
  2742. Vector vVelInPlane = mv->m_vecVelocity - m_vGravityDirection * DotProduct( mv->m_vecVelocity, m_vGravityDirection );
  2743. float fLateralStoppingAmount = vPrimalVelInPlane.Length() - vVelInPlane.Length();
  2744. if ( fLateralStoppingAmount > PLAYER_MAX_SAFE_FALL_SPEED )
  2745. {
  2746. #if defined CLIENT_DLL
  2747. STEAMWORKS_TESTSECRET();
  2748. #endif
  2749. fSlamVol = 1.0f;
  2750. }
  2751. else if ( fLateralStoppingAmount > 0.5f * PLAYER_MAX_SAFE_FALL_SPEED )
  2752. {
  2753. fSlamVol = 0.85f;
  2754. }
  2755. if ( fSlamVol > 0.0f )
  2756. {
  2757. PlayerWallImpactEffects( fSlamVol, DotProduct( (vPrimalVelInPlane - vVelInPlane).Normalized(), vPrimalVelInPlane) );
  2758. }
  2759. // Check if the player slammed into a ceiling
  2760. fSlamVol = 0.0f;
  2761. float flLostVerticalSpeed = primal_velocity.z - mv->m_vecVelocity.z;
  2762. if( flLostVerticalSpeed > PLAYER_MAX_SAFE_FALL_SPEED )
  2763. {
  2764. fSlamVol = 1.0f;
  2765. }
  2766. else if( flLostVerticalSpeed > 0.5f * PLAYER_MAX_SAFE_FALL_SPEED )
  2767. {
  2768. fSlamVol = 0.85f;
  2769. }
  2770. if( fSlamVol > 0.0f )
  2771. {
  2772. PlayerCeilingImpactEffects( fSlamVol );
  2773. }
  2774. // Note: The check for a rough landing on the floor is in CheckFalling().
  2775. // impact animation
  2776. if ( g_pGameRules->IsMultiplayer() )
  2777. {
  2778. const float flImpactThreshold = coop_impact_velocity_threshold.GetFloat();
  2779. float flLostHorizontalSpeed = primal_velocity.Length2D() - mv->m_vecVelocity.Length2D();
  2780. Activity impactActivity = ACT_INVALID;
  2781. if ( flLostVerticalSpeed > flImpactThreshold )
  2782. {
  2783. impactActivity = ACT_MP_JUMP_IMPACT_TOP;
  2784. }
  2785. else if ( flLostHorizontalSpeed > flImpactThreshold )
  2786. {
  2787. const float flDot45Degree = 0.707106781187;
  2788. Vector vImpactDir = primal_velocity - mv->m_vecVelocity;
  2789. vImpactDir.NormalizeInPlace();
  2790. float flImpactDot = DotProduct( vImpactDir, player->Forward() );
  2791. float flOrthoDot = DotProduct( vImpactDir, -player->Left() );
  2792. // Do nothing at the moment
  2793. if ( flImpactDot < -flDot45Degree )
  2794. {
  2795. // Back impact
  2796. impactActivity = ACT_MP_JUMP_IMPACT_S;
  2797. }
  2798. else if ( flImpactDot > flDot45Degree )
  2799. {
  2800. // Head-on impact
  2801. impactActivity = ACT_MP_JUMP_IMPACT_N;
  2802. }
  2803. else if ( flOrthoDot > 0.0f )
  2804. {
  2805. // Right impact
  2806. impactActivity = ACT_MP_JUMP_IMPACT_E;
  2807. }
  2808. else
  2809. {
  2810. // Left impact
  2811. impactActivity = ACT_MP_JUMP_IMPACT_W;
  2812. }
  2813. }
  2814. if ( impactActivity != ACT_INVALID )
  2815. {
  2816. CPortal_Player *pPortalPlayer = ToPortalPlayer( player );
  2817. pPortalPlayer->DoAnimationEvent( PLAYERANIMEVENT_CUSTOM_GESTURE, impactActivity );
  2818. CRecipientFilter filter;
  2819. filter.UsePredictionRules();
  2820. filter.AddRecipientsByPAS( pPortalPlayer->GetAbsOrigin() );
  2821. pPortalPlayer->EmitSound( filter, pPortalPlayer->entindex(), "CoopBot.WallSlam" );
  2822. }
  2823. }
  2824. }
  2825. // Edge friction convars
  2826. ConVar sv_edgefriction( "sv_edgefriction", "2", FCVAR_CHEAT | FCVAR_REPLICATED );
  2827. ConVar sv_use_edgefriction( "sv_use_edgefriction", "1", FCVAR_CHEAT | FCVAR_REPLICATED );
  2828. //-----------------------------------------------------------------------------
  2829. // Purpose:
  2830. //-----------------------------------------------------------------------------
  2831. void CPortalGameMovement::Friction()
  2832. {
  2833. float speed, newspeed, control;
  2834. float friction;
  2835. float drop;
  2836. // If we are in water jump cycle, don't apply friction
  2837. if (player->m_flWaterJumpTime)
  2838. return;
  2839. // Calculate speed
  2840. speed = mv->m_vecVelocity.Length();
  2841. // If too slow, return
  2842. if (speed < 0.1f)
  2843. {
  2844. return;
  2845. }
  2846. drop = 0;
  2847. // apply ground friction
  2848. if ( player->GetGroundEntity() != NULL ) // On an entity that is the ground
  2849. {
  2850. // Set our base friction for the surface
  2851. friction = sv_friction.GetFloat() * player->m_surfaceFriction;
  2852. if ( sv_use_edgefriction.GetBool() )
  2853. {
  2854. Vector start, stop;
  2855. trace_t pm;
  2856. // NOTE: added a "1.0f" to the player minimum (bbox) value so that the
  2857. // trace starts just inside of the bounding box, this make sure
  2858. // that we don't get any collision epsilon (on surface) errors.
  2859. // The significance of the 16 below is this is how many units out front we are checking
  2860. // to see if the player box would fall. The 49 is the number of units down that is required
  2861. // to be considered a fall. 49 is derived from 1 (added 1 from above) + 48 the max fall
  2862. // distance a player can fall and still jump back up.
  2863. //
  2864. // UNDONE: In some cases there are still problems here. Specifically, no collision check is
  2865. // done so 16 units in front of the player could be inside a volume or past a collision point.
  2866. Vector vOrigin = player->GetAbsOrigin();
  2867. //start[0] = stop[0] = vOrigin[0] + (mv->m_vecVelocity[0]/speed)*16;
  2868. //start[1] = stop[1] = vOrigin[1] + (mv->m_vecVelocity[1]/speed)*16;
  2869. //start[2] = vOrigin[2] + ( GetPlayerMins()[2] + 1.0f );
  2870. //stop[2] = start[2] - 49;
  2871. Vector directionInPlane = mv->m_vecVelocity;
  2872. directionInPlane -= DotProduct( directionInPlane, m_vGravityDirection ) * directionInPlane;
  2873. directionInPlane.NormalizeInPlace();
  2874. start = vOrigin + 16.0f * directionInPlane; // Offset 16 units in the travel direction
  2875. start -= m_vGravityDirection; // Bump up 1 unit
  2876. stop = start + 49.0f * m_vGravityDirection; // Trace down 49 units
  2877. // This should *not* be a raytrace, it should be a box trace such that we can determine if the
  2878. // player's box would fall off the ledge. Ray tracing has problems associated with walking on rails
  2879. // or on planks where a single ray would have the code believe the player is going to fall when in fact
  2880. // they wouldn't. The by product of this not working properly is that when a player gets to what
  2881. // the code believes is an edge, the friction is bumped way up thus slowing the player down.
  2882. // If not done properly, this kicks in way too often and forces big unintentional slowdowns.
  2883. UTIL_TraceHull( start, stop, GetPlayerMins(), GetPlayerMaxs(), MASK_PLAYERSOLID, player, COLLISION_GROUP_PLAYER_MOVEMENT, &pm );
  2884. /*
  2885. NDebugOverlay::Cross( start, 8.0f, 255, 0, 0, true, 2.0f );
  2886. NDebugOverlay::Cross( stop, 8.0f, 0, 255, 0, true, 2.0f );
  2887. */
  2888. // If we didn't contact the ground, we need to apply some friction!
  2889. if ( pm.fraction == 1.0 )
  2890. {
  2891. friction *= sv_edgefriction.GetFloat();
  2892. //NDebugOverlay::SweptBox( start, stop, GetPlayerMins(), GetPlayerMaxs(), QAngle(0,0,0), 0, 255, 0, 32, 5 );
  2893. }
  2894. //else
  2895. //{
  2896. // NDebugOverlay::SweptBox( start, stop, GetPlayerMins(), GetPlayerMaxs(), QAngle(0,0,0), 255, 0, 0, 32, 5 );
  2897. //}
  2898. }
  2899. // Bleed off some speed, but if we have less than the bleed
  2900. // threshold, bleed the threshold amount.
  2901. if ( IsCrossPlayPlatformAConsole( player->GetCrossPlayPlatform() ) )
  2902. {
  2903. if( player->m_Local.m_bDucked )
  2904. {
  2905. control = (speed < sv_stopspeed.GetFloat()) ? sv_stopspeed.GetFloat() : speed;
  2906. }
  2907. else
  2908. {
  2909. #if defined ( TF_DLL ) || defined ( TF_CLIENT_DLL )
  2910. control = (speed < sv_stopspeed.GetFloat()) ? sv_stopspeed.GetFloat() : speed;
  2911. #else
  2912. control = (speed < sv_stopspeed.GetFloat()) ? (sv_stopspeed.GetFloat() * 2.0f) : speed;
  2913. #endif
  2914. }
  2915. }
  2916. else
  2917. {
  2918. control = (speed < sv_stopspeed.GetFloat()) ? sv_stopspeed.GetFloat() : speed;
  2919. }
  2920. // Add the amount to the drop amount.
  2921. drop += control*friction*gpGlobals->frametime;
  2922. }
  2923. // scale the velocity
  2924. newspeed = speed - drop;
  2925. if (newspeed < 0)
  2926. newspeed = 0;
  2927. if ( newspeed != speed )
  2928. {
  2929. // Determine proportion of old speed we are using.
  2930. newspeed /= speed;
  2931. // Adjust velocity according to proportion.
  2932. VectorScale( mv->m_vecVelocity, newspeed, mv->m_vecVelocity );
  2933. }
  2934. mv->m_outWishVel -= (1.f-newspeed) * mv->m_vecVelocity;
  2935. }
  2936. //-----------------------------------------------------------------------------
  2937. // Purpose: Try to keep a walking player on the ground or whatever surface
  2938. // he's stuck to.
  2939. //-----------------------------------------------------------------------------
  2940. void CPortalGameMovement::StayOnGround()
  2941. {
  2942. CTrace_PlayerAABB_vs_Portals trace;
  2943. const Vector& surfaceNormal = GetPortalPlayer()->GetPortalPlayerLocalData().m_StickNormal;
  2944. Vector start = mv->GetAbsOrigin() + surfaceNormal;
  2945. Vector end = mv->GetAbsOrigin() + player->GetStepSize() * m_vGravityDirection;
  2946. // See how far up we can go without getting stuck
  2947. TracePlayerBBox( mv->GetAbsOrigin(), start, PlayerSolidMask(), COLLISION_GROUP_PLAYER_MOVEMENT, trace );
  2948. start = trace.endpos;
  2949. // using trace.startsolid is unreliable here, it doesn't get set when
  2950. // tracing bounding box vs. terrain
  2951. // Now trace down from a known safe position
  2952. TracePlayerBBox( start, end, PlayerSolidMask(), COLLISION_GROUP_PLAYER_MOVEMENT, trace );
  2953. if ( trace.fraction > 0.0f && // must go somewhere
  2954. trace.fraction < 1.0f && // must hit something
  2955. !trace.startsolid && // can't be embedded in a solid
  2956. ((DotProduct( trace.plane.normal, surfaceNormal ) >= CRITICAL_SLOPE) || trace.HitPortalRamp(surfaceNormal)) ) // can't hit a steep slope that we can't stand on anyway
  2957. {
  2958. const float flDelta = DotProduct( trace.endpos - mv->GetAbsOrigin(), surfaceNormal );
  2959. // Set the implicit vertical speed keeping the player on the surface
  2960. // Ignore this one if it's on an angled surface, the player is moving, and we get a zero.
  2961. // The values cycle back to zero occasionally while moving on sloped surfaces, which doesn't accurately reflect this implicit speed.
  2962. if( gpGlobals->frametime != 0.0f && ( flDelta != 0.0f || AlmostEqual( trace.plane.normal.z, 1.0f ) || ( mv->m_flSideMove == 0.0f && mv->m_flForwardMove == 0.0f ) ) )
  2963. GetPortalPlayer()->SetImplicitVerticalStepSpeed( flDelta / gpGlobals->frametime );
  2964. // This is incredibly hacky. The real problem is that trace returning that strange value we can't network over.
  2965. if ( fabs( flDelta ) > 0.5f * COORD_RESOLUTION )
  2966. {
  2967. mv->SetAbsOrigin( trace.endpos );
  2968. }
  2969. }
  2970. }
  2971. void CPortalGameMovement::WaterMove()
  2972. {
  2973. int i;
  2974. Vector wishvel;
  2975. float wishspeed;
  2976. Vector wishdir;
  2977. Vector start, dest;
  2978. Vector temp;
  2979. trace_t pm;
  2980. float speed, newspeed, addspeed, accelspeed;
  2981. Vector forward, right, up;
  2982. AngleVectors (mv->m_vecViewAngles, &forward, &right, &up); // Determine movement angles
  2983. //
  2984. // user intentions
  2985. //
  2986. for (i=0 ; i<3 ; i++)
  2987. {
  2988. wishvel[i] = forward[i]*mv->m_flForwardMove + right[i]*mv->m_flSideMove;
  2989. }
  2990. if ( GameRules()->IsMultiplayer() )
  2991. {
  2992. float flSinkSpeed;
  2993. switch ( player->GetTeamNumber() )
  2994. {
  2995. case TEAM_RED:
  2996. flSinkSpeed = eggbot_sink_speed.GetFloat();
  2997. break;
  2998. case TEAM_BLUE:
  2999. flSinkSpeed = ballbot_sink_speed.GetFloat();
  3000. break;
  3001. default:
  3002. flSinkSpeed = 60.f;
  3003. }
  3004. wishvel = Vector( 0.2f * wishvel[0], 0.2f * wishvel[1], -flSinkSpeed );
  3005. // slow down the velocity
  3006. mv->m_vecVelocity *= ExponentialDecay( coop_sink_speed_decay.GetFloat(), gpGlobals->frametime );
  3007. }
  3008. else
  3009. {
  3010. // if we have the jump key down, move us up as well
  3011. if (mv->m_nButtons & IN_JUMP)
  3012. {
  3013. wishvel[2] += mv->m_flClientMaxSpeed;
  3014. }
  3015. // Sinking after no other movement occurs
  3016. else if (!mv->m_flForwardMove && !mv->m_flSideMove && !mv->m_flUpMove)
  3017. {
  3018. wishvel[2] -= 60; // drift towards bottom
  3019. }
  3020. else // Go straight up by upmove amount.
  3021. {
  3022. // exaggerate upward movement along forward as well
  3023. float upwardMovememnt = mv->m_flForwardMove * forward.z * 2;
  3024. upwardMovememnt = clamp( upwardMovememnt, 0, mv->m_flClientMaxSpeed );
  3025. wishvel[2] += mv->m_flUpMove + upwardMovememnt;
  3026. }
  3027. }
  3028. // Copy it over and determine speed
  3029. VectorCopy (wishvel, wishdir);
  3030. wishspeed = VectorNormalize(wishdir);
  3031. // Cap speed.
  3032. if (wishspeed > mv->m_flMaxSpeed)
  3033. {
  3034. VectorScale (wishvel, mv->m_flMaxSpeed/wishspeed, wishvel);
  3035. wishspeed = mv->m_flMaxSpeed;
  3036. }
  3037. // Slow us down a bit.
  3038. wishspeed *= 0.8;
  3039. // Water friction
  3040. VectorCopy(mv->m_vecVelocity, temp);
  3041. speed = VectorNormalize(temp);
  3042. if (speed)
  3043. {
  3044. newspeed = speed - gpGlobals->frametime * speed * sv_friction.GetFloat() * player->m_surfaceFriction;
  3045. if (newspeed < 0.1f)
  3046. {
  3047. newspeed = 0;
  3048. }
  3049. VectorScale (mv->m_vecVelocity, newspeed/speed, mv->m_vecVelocity);
  3050. }
  3051. else
  3052. {
  3053. newspeed = 0;
  3054. }
  3055. // water acceleration
  3056. if (wishspeed >= 0.1f) // old !
  3057. {
  3058. addspeed = wishspeed - newspeed;
  3059. if (addspeed > 0)
  3060. {
  3061. VectorNormalize(wishvel);
  3062. accelspeed = sv_accelerate.GetFloat() * wishspeed * gpGlobals->frametime * player->m_surfaceFriction;
  3063. if (accelspeed > addspeed)
  3064. {
  3065. accelspeed = addspeed;
  3066. }
  3067. for (i = 0; i < 3; i++)
  3068. {
  3069. float deltaSpeed = accelspeed * wishvel[i];
  3070. mv->m_vecVelocity[i] += deltaSpeed;
  3071. mv->m_outWishVel[i] += deltaSpeed;
  3072. }
  3073. }
  3074. }
  3075. VectorAdd (mv->m_vecVelocity, player->GetBaseVelocity(), mv->m_vecVelocity);
  3076. // Now move
  3077. // assume it is a stair or a slope, so press down from stepheight above
  3078. VectorMA (mv->GetAbsOrigin(), gpGlobals->frametime, mv->m_vecVelocity, dest);
  3079. TracePlayerBBox( mv->GetAbsOrigin(), dest, PlayerSolidMask(), COLLISION_GROUP_PLAYER_MOVEMENT, pm );
  3080. if ( pm.fraction == 1.0f )
  3081. {
  3082. VectorCopy( dest, start );
  3083. if ( player->m_Local.m_bAllowAutoMovement )
  3084. {
  3085. start[2] += player->m_Local.m_flStepSize + 1;
  3086. }
  3087. TracePlayerBBox( start, dest, PlayerSolidMask(), COLLISION_GROUP_PLAYER_MOVEMENT, pm );
  3088. if (!pm.startsolid && !pm.allsolid)
  3089. {
  3090. float stepDist = pm.endpos.z - mv->GetAbsOrigin().z;
  3091. mv->m_outStepHeight += stepDist;
  3092. // walked up the step, so just keep result and exit
  3093. mv->SetAbsOrigin( pm.endpos );
  3094. VectorSubtract( mv->m_vecVelocity, player->GetBaseVelocity(), mv->m_vecVelocity );
  3095. return;
  3096. }
  3097. // Try moving straight along out normal path.
  3098. TryPlayerMove();
  3099. }
  3100. else
  3101. {
  3102. if ( !player->GetGroundEntity() )
  3103. {
  3104. TryPlayerMove();
  3105. VectorSubtract( mv->m_vecVelocity, player->GetBaseVelocity(), mv->m_vecVelocity );
  3106. return;
  3107. }
  3108. StepMove( dest, pm );
  3109. }
  3110. VectorSubtract( mv->m_vecVelocity, player->GetBaseVelocity(), mv->m_vecVelocity );
  3111. }
  3112. //-----------------------------------------------------------------------------
  3113. // Purpose:
  3114. //-----------------------------------------------------------------------------
  3115. void CPortalGameMovement::WalkMove()
  3116. {
  3117. Vector wishvel;
  3118. float spd;
  3119. float fmove, smove;
  3120. Vector wishdir;
  3121. float wishspeed;
  3122. Vector dest;
  3123. trace_t pm;
  3124. Vector forward, right, up;
  3125. AngleVectors (mv->m_vecViewAngles, &forward, &right, &up); // Determine movement angles
  3126. CBaseEntity *pOldGround = player->GetGroundEntity();
  3127. // Copy movement amounts
  3128. fmove = mv->m_flForwardMove;
  3129. smove = mv->m_flSideMove;
  3130. // Keep movement vectors in our plane of movement
  3131. forward -= m_vGravityDirection * DotProduct( forward, m_vGravityDirection );
  3132. VectorNormalize( forward );
  3133. right -= m_vGravityDirection * DotProduct( right, m_vGravityDirection );
  3134. VectorNormalize( right );
  3135. // Determine velocity
  3136. wishvel = fmove * forward + smove * right;
  3137. // Don't let them stand on the edge of vertical bridges
  3138. bool shouldShovePlayer = false;
  3139. float wishVelShoveDampenFactor = 0;
  3140. Vector shoveVector = vec3_origin;
  3141. CProjectedWallEntity *pProjectedWall = dynamic_cast< CProjectedWallEntity* >( pOldGround );
  3142. if ( pProjectedWall )
  3143. {
  3144. Vector vBridgeUp = pProjectedWall->Up();
  3145. if ( vBridgeUp.z > -0.4f && vBridgeUp.z < 0.4f )
  3146. {
  3147. wishVelShoveDampenFactor = 0.25f; // Weaken their manual control
  3148. shoveVector = vBridgeUp * 150.f; // Shove!
  3149. shouldShovePlayer = true;
  3150. }
  3151. }
  3152. // Don't let players stand on top of each other
  3153. if( pOldGround && pOldGround->IsPlayer() )
  3154. {
  3155. wishVelShoveDampenFactor = 0.25f;
  3156. shoveVector = player->GetAbsOrigin() - pOldGround->GetAbsOrigin();
  3157. shoveVector.z = 0.0f;
  3158. if( shoveVector.IsZeroFast() )
  3159. shoveVector = player->Forward();
  3160. shoveVector.NormalizeInPlace();
  3161. shoveVector *= 150.0f;
  3162. shouldShovePlayer = true;
  3163. }
  3164. if( shouldShovePlayer )
  3165. {
  3166. wishvel *= wishVelShoveDampenFactor; // Weaken their manual control
  3167. wishvel += shoveVector; // Shove!
  3168. }
  3169. // Restrict wishvel to plane of movement
  3170. wishvel -= m_vGravityDirection * DotProduct( wishvel, m_vGravityDirection );
  3171. // Try funnelling
  3172. if( sv_player_funnel_into_portals.GetBool() && speed_funnelling_enabled.GetBool() )
  3173. {
  3174. wishvel += PortalFunnel( wishvel );
  3175. }
  3176. VectorCopy (wishvel, wishdir); // Determine maginitude of speed of move
  3177. wishspeed = VectorNormalize(wishdir);
  3178. //
  3179. // Clamp to server defined max speed
  3180. //
  3181. if ((wishspeed != 0.0f) && (wishspeed > mv->m_flMaxSpeed))
  3182. {
  3183. VectorScale (wishvel, mv->m_flMaxSpeed/wishspeed, wishvel);
  3184. wishspeed = mv->m_flMaxSpeed;
  3185. }
  3186. // Set pmove velocity
  3187. Accelerate ( wishdir, wishspeed, sv_accelerate.GetFloat() );
  3188. // Add in any base velocity to the current velocity.
  3189. VectorAdd (mv->m_vecVelocity, player->GetBaseVelocity(), mv->m_vecVelocity );
  3190. spd = VectorLength( mv->m_vecVelocity );
  3191. if ( spd < 1.0f )
  3192. {
  3193. mv->m_vecVelocity.Init();
  3194. // 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?)
  3195. VectorSubtract( mv->m_vecVelocity, player->GetBaseVelocity(), mv->m_vecVelocity );
  3196. return;
  3197. }
  3198. // first try just moving to the destination
  3199. dest = mv->GetAbsOrigin() + ( mv->m_vecVelocity * gpGlobals->frametime );
  3200. // first try moving directly to the next spot
  3201. TracePlayerBBox( mv->GetAbsOrigin(), dest, PlayerSolidMask(), COLLISION_GROUP_PLAYER_MOVEMENT, pm );
  3202. // If we made it all the way, then copy trace end as new player position.
  3203. mv->m_outWishVel += wishdir * wishspeed;
  3204. if ( pm.fraction == 1 )
  3205. {
  3206. mv->SetAbsOrigin( pm.endpos );
  3207. // 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?)
  3208. VectorSubtract( mv->m_vecVelocity, player->GetBaseVelocity(), mv->m_vecVelocity );
  3209. StayOnGround();
  3210. return;
  3211. }
  3212. // Don't walk up stairs if not on ground.
  3213. if ( pOldGround == NULL && player->GetWaterLevel() == 0 )
  3214. {
  3215. // 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?)
  3216. VectorSubtract( mv->m_vecVelocity, player->GetBaseVelocity(), mv->m_vecVelocity );
  3217. return;
  3218. }
  3219. // If we are jumping out of water, don't do anything more.
  3220. if ( player->m_flWaterJumpTime )
  3221. {
  3222. // 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?)
  3223. VectorSubtract( mv->m_vecVelocity, player->GetBaseVelocity(), mv->m_vecVelocity );
  3224. return;
  3225. }
  3226. // See if we collided with a ramp
  3227. if( DotProduct( pm.plane.normal, -m_vGravityDirection ) > CRITICAL_SLOPE )
  3228. {
  3229. // Compute normalized forward direction in tangent plane
  3230. const Vector vWishDirection = mv->m_vecVelocity.Normalized();
  3231. const Vector vTangentRight = CrossProduct( vWishDirection, pm.plane.normal );
  3232. const Vector vNormTangentForward = CrossProduct( pm.plane.normal, vTangentRight ).Normalized();
  3233. // Move up the ramp
  3234. float fSpeed = mv->m_vecVelocity.Length();
  3235. Vector vEndPos = player->GetAbsOrigin() + (mv->m_vecVelocity * pm.fraction + vNormTangentForward * (1.0 - pm.fraction) * fSpeed) * gpGlobals->frametime;
  3236. //above code has the distinct possibility of placing the player inside a wall. Not quite sure why it works so well most of the time. Add some error checking
  3237. if( sv_portal_new_player_trace.GetBool() )
  3238. {
  3239. trace_t rampTrace;
  3240. TracePlayerBBox( vEndPos, vEndPos, PlayerSolidMask(), COLLISION_GROUP_PLAYER_MOVEMENT, rampTrace );
  3241. if( !rampTrace.startsolid )
  3242. {
  3243. mv->SetAbsOrigin( vEndPos );
  3244. }
  3245. else
  3246. {
  3247. StepMove( dest, pm );
  3248. }
  3249. }
  3250. else
  3251. {
  3252. mv->SetAbsOrigin( vEndPos );
  3253. }
  3254. }
  3255. else
  3256. {
  3257. StepMove( dest, pm );
  3258. }
  3259. #if defined GAME_DLL
  3260. RANDOM_CEG_TEST_SECRET();
  3261. #endif
  3262. // 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?)
  3263. VectorSubtract( mv->m_vecVelocity, player->GetBaseVelocity(), mv->m_vecVelocity );
  3264. StayOnGround();
  3265. }
  3266. //-----------------------------------------------------------------------------
  3267. // Purpose:
  3268. //-----------------------------------------------------------------------------
  3269. void CPortalGameMovement::FullWalkMove()
  3270. {
  3271. if ( !CheckWater() )
  3272. {
  3273. StartGravity();
  3274. }
  3275. // If we are leaping out of the water, just update the counters.
  3276. if (player->m_flWaterJumpTime)
  3277. {
  3278. WaterJump();
  3279. TryPlayerMove( 0, 0 );
  3280. // See if we are still in water?
  3281. CheckWater();
  3282. return;
  3283. }
  3284. // If we are swimming in the water, see if we are nudging against a place we can jump up out
  3285. // of, and, if so, start out jump. Otherwise, if we are not moving up, then reset jump timer to 0
  3286. if ( player->GetWaterLevel() >= WL_Waist )
  3287. {
  3288. // don't let player jump off the water in coop
  3289. if ( !GameRules()->IsMultiplayer() )
  3290. {
  3291. if ( player->GetWaterLevel() == WL_Waist )
  3292. {
  3293. CheckWaterJump();
  3294. }
  3295. // If we are falling again, then we must not trying to jump out of water any more.
  3296. if ( DotProduct( mv->m_vecVelocity, m_vGravityDirection) > 0.f &&
  3297. player->m_flWaterJumpTime )
  3298. {
  3299. player->m_flWaterJumpTime = 0;
  3300. }
  3301. // Was jump button pressed?
  3302. if (mv->m_nButtons & IN_JUMP)
  3303. {
  3304. CheckJumpButton();
  3305. }
  3306. else
  3307. {
  3308. mv->m_nOldButtons &= ~IN_JUMP;
  3309. }
  3310. }
  3311. else
  3312. {
  3313. CPortal_Player *pPortalPlayer = static_cast< CPortal_Player* >( player );
  3314. if ( !pPortalPlayer->m_Shared.InCond( PORTAL_COND_DROWNING ) )
  3315. {
  3316. pPortalPlayer->m_Shared.AddCond( PORTAL_COND_DROWNING );
  3317. }
  3318. }
  3319. // Perform regular water movement
  3320. WaterMove();
  3321. // Redetermine position vars
  3322. CategorizePosition();
  3323. // If we are on ground, no downward velocity.
  3324. if ( player->GetGroundEntity() != NULL )
  3325. {
  3326. mv->m_vecVelocity.z = 0;
  3327. }
  3328. }
  3329. else
  3330. // Not fully underwater
  3331. {
  3332. // Was jump button pressed?
  3333. if (mv->m_nButtons & IN_JUMP)
  3334. {
  3335. CheckJumpButton();
  3336. }
  3337. else
  3338. {
  3339. mv->m_nOldButtons &= ~IN_JUMP;
  3340. }
  3341. // Fricion is handled before we add in any base velocity. That way, if we are on a conveyor,
  3342. // we don't slow when standing still, relative to the conveyor.
  3343. if (player->GetGroundEntity() != NULL)
  3344. {
  3345. mv->m_vecVelocity -= m_vGravityDirection * DotProduct( mv->m_vecVelocity, m_vGravityDirection );
  3346. player->m_Local.m_flFallVelocity = 0.0f;
  3347. Friction();
  3348. }
  3349. // Make sure velocity is valid.
  3350. CheckVelocity();
  3351. CPortal_Player *pPortalPlayer = static_cast< CPortal_Player* >( player );
  3352. if ( pPortalPlayer->m_PortalLocal.m_hTractorBeam.Get() )
  3353. {
  3354. TBeamMove();
  3355. }
  3356. else
  3357. {
  3358. Vector vecVel = mv->m_vecVelocity;
  3359. if ( player->GetGroundEntity() != NULL )
  3360. {
  3361. WalkMove();
  3362. }
  3363. else
  3364. {
  3365. #if defined CLIENT_DLL
  3366. RANDOM_CEG_TEST_SECRET();
  3367. #endif
  3368. AirMove(); // Take into account movement when in air.
  3369. }
  3370. CheckWallImpact( vecVel );
  3371. }
  3372. // Set final flags.
  3373. CategorizePosition();
  3374. // Make sure velocity is valid.
  3375. CheckVelocity();
  3376. // Add any remaining gravitational component.
  3377. if ( !CheckWater() )
  3378. {
  3379. FinishGravity();
  3380. }
  3381. // If we are on ground, no downward velocity.
  3382. if ( player->GetGroundEntity() != NULL )
  3383. {
  3384. mv->m_vecVelocity -= m_vGravityDirection * DotProduct( mv->m_vecVelocity, m_vGravityDirection );
  3385. }
  3386. CheckFalling();
  3387. }
  3388. if ( ( m_nOldWaterLevel == WL_NotInWater && player->GetWaterLevel() != WL_NotInWater ) ||
  3389. ( m_nOldWaterLevel != WL_NotInWater && player->GetWaterLevel() == WL_NotInWater ) )
  3390. {
  3391. PlaySwimSound();
  3392. #if !defined( CLIENT_DLL )
  3393. player->Splash();
  3394. #endif
  3395. }
  3396. }
  3397. //-----------------------------------------------------------------------------
  3398. // Purpose:
  3399. // Output : int
  3400. //-----------------------------------------------------------------------------
  3401. int CPortalGameMovement::TryPlayerMove( Vector *pFirstDest, trace_t *pFirstTrace )
  3402. {
  3403. int bumpcount, numbumps;
  3404. Vector dir;
  3405. float d;
  3406. int numplanes;
  3407. Vector planes[MAX_CLIP_PLANES];
  3408. Vector primal_velocity, original_velocity;
  3409. Vector new_velocity;
  3410. int i, j;
  3411. trace_t pm;
  3412. Vector end;
  3413. float time_left, allFraction;
  3414. int blocked;
  3415. numbumps = 4; // Bump up to four times
  3416. blocked = 0; // Assume not blocked
  3417. numplanes = 0; // and not sliding along any planes
  3418. VectorCopy (mv->m_vecVelocity, original_velocity); // Store original velocity
  3419. VectorCopy (mv->m_vecVelocity, primal_velocity);
  3420. allFraction = 0;
  3421. time_left = gpGlobals->frametime; // Total time for this movement operation.
  3422. new_velocity.Init();
  3423. for (bumpcount=0 ; bumpcount < numbumps; bumpcount++)
  3424. {
  3425. if ( mv->m_vecVelocity.Length() == 0.0 )
  3426. break;
  3427. // Assume we can move all the way from the current origin to the
  3428. // end point.
  3429. VectorMA( mv->GetAbsOrigin(), time_left, mv->m_vecVelocity, end );
  3430. // See if we can make it from origin to end point.
  3431. if ( g_bMovementOptimizations )
  3432. {
  3433. // If their velocity Z is 0, then we can avoid an extra trace here during WalkMove.
  3434. if ( pFirstDest && ( end == *pFirstDest ) )
  3435. {
  3436. pm = *pFirstTrace;
  3437. }
  3438. else
  3439. {
  3440. #if defined( PLAYER_GETTING_STUCK_TESTING )
  3441. trace_t foo;
  3442. TracePlayerBBox( mv->GetAbsOrigin(), mv->GetAbsOrigin(), PlayerSolidMask(), COLLISION_GROUP_PLAYER_MOVEMENT, foo );
  3443. if ( foo.startsolid || foo.fraction != 1.0f )
  3444. {
  3445. Msg( "bah\n" );
  3446. }
  3447. #endif
  3448. TracePlayerBBox( mv->GetAbsOrigin(), end, PlayerSolidMask(), COLLISION_GROUP_PLAYER_MOVEMENT, pm );
  3449. }
  3450. }
  3451. else
  3452. {
  3453. TracePlayerBBox( mv->GetAbsOrigin(), end, PlayerSolidMask(), COLLISION_GROUP_PLAYER_MOVEMENT, pm );
  3454. }
  3455. allFraction += pm.fraction;
  3456. // If we started in a solid object, or we were in solid space
  3457. // the whole way, zero out our velocity and return that we
  3458. // are blocked by floor and wall.
  3459. if (pm.allsolid)
  3460. {
  3461. // entity is trapped in another solid
  3462. VectorCopy (vec3_origin, mv->m_vecVelocity);
  3463. return 4;
  3464. }
  3465. // If we moved some portion of the total distance, then
  3466. // copy the end position into the pmove.origin and
  3467. // zero the plane counter.
  3468. if( pm.fraction > 0 )
  3469. {
  3470. // NOTE: Disabled this in portal as we don't have displacement collisions and all traces are amplified greatly making each TracePlayerBBox() really expensive
  3471. #if defined(RECHECK_TERRAIN_COLLISION_BUG)
  3472. if ( numbumps > 0 && pm.fraction == 1 )
  3473. {
  3474. // There's a precision issue with terrain tracing that can cause a swept box to successfully trace
  3475. // when the end position is stuck in the triangle. Re-run the test with an uswept box to catch that
  3476. // case until the bug is fixed.
  3477. // If we detect getting stuck, don't allow the movement
  3478. trace_t stuck;
  3479. TracePlayerBBox( pm.endpos, pm.endpos, PlayerSolidMask(), COLLISION_GROUP_PLAYER_MOVEMENT, stuck );
  3480. if ( stuck.startsolid || stuck.fraction != 1.0f )
  3481. {
  3482. //Msg( "Player will become stuck!!!\n" );
  3483. VectorCopy (vec3_origin, mv->m_vecVelocity);
  3484. break;
  3485. }
  3486. }
  3487. #endif
  3488. #if defined( PLAYER_GETTING_STUCK_TESTING )
  3489. trace_t foo;
  3490. TracePlayerBBox( pm.endpos, pm.endpos, PlayerSolidMask(), COLLISION_GROUP_PLAYER_MOVEMENT, foo );
  3491. if ( foo.startsolid || foo.fraction != 1.0f )
  3492. {
  3493. Msg( "Player will become stuck!!!\n" );
  3494. }
  3495. #endif
  3496. // actually covered some distance
  3497. mv->SetAbsOrigin( pm.endpos);
  3498. VectorCopy (mv->m_vecVelocity, original_velocity);
  3499. numplanes = 0;
  3500. }
  3501. // If we covered the entire distance, we are done
  3502. // and can return.
  3503. if (pm.fraction == 1)
  3504. {
  3505. break; // moved the entire distance
  3506. }
  3507. // Save entity that blocked us (since fraction was < 1.0)
  3508. // for contact
  3509. // Add it if it's not already in the list!!!
  3510. MoveHelper( )->AddToTouched( pm, mv->m_vecVelocity );
  3511. if ( player->GetAbsVelocity().AsVector2D().Length() > 275.0f )
  3512. {
  3513. if ( pm.m_pEnt && pm.m_pEnt->GetMoveType() == MOVETYPE_VPHYSICS )
  3514. {
  3515. // Don't let the player auto grab something that they just ran in to
  3516. // They end up holding it, but with less momentum which is confusing.
  3517. CPortal_Player *pPortalPlayer = static_cast< CPortal_Player* >( player );
  3518. pPortalPlayer->m_flAutoGrabLockOutTime = gpGlobals->curtime;
  3519. #ifdef CLIENT_DLL
  3520. STEAMWORKS_TESTSECRET();
  3521. if ( pm.m_pEnt == pPortalPlayer->m_hUseEntToSend.Get() )
  3522. {
  3523. pPortalPlayer->m_hUseEntToSend = NULL;
  3524. }
  3525. #endif
  3526. }
  3527. }
  3528. // If the plane we hit has a normal whose dot product with the gravity direction is high
  3529. // then this is probably a floor relative to our orientation
  3530. if ( DotProduct( pm.plane.normal, m_vGravityDirection ) > CRITICAL_SLOPE )
  3531. {
  3532. blocked |= 1; // floor
  3533. }
  3534. // If the plane's normal dotted with the gravity direction is 0 then it's a wall relative to our orientation
  3535. if ( DotProduct( pm.plane.normal, m_vGravityDirection ) == 0.f )
  3536. {
  3537. blocked |= 2; // step / wall
  3538. }
  3539. // Reduce amount of m_flFrameTime left by total time left * fraction
  3540. // that we covered.
  3541. time_left -= time_left * pm.fraction;
  3542. // Did we run out of planes to clip against?
  3543. if (numplanes >= MAX_CLIP_PLANES)
  3544. {
  3545. // this shouldn't really happen
  3546. // Stop our movement if so.
  3547. VectorCopy (vec3_origin, mv->m_vecVelocity);
  3548. //Con_DPrintf("Too many planes 4\n");
  3549. break;
  3550. }
  3551. // Set up next clipping plane
  3552. VectorCopy (pm.plane.normal, planes[numplanes]);
  3553. numplanes++;
  3554. // modify original_velocity so it parallels all of the clip planes
  3555. //
  3556. // reflect player velocity
  3557. // Only give this a try for first impact plane because you can get yourself stuck in an acute corner by jumping in place
  3558. // and pressing forward and nobody was really using this bounce/reflection feature anyway...
  3559. if ( numplanes == 1 &&
  3560. player->GetMoveType() == MOVETYPE_WALK &&
  3561. player->GetGroundEntity() == NULL )
  3562. {
  3563. for ( i = 0; i < numplanes; i++ )
  3564. {
  3565. if ( DotProduct( planes[i], m_vGravityDirection ) > CRITICAL_SLOPE )
  3566. {
  3567. // floor or slope
  3568. ClipVelocity( original_velocity, planes[i], new_velocity, 1 );
  3569. VectorCopy( new_velocity, original_velocity );
  3570. }
  3571. else
  3572. {
  3573. ClipVelocity( original_velocity, planes[i], new_velocity, 1.0 + sv_bounce.GetFloat() * (1 - player->m_surfaceFriction) );
  3574. }
  3575. }
  3576. VectorCopy( new_velocity, mv->m_vecVelocity );
  3577. VectorCopy( new_velocity, original_velocity );
  3578. }
  3579. else
  3580. {
  3581. for (i=0 ; i < numplanes ; i++)
  3582. {
  3583. ClipVelocity( original_velocity, planes[i], mv->m_vecVelocity, 1 );
  3584. for (j=0 ; j<numplanes ; j++)
  3585. {
  3586. if (j != i)
  3587. {
  3588. // Are we now moving against this plane?
  3589. if (mv->m_vecVelocity.Dot(planes[j]) < 0)
  3590. break; // not ok
  3591. }
  3592. }
  3593. if (j == numplanes) // Didn't have to clip, so we're ok
  3594. break;
  3595. }
  3596. // Did we go all the way through plane set
  3597. if (i != numplanes)
  3598. { // go along this plane
  3599. // pmove.velocity is set in clipping call, no need to set again.
  3600. ;
  3601. }
  3602. else
  3603. { // go along the crease
  3604. if (numplanes != 2)
  3605. {
  3606. VectorCopy (vec3_origin, mv->m_vecVelocity);
  3607. break;
  3608. }
  3609. CrossProduct (planes[0], planes[1], dir);
  3610. dir.NormalizeInPlace();
  3611. d = dir.Dot(mv->m_vecVelocity);
  3612. VectorScale (dir, d, mv->m_vecVelocity );
  3613. }
  3614. //
  3615. // if original velocity is against the original velocity, stop dead
  3616. // to avoid tiny occilations in sloping corners
  3617. //
  3618. d = mv->m_vecVelocity.Dot(primal_velocity);
  3619. if (d <= 0)
  3620. {
  3621. //Con_DPrintf("Back\n");
  3622. VectorCopy (vec3_origin, mv->m_vecVelocity);
  3623. break;
  3624. }
  3625. }
  3626. }
  3627. if ( allFraction == 0 )
  3628. {
  3629. VectorCopy (vec3_origin, mv->m_vecVelocity);
  3630. }
  3631. return blocked;
  3632. }
  3633. //-----------------------------------------------------------------------------
  3634. // Purpose:
  3635. // Input : in -
  3636. // normal -
  3637. // out -
  3638. // overbounce -
  3639. // Output : int
  3640. //-----------------------------------------------------------------------------
  3641. int CPortalGameMovement::ClipVelocity( Vector& in, Vector& normal, Vector& out, float overbounce )
  3642. {
  3643. const Vector& stickNormal = GetPortalPlayer()->GetPortalPlayerLocalData().m_StickNormal;
  3644. const float angle = DotProduct( normal, stickNormal );
  3645. int blocked = 0x00; // Assume unblocked.
  3646. if (angle > 0) // If the plane that is blocking us has a positive z component, then assume it's a floor.
  3647. blocked |= 0x01; //
  3648. if (!angle) // If the plane has no Z, it is vertical (wall/step)
  3649. blocked |= 0x02; //
  3650. // Determine how far along plane to slide based on incoming direction.
  3651. const float backoff = DotProduct( in, normal ) * overbounce;
  3652. out = in - backoff * normal;
  3653. // iterate once to make sure we aren't still moving through the plane
  3654. const float adjust = DotProduct( out, normal );
  3655. if( adjust < 0.0f )
  3656. {
  3657. out -= ( normal * adjust );
  3658. // Msg( "Adjustment = %lf\n", adjust );
  3659. }
  3660. // Return blocking flags.
  3661. return blocked;
  3662. }
  3663. //-----------------------------------------------------------------------------
  3664. // Purpose: Determine if crouch/uncrouch caused player to get stuck in world
  3665. // Input : direction -
  3666. //-----------------------------------------------------------------------------
  3667. void CPortalGameMovement::FixPlayerCrouchStuck( bool upward )
  3668. {
  3669. EntityHandle_t hitent;
  3670. int i;
  3671. Vector test;
  3672. trace_t dummy;
  3673. int direction = upward ? 1 : 0;
  3674. hitent = TestPlayerPosition( mv->GetAbsOrigin(), COLLISION_GROUP_PLAYER_MOVEMENT, dummy );
  3675. if (hitent == INVALID_ENTITY_HANDLE )
  3676. return;
  3677. VectorCopy( mv->GetAbsOrigin(), test );
  3678. for ( i = 0; i < 36; i++ )
  3679. {
  3680. Vector org = mv->GetAbsOrigin();
  3681. org += direction * -m_vGravityDirection;
  3682. mv->SetAbsOrigin( org );
  3683. hitent = TestPlayerPosition( mv->GetAbsOrigin(), COLLISION_GROUP_PLAYER_MOVEMENT, dummy );
  3684. if (hitent == INVALID_ENTITY_HANDLE )
  3685. return;
  3686. }
  3687. mv->SetAbsOrigin( test ); // Failed
  3688. }
  3689. bool CPortalGameMovement::CanUnduck()
  3690. {
  3691. Vector newOrigin = mv->GetAbsOrigin();
  3692. const CPortal_Player* pPortalPlayer = GetPortalPlayer();
  3693. if ( player->GetGroundEntity() != NULL )
  3694. {
  3695. //newOrigin += pPortalPlayer->GetDuckHullMaxs() - pPortalPlayer->GetStandHullMins();
  3696. // EXTREMELY USEFUL HACK: When sticking to the ceiling, the bounding box still expands upwards,
  3697. // but we move the player down when this happens, so the trace needs to be different
  3698. //if (onCeiling)
  3699. //{
  3700. // // Move the origin to the new position and down by an epsilon
  3701. // newOrigin.z += pPortalPlayer->GetDuckHullMaxs().z - pPortalPlayer->GetStandHullMaxs().z;
  3702. // newOrigin.z -= EQUAL_EPSILON;
  3703. //}
  3704. }
  3705. else
  3706. {
  3707. // If in air an letting go of crouch, make sure we can offset origin to make
  3708. // up for uncrouching
  3709. if ( player->m_Local.m_bDucked )
  3710. {
  3711. Vector hullSizeNormal = pPortalPlayer->GetStandHullMaxs() - pPortalPlayer->GetStandHullMins();
  3712. Vector hullSizeCrouch = pPortalPlayer->GetDuckHullMaxs() - pPortalPlayer->GetDuckHullMins();
  3713. Vector originDelta = ( hullSizeCrouch - hullSizeNormal );
  3714. originDelta *= 0.5f; //only move by half delta to maintain center
  3715. newOrigin += originDelta;
  3716. }
  3717. }
  3718. // Don't use the portal trace if the player is in a portal linked to another at a drastic angle.
  3719. // This will prevent us from finding empty space on the far side of the portal and erroneously
  3720. // deciding to uncrouch when we need to crouch to get through the portal.
  3721. bool bUsePortalTrace = true;
  3722. if( pPortalPlayer != NULL )
  3723. {
  3724. // If the player just duck jumped in a portal environment, she probably teleported.
  3725. // Make sure she doesn't screw up a fling.
  3726. const CPortal_Base2D* pPortal = pPortalPlayer->m_hPortalEnvironment.Get();
  3727. if( pPortal )
  3728. {
  3729. //portal facing at least a little bit up, player at a sufficient fling speed, and that velocity is also at least a little bit upward
  3730. if( ( player->GetGroundEntity() == NULL ) && ShouldMaintainFlingAssistCrouch( pPortal, mv->m_vecVelocity ) )
  3731. {
  3732. //fling case, require that whichever extent is closest to the portal plane has cleared it. Otherwise we unduck and catch on edges as we exit
  3733. Vector vOriginToCenter = (pPortalPlayer->GetHullMaxs() + pPortalPlayer->GetHullMins()) * 0.5f;
  3734. //Vector vPortalToCenter = mv->GetAbsOrigin() + vOriginToCenter - pPortal->m_ptOrigin;
  3735. //Vector vOffCenter = vPortalToCenter - (vPortalToCenter.Dot( pPortal->m_vForward ) * pPortal->m_vForward);
  3736. Vector vTestPoint = mv->GetAbsOrigin() + vOriginToCenter;
  3737. Vector vStandExtents = (pPortalPlayer->GetStandHullMaxs() - pPortalPlayer->GetStandHullMins()) * 0.5f;
  3738. for( int k = 0; k != 3; ++k )
  3739. {
  3740. vTestPoint[k] -= vStandExtents[k] * Sign( pPortal->m_vForward[k] );
  3741. //vTestPoint[k] += vStandExtents[k] * Sign( vOffCenter[k] );
  3742. }
  3743. if( pPortal->m_plane_Origin.normal.Dot( vTestPoint ) < pPortal->m_plane_Origin.dist )
  3744. {
  3745. return false;
  3746. }
  3747. #if defined( DEBUG_FLINGS )
  3748. s_MovementDebug.vUnduckPos = mv->GetAbsOrigin() + vOriginToCenter;
  3749. s_MovementDebug.vUnduckVel = mv->m_vecVelocity;
  3750. #endif
  3751. //NDebugOverlay::Box( mv->GetAbsOrigin() + vOriginToCenter, -vStandExtents, vStandExtents, 255, 0, 0, 100, 30.0f );
  3752. //NDebugOverlay::Box( vTestPoint, -Vector( 1.0f, 1.0f, 1.0f ), Vector( 1.0f, 1.0f, 1.0f ), 0, 0, 255, 100, 30.0f );
  3753. }
  3754. matrix3x4_t matTransform = pPortal->MatrixThisToLinked().As3x4();
  3755. Vector vWorldUp;
  3756. VectorRotate( Vector( 0.0f, 0.0f, 1.0f ), matTransform, vWorldUp );
  3757. const float COS_PI_OVER_SIX = 0.86602540378443864676372317075294f; // cos( 30 degrees ) in radians
  3758. const float flUpDot = fabs( vWorldUp.z );
  3759. // There's a significant rotation between the portal orientations, less than 90 degrees.
  3760. // 90 degree rotations don't have the disadvantage of uncrouching too soon, and doing
  3761. // so too late causes bugs when the player intentionally crouch-walks through a portal.
  3762. // Also, the player must be moving through the portal at considerable speed. Otherwise,
  3763. // it doesn't matter much when she uncrouches.
  3764. if( flUpDot < COS_PI_OVER_SIX && flUpDot >= EQUAL_EPSILON && pPortalPlayer->MaxSpeed() > sv_speed_normal.GetFloat() )
  3765. {
  3766. bUsePortalTrace = false;
  3767. }
  3768. }
  3769. }
  3770. const bool saveducked = player->m_Local.m_bDucked;
  3771. player->m_Local.m_bDucked = false;
  3772. trace_t trace;
  3773. if( bUsePortalTrace )
  3774. TracePlayerBBox( mv->GetAbsOrigin(), newOrigin, PlayerSolidMask(), COLLISION_GROUP_PLAYER_MOVEMENT, trace );
  3775. else
  3776. CGameMovement::TracePlayerBBox( mv->GetAbsOrigin(), newOrigin, PlayerSolidMask(), COLLISION_GROUP_PLAYER_MOVEMENT, trace );
  3777. player->m_Local.m_bDucked = saveducked;
  3778. // In this case, if the fraction is 1, the player can move all the way to the new origin.
  3779. if ( trace.startsolid || trace.fraction != 1.0f )
  3780. {
  3781. return false;
  3782. }
  3783. return true;
  3784. }
  3785. //-----------------------------------------------------------------------------
  3786. // Purpose: Stop ducking
  3787. //-----------------------------------------------------------------------------
  3788. void CPortalGameMovement::FinishUnDuck()
  3789. {
  3790. trace_t trace;
  3791. Vector newOrigin = mv->GetAbsOrigin();
  3792. CPortal_Player* pPortalPlayer = GetPortalPlayer();
  3793. if ( player->GetGroundEntity() != NULL )
  3794. {
  3795. //newOrigin += pPortalPlayer->GetDuckHullMaxs() - pPortalPlayer->GetStandHullMins();
  3796. // EXTREMELY USEFUL HACK: When sticking to the ceiling, the bounding box still expands upwards,
  3797. // but we move the player down when this happens, so the trace needs to be different
  3798. //if (onCeiling)
  3799. //{
  3800. // // Move the origin to the new position and down by an epsilon
  3801. // newOrigin.z += pPortalPlayer->GetDuckHullMaxs().z - pPortalPlayer->GetStandHullMaxs().z;
  3802. // newOrigin.z -= EQUAL_EPSILON;
  3803. //}
  3804. }
  3805. else
  3806. {
  3807. // If in air an letting go of crouch, make sure we can offset origin to make
  3808. // up for uncrouching
  3809. if ( player->m_Local.m_bDucked )
  3810. {
  3811. Vector hullSizeNormal = pPortalPlayer->GetStandHullMaxs() - pPortalPlayer->GetStandHullMins();
  3812. Vector hullSizeCrouch = pPortalPlayer->GetDuckHullMaxs() - pPortalPlayer->GetDuckHullMins();
  3813. Vector originDelta = ( hullSizeCrouch - hullSizeNormal );
  3814. originDelta *= 0.5f; //only move by half delta to maintain center
  3815. newOrigin += originDelta;
  3816. }
  3817. }
  3818. pPortalPlayer->SetAirDuck( false );
  3819. pPortalPlayer->UnDuck();
  3820. player->m_Local.m_bDucked = false;
  3821. player->RemoveFlag( FL_DUCKING );
  3822. player->m_Local.m_bDucking = false;
  3823. player->m_Local.m_bInDuckJump = false;
  3824. player->SetViewOffset( GetPlayerViewOffset( false ) );
  3825. player->m_Local.m_nDuckTimeMsecs = 0;
  3826. mv->SetAbsOrigin( newOrigin );
  3827. if ( pPortalPlayer )
  3828. {
  3829. pPortalPlayer->SetHullHeight( pPortalPlayer->GetHullHeight() );
  3830. }
  3831. // Recategorize position since ducking can change origin
  3832. CategorizePosition();
  3833. }
  3834. //-----------------------------------------------------------------------------
  3835. //
  3836. //-----------------------------------------------------------------------------
  3837. void CPortalGameMovement::UpdateDuckJumpEyeOffset()
  3838. {
  3839. if ( player->m_Local.m_nDuckJumpTimeMsecs != 0 )
  3840. {
  3841. int nDuckMilliseconds = MAX( 0, GAMEMOVEMENT_DUCK_TIME - player->m_Local.m_nDuckJumpTimeMsecs );
  3842. if ( nDuckMilliseconds > TIME_TO_UNDUCK_MSECS )
  3843. {
  3844. player->m_Local.m_nDuckJumpTimeMsecs = 0;
  3845. SetDuckedEyeOffset( 0.0f );
  3846. }
  3847. else
  3848. {
  3849. float flDuckFraction = SimpleSpline( 1.0f - FractionUnDucked( nDuckMilliseconds ) );
  3850. SetDuckedEyeOffset( flDuckFraction );
  3851. }
  3852. }
  3853. }
  3854. //-----------------------------------------------------------------------------
  3855. // Purpose:
  3856. //-----------------------------------------------------------------------------
  3857. void CPortalGameMovement::FinishUnDuckJump( trace_t &trace )
  3858. {
  3859. Vector vecNewOrigin;
  3860. VectorCopy( mv->GetAbsOrigin(), vecNewOrigin );
  3861. // Up for uncrouching.
  3862. /*Vector hullSizeNormal = VEC_HULL_MAX - VEC_HULL_MIN;
  3863. Vector hullSizeCrouch = VEC_DUCK_HULL_MAX - VEC_DUCK_HULL_MIN;
  3864. Vector viewDelta = ( hullSizeNormal - hullSizeCrouch );*/
  3865. Vector viewDelta = GetPlayerViewOffset( false ) - GetPlayerViewOffset( true );
  3866. float flDeltaZ = viewDelta.z;
  3867. viewDelta.z *= trace.fraction;
  3868. flDeltaZ -= viewDelta.z;
  3869. player->RemoveFlag( FL_DUCKING );
  3870. player->m_Local.m_bDucked = false;
  3871. player->m_Local.m_bDucking = false;
  3872. player->m_Local.m_bInDuckJump = false;
  3873. player->m_Local.m_nDuckTimeMsecs = 0;
  3874. player->m_Local.m_nDuckJumpTimeMsecs = 0;
  3875. player->m_Local.m_nJumpTimeMsecs = 0;
  3876. Vector vecViewOffset = GetPlayerViewOffset( false );
  3877. vecViewOffset.z -= flDeltaZ;
  3878. player->SetViewOffset( vecViewOffset );
  3879. VectorSubtract( vecNewOrigin, viewDelta, vecNewOrigin );
  3880. mv->SetAbsOrigin( vecNewOrigin );
  3881. CPortal_Player *pPortalPlayer = ToPortalPlayer( player );
  3882. if ( pPortalPlayer )
  3883. {
  3884. pPortalPlayer->SetHullHeight( pPortalPlayer->GetHullHeight() );
  3885. }
  3886. // Recategorize position since ducking can change origin
  3887. CategorizePosition();
  3888. }
  3889. //-----------------------------------------------------------------------------
  3890. // Purpose: Finish ducking
  3891. //-----------------------------------------------------------------------------
  3892. void CPortalGameMovement::FinishDuck()
  3893. {
  3894. bool bWasDucking = ( player->GetFlags() & FL_DUCKING ) > 0;
  3895. player->AddFlag( FL_DUCKING );
  3896. player->m_Local.m_bDucked = true;
  3897. player->m_Local.m_bDucking = false;
  3898. player->SetViewOffset( GetPlayerViewOffset( true ) );
  3899. CPortal_Player* pPortalPlayer = GetPortalPlayer();
  3900. // HACKHACK - Fudge for collision bug - no time to fix this properly
  3901. if ( player->GetGroundEntity() != NULL )
  3902. {
  3903. //Vector org = mv->GetAbsOrigin();
  3904. //org -= pPortalPlayer->GetDuckHullMins() - pPortalPlayer->GetStandHullMins();
  3905. //mv->SetAbsOrigin( org );
  3906. }
  3907. else
  3908. {
  3909. if ( !bWasDucking )
  3910. {
  3911. Vector hullSizeNormal = pPortalPlayer->GetStandHullMaxs() - pPortalPlayer->GetStandHullMins();
  3912. Vector hullSizeCrouch = pPortalPlayer->GetDuckHullMaxs() - pPortalPlayer->GetDuckHullMins();
  3913. Vector originDelta = ( hullSizeNormal - hullSizeCrouch );
  3914. originDelta *= 0.5f; //only move by half delta to maintain center
  3915. Vector out;
  3916. VectorAdd( mv->GetAbsOrigin(), originDelta, out );
  3917. mv->SetAbsOrigin( out );
  3918. }
  3919. }
  3920. // See if we are stuck?
  3921. FixPlayerCrouchStuck( true );
  3922. if ( pPortalPlayer )
  3923. {
  3924. pPortalPlayer->SetHullHeight( pPortalPlayer->GetHullHeight() );
  3925. }
  3926. // Recategorize position since ducking can change origin
  3927. CategorizePosition();
  3928. }
  3929. //-----------------------------------------------------------------------------
  3930. // Purpose:
  3931. //-----------------------------------------------------------------------------
  3932. void CPortalGameMovement::StartUnDuckJump()
  3933. {
  3934. player->AddFlag( FL_DUCKING );
  3935. player->m_Local.m_bDucked = true;
  3936. player->m_Local.m_bDucking = false;
  3937. player->SetViewOffset( GetPlayerViewOffset( true ) );
  3938. /*Vector hullSizeNormal = VEC_HULL_MAX - VEC_HULL_MIN;
  3939. Vector hullSizeCrouch = VEC_DUCK_HULL_MAX - VEC_DUCK_HULL_MIN;
  3940. Vector viewDelta = ( hullSizeNormal - hullSizeCrouch );*/
  3941. Vector viewDelta = GetPlayerViewOffset( false ) - GetPlayerViewOffset( true );
  3942. Vector out;
  3943. VectorAdd( mv->GetAbsOrigin(), viewDelta, out );
  3944. mv->SetAbsOrigin( out );
  3945. // See if we are stuck?
  3946. FixPlayerCrouchStuck( true );
  3947. // Recategorize position since ducking can change origin
  3948. CategorizePosition();
  3949. }
  3950. //-----------------------------------------------------------------------------
  3951. // Purpose:
  3952. // Input : duckFraction -
  3953. //-----------------------------------------------------------------------------
  3954. void CPortalGameMovement::SetDuckedEyeOffset( float duckFraction )
  3955. {
  3956. Vector vDuckHullMin = GetPlayerMins( true );
  3957. Vector vStandHullMin = GetPlayerMins( false );
  3958. //float fMore = ( vDuckHullMin.z - vStandHullMin.z );
  3959. //DevMsg( "fMore: %f\n", fMore );
  3960. CPortal_Player *pPortalPlayer = ToPortalPlayer( player );
  3961. bool bOnGround = ( player->GetGroundEntity() != NULL );
  3962. bool bDuckedInAir = pPortalPlayer->GetPortalPlayerLocalData().m_bDuckedInAir;
  3963. bool bDucking = player->m_Local.m_bDucking;
  3964. bool bDucked = player->m_Local.m_bDucked;
  3965. bool bInDuck = ( player->GetFlags() & FL_DUCKING ) ? true : false;
  3966. Vector originDelta = Vector( 0, 0, 0 );
  3967. Vector vecDuckViewOffset = GetPlayerViewOffset( true );
  3968. Vector vecStandViewOffset = GetPlayerViewOffset( false );
  3969. if ( bOnGround )
  3970. {
  3971. /*DevMsg("ground ");
  3972. if ( bDucking )
  3973. {
  3974. DevMsg("ducking\n");
  3975. }
  3976. else
  3977. {
  3978. DevMsg("UNducking\n");
  3979. }*/
  3980. }
  3981. else
  3982. {
  3983. //DevMsg("air ");
  3984. if ( bDucking )
  3985. {
  3986. // started ducking on ground, continued ducking in air
  3987. if ( !bDuckedInAir )
  3988. {
  3989. pPortalPlayer->SetAirDuck( true );
  3990. }
  3991. //DevMsg("ducking\n");
  3992. }
  3993. else
  3994. {
  3995. //DevMsg("UNducking\n");
  3996. }
  3997. }
  3998. if ( bDuckedInAir && ( bInDuck || bDucking ) && !bDucked && !bOnGround )
  3999. {
  4000. CPortal_Player *pPortalPlayer = ToPortalPlayer( player );
  4001. Vector hullSizeNormal = pPortalPlayer->GetStandHullMaxs() - pPortalPlayer->GetStandHullMins();
  4002. Vector hullSizeCrouch = pPortalPlayer->GetDuckHullMaxs() - pPortalPlayer->GetDuckHullMins();
  4003. originDelta = ( hullSizeNormal - hullSizeCrouch );
  4004. originDelta *= 0.5f;
  4005. vecDuckViewOffset += originDelta;
  4006. }
  4007. else if ( bDuckedInAir && ( bInDuck || bDucking || bDucked ) && !bOnGround )
  4008. {
  4009. CPortal_Player *pPortalPlayer = ToPortalPlayer( player );
  4010. Vector hullSizeNormal = pPortalPlayer->GetStandHullMaxs() - pPortalPlayer->GetStandHullMins();
  4011. Vector hullSizeCrouch = pPortalPlayer->GetDuckHullMaxs() - pPortalPlayer->GetDuckHullMins();
  4012. originDelta = ( hullSizeCrouch - hullSizeNormal );
  4013. originDelta *= 0.5f;
  4014. vecStandViewOffset += originDelta;
  4015. }
  4016. //int buttonsChanged = ( mv->m_nOldButtons ^ mv->m_nButtons ); // These buttons have changed this frame
  4017. //int buttonsPressed = buttonsChanged & mv->m_nButtons; // The changed ones still down are "pressed"
  4018. //int buttonsReleased = buttonsChanged & mv->m_nOldButtons; // The changed ones which were previously down are "released"
  4019. //// started ducking in the air, landed and still ducking
  4020. //if ( bDucking && bDuckedInAir && bOnGround )
  4021. //{
  4022. // vecDuckViewOffset = player->GetViewOffset();
  4023. //}
  4024. /*else if ( bOnGround && bDuckedInAir && buttonsReleased & IN_DUCK )
  4025. {
  4026. vecDuckViewOffset = player->GetViewOffset();
  4027. }*/
  4028. Vector temp = player->GetViewOffset();
  4029. temp.z = ( ( vecDuckViewOffset.z /*- fMore*/ ) * duckFraction ) +
  4030. ( vecStandViewOffset.z * ( 1 - duckFraction ) );
  4031. //DevMsg( "temp.z: %f\n", temp.z );
  4032. player->SetViewOffset( temp );
  4033. }
  4034. //-----------------------------------------------------------------------------
  4035. // Purpose: Check to see if we are in a situation where we can unduck jump.
  4036. //-----------------------------------------------------------------------------
  4037. bool CPortalGameMovement::CanUnDuckJump( trace_t &trace )
  4038. {
  4039. // Don't use the portal trace if the player is in a portal linked to another at a drastic angle.
  4040. // This will prevent us from finding empty space on the far side of the portal and erroneously
  4041. // deciding to uncrouch when we need to crouch to get through the portal.
  4042. bool bUsePortalTrace = true;
  4043. const CPortal_Player* pPortalPlayer = GetPortalPlayer();
  4044. if( pPortalPlayer != NULL )
  4045. {
  4046. // If the player just duck jumped in a portal environment, she probably teleported.
  4047. // Make sure she doesn't screw up a fling.
  4048. const CPortal_Base2D* pPortal = pPortalPlayer->m_hPortalEnvironment.Get();
  4049. if( pPortal )
  4050. {
  4051. //portal facing at least a little bit up, player at a sufficient fling speed, and that velocity is also at least a little bit upward
  4052. if( ShouldMaintainFlingAssistCrouch( pPortal, mv->m_vecVelocity ) )
  4053. {
  4054. //fling case, require that whichever extent is closest to the portal plane has cleared it. Otherwise we unduck and catch on edges as we exit
  4055. Vector vOriginToCenter = (pPortalPlayer->GetHullMaxs() + pPortalPlayer->GetHullMins()) * 0.5f;
  4056. //Vector vPortalToCenter = mv->GetAbsOrigin() + vOriginToCenter - pPortal->m_ptOrigin;
  4057. //Vector vOffCenter = vPortalToCenter - (vPortalToCenter.Dot( pPortal->m_vForward ) * pPortal->m_vForward);
  4058. Vector vTestPoint = mv->GetAbsOrigin() + vOriginToCenter;
  4059. Vector vStandExtents = (pPortalPlayer->GetStandHullMaxs() - pPortalPlayer->GetStandHullMins()) * 0.5f;
  4060. for( int k = 0; k != 3; ++k )
  4061. {
  4062. vTestPoint[k] -= vStandExtents[k] * Sign( pPortal->m_vForward[k] );
  4063. //vTestPoint[k] += vStandExtents[k] * Sign( vOffCenter[k] );
  4064. }
  4065. if( pPortal->m_plane_Origin.normal.Dot( vTestPoint ) < pPortal->m_plane_Origin.dist )
  4066. {
  4067. return false;
  4068. }
  4069. #if defined( DEBUG_FLINGS )
  4070. s_MovementDebug.vUnduckJumpPos = mv->GetAbsOrigin() + vOriginToCenter;
  4071. s_MovementDebug.vUnduckJumpVel = mv->m_vecVelocity;
  4072. #endif
  4073. //NDebugOverlay::Box( mv->GetAbsOrigin() + vOriginToCenter, -vStandExtents, vStandExtents, 0, 255, 0, 100, 30.0f );
  4074. //NDebugOverlay::Box( vTestPoint, -Vector( 1.0f, 1.0f, 1.0f ), Vector( 1.0f, 1.0f, 1.0f ), 0, 0, 255, 100, 30.0f );
  4075. }
  4076. matrix3x4_t matTransform = pPortal->MatrixThisToLinked().As3x4();
  4077. Vector vWorldUp;
  4078. VectorRotate( Vector( 0.0f, 0.0f, 1.0f ), matTransform, vWorldUp );
  4079. const float COS_PI_OVER_SIX = 0.86602540378443864676372317075294f; // cos( 30 degrees ) in radians
  4080. const float flUpDot = fabs( vWorldUp.z );
  4081. // There's a significant rotation between the portal orientations, less than 90 degrees.
  4082. // 90 degrees rotations don't have the disadvantage of uncrouching too soon, and doing
  4083. // so too late causes bugs when the player intentionally crouch-walks through a portal.
  4084. // Also, the player must be moving through the portal at considerable speed. Otherwise,
  4085. // it doesn't matter much when she uncrouches.
  4086. if( flUpDot < COS_PI_OVER_SIX && flUpDot >= EQUAL_EPSILON )
  4087. {
  4088. //if we unduckjump. That moves our feet down while keeping our top as-is. Instantly moving our center. Which can teleport us back through the portal
  4089. //That's bad. See bugbait #72275
  4090. Vector vOriginDelta = GetPlayerViewOffset( true ) - GetPlayerViewOffset( false );
  4091. if( pPortal->m_plane_Origin.normal.Dot( vOriginDelta ) < 0 ) //if delta would push center more into portal
  4092. {
  4093. Vector vUnduckedCenter = mv->GetAbsOrigin() + vOriginDelta + ((pPortalPlayer->GetStandHullMaxs() + pPortalPlayer->GetStandHullMins()) * 0.5f);
  4094. if( pPortal->m_plane_Origin.normal.Dot( vUnduckedCenter ) < pPortal->m_plane_Origin.dist ) //if unducked origin would be behind portal plane, causing a teleportation
  4095. {
  4096. return false; //block the unduck operation
  4097. }
  4098. }
  4099. if( pPortalPlayer->MaxSpeed() > sv_speed_normal.GetFloat() )
  4100. {
  4101. bUsePortalTrace = false;
  4102. }
  4103. }
  4104. }
  4105. }
  4106. // Trace down to the stand position and see if we can stand.
  4107. Vector vecEnd( mv->GetAbsOrigin() );
  4108. vecEnd.z -= 36.0f; // This will have to change if bounding hull change!
  4109. if( bUsePortalTrace )
  4110. TracePlayerBBox( mv->GetAbsOrigin(), vecEnd, PlayerSolidMask(), COLLISION_GROUP_PLAYER_MOVEMENT, trace );
  4111. else
  4112. CGameMovement::TracePlayerBBox( mv->GetAbsOrigin(), vecEnd, PlayerSolidMask(), COLLISION_GROUP_PLAYER_MOVEMENT, trace );
  4113. if ( trace.fraction < 1.0f )
  4114. {
  4115. // Find the endpoint.
  4116. vecEnd.z = mv->GetAbsOrigin().z + ( -36.0f * trace.fraction );
  4117. // Test a normal hull.
  4118. trace_t traceUp;
  4119. bool bWasDucked = player->m_Local.m_bDucked;
  4120. player->m_Local.m_bDucked = false;
  4121. if( bUsePortalTrace )
  4122. TracePlayerBBox( vecEnd, vecEnd, PlayerSolidMask(), COLLISION_GROUP_PLAYER_MOVEMENT, traceUp );
  4123. else
  4124. CGameMovement::TracePlayerBBox( vecEnd, vecEnd, PlayerSolidMask(), COLLISION_GROUP_PLAYER_MOVEMENT, traceUp );
  4125. player->m_Local.m_bDucked = bWasDucked;
  4126. if ( !traceUp.startsolid )
  4127. return true;
  4128. }
  4129. return false;
  4130. }
  4131. //-----------------------------------------------------------------------------
  4132. // Purpose: See if duck button is pressed and do the appropriate things
  4133. //-----------------------------------------------------------------------------
  4134. void CPortalGameMovement::Duck()
  4135. {
  4136. int buttonsChanged = ( mv->m_nOldButtons ^ mv->m_nButtons ); // These buttons have changed this frame
  4137. int buttonsPressed = buttonsChanged & mv->m_nButtons; // The changed ones still down are "pressed"
  4138. int buttonsReleased = buttonsChanged & mv->m_nOldButtons; // The changed ones which were previously down are "released"
  4139. // Check to see if we are in the air.
  4140. bool bInAir = ( player->GetGroundEntity() == NULL );
  4141. bool bInDuck = ( player->GetFlags() & FL_DUCKING ) ? true : false;
  4142. bool bDuckJump = ( player->m_Local.m_nJumpTimeMsecs > 0 );
  4143. bool bDuckJumpTime = ( player->m_Local.m_nDuckJumpTimeMsecs > 0 );
  4144. if ( mv->m_nButtons & IN_DUCK )
  4145. {
  4146. mv->m_nOldButtons |= IN_DUCK;
  4147. }
  4148. else
  4149. {
  4150. mv->m_nOldButtons &= ~IN_DUCK;
  4151. }
  4152. // Handle death.
  4153. if ( IsDead() )
  4154. return;
  4155. // Slow down ducked players.
  4156. HandleDuckingSpeedCrop();
  4157. // If the player is holding down the duck button, the player is in duck transition, ducking, or duck-jumping.
  4158. if ( ( mv->m_nButtons & IN_DUCK ) || player->m_Local.m_bDucking || bInDuck || bDuckJump )
  4159. {
  4160. // DUCK
  4161. if ( ( mv->m_nButtons & IN_DUCK ) || bDuckJump )
  4162. {
  4163. // XBOX SERVER ONLY
  4164. #if !defined(CLIENT_DLL)
  4165. if ( IsGameConsole() && buttonsPressed & IN_DUCK )
  4166. {
  4167. // Hinting logic
  4168. if ( player->GetToggledDuckState() && player->m_nNumCrouches < NUM_CROUCH_HINTS )
  4169. {
  4170. UTIL_HudHintText( player, "#Valve_Hint_Crouch" );
  4171. player->m_nNumCrouches++;
  4172. }
  4173. }
  4174. #endif
  4175. // Have the duck button pressed, but the player currently isn't in the duck position.
  4176. if ( ( buttonsPressed & IN_DUCK ) && !bInDuck && !bDuckJump && !bDuckJumpTime )
  4177. {
  4178. player->m_Local.m_nDuckTimeMsecs = GAMEMOVEMENT_DUCK_TIME;
  4179. player->m_Local.m_bDucking = true;
  4180. // set air ducking state
  4181. {
  4182. CPortal_Player *pPortalPlayer = ToPortalPlayer( player );
  4183. pPortalPlayer->SetAirDuck( bInAir );
  4184. }
  4185. }
  4186. // The player is in duck transition and not duck-jumping.
  4187. if ( player->m_Local.m_bDucking && !bDuckJump && !bDuckJumpTime )
  4188. {
  4189. int nDuckMilliseconds = MAX( 0, GAMEMOVEMENT_DUCK_TIME - player->m_Local.m_nDuckTimeMsecs );
  4190. // Finish in duck transition when transition time is over, in "duck", in air.
  4191. if ( ( nDuckMilliseconds > TIME_TO_DUCK_MSECS ) || bInDuck /*|| bInAir*/ )
  4192. {
  4193. FinishDuck();
  4194. }
  4195. else
  4196. {
  4197. // Calc parametric time
  4198. float flDuckFraction = SimpleSpline( FractionDucked( nDuckMilliseconds ) );
  4199. SetDuckedEyeOffset( flDuckFraction );
  4200. }
  4201. }
  4202. if ( bDuckJump )
  4203. {
  4204. // Make the bounding box small immediately.
  4205. if ( !bInDuck )
  4206. {
  4207. StartUnDuckJump();
  4208. }
  4209. else
  4210. {
  4211. // Check for a crouch override.
  4212. if ( !( mv->m_nButtons & IN_DUCK ) )
  4213. {
  4214. trace_t trace;
  4215. if ( CanUnDuckJump( trace ) )
  4216. {
  4217. FinishUnDuckJump( trace );
  4218. player->m_Local.m_nDuckJumpTimeMsecs = (int)( ( (float)GAMEMOVEMENT_TIME_TO_UNDUCK_MSECS * ( 1.0f - trace.fraction ) ) + (float)GAMEMOVEMENT_TIME_TO_UNDUCK_MSECS_INV );
  4219. }
  4220. }
  4221. }
  4222. }
  4223. }
  4224. // UNDUCK (or attempt to...)
  4225. else
  4226. {
  4227. if ( player->m_Local.m_bInDuckJump )
  4228. {
  4229. // Check for a crouch override.
  4230. if ( !( mv->m_nButtons & IN_DUCK ) )
  4231. {
  4232. trace_t trace;
  4233. if ( CanUnDuckJump( trace ) )
  4234. {
  4235. FinishUnDuckJump( trace );
  4236. if ( trace.fraction < 1.0f )
  4237. {
  4238. player->m_Local.m_nDuckJumpTimeMsecs = (int)( ( (float)GAMEMOVEMENT_TIME_TO_UNDUCK_MSECS * ( 1.0f - trace.fraction ) ) + (float)GAMEMOVEMENT_TIME_TO_UNDUCK_MSECS_INV );
  4239. }
  4240. }
  4241. }
  4242. else
  4243. {
  4244. player->m_Local.m_bInDuckJump = false;
  4245. }
  4246. }
  4247. if ( bDuckJumpTime )
  4248. return;
  4249. // Try to unduck unless automovement is not allowed
  4250. // NOTE: When not onground, you can always unduck
  4251. if ( player->m_Local.m_bAllowAutoMovement || bInAir || player->m_Local.m_bDucking )
  4252. {
  4253. // We released the duck button, we aren't in "duck" and we are not in the air - start unduck transition.
  4254. if ( ( buttonsReleased & IN_DUCK ) )
  4255. {
  4256. if ( bInDuck && !bDuckJump )
  4257. {
  4258. player->m_Local.m_nDuckTimeMsecs = GAMEMOVEMENT_DUCK_TIME;
  4259. }
  4260. else if ( player->m_Local.m_bDucking && !player->m_Local.m_bDucked )
  4261. {
  4262. // Invert time if release before fully ducked!!!
  4263. int elapsedMilliseconds = GAMEMOVEMENT_DUCK_TIME - player->m_Local.m_nDuckTimeMsecs;
  4264. float fracDucked = FractionDucked( elapsedMilliseconds );
  4265. int remainingUnduckMilliseconds = (int)( fracDucked * TIME_TO_UNDUCK_MSECS );
  4266. player->m_Local.m_nDuckTimeMsecs = GAMEMOVEMENT_DUCK_TIME - TIME_TO_UNDUCK_MSECS + remainingUnduckMilliseconds;
  4267. }
  4268. }
  4269. // Check to see if we are capable of unducking.
  4270. if ( CanUnduck() )
  4271. {
  4272. // or unducking
  4273. if ( ( player->m_Local.m_bDucking || player->m_Local.m_bDucked ) )
  4274. {
  4275. int nDuckMilliseconds = MAX( 0, GAMEMOVEMENT_DUCK_TIME - player->m_Local.m_nDuckTimeMsecs );
  4276. // Finish ducking immediately if duck time is over or not on ground
  4277. if ( nDuckMilliseconds > TIME_TO_UNDUCK_MSECS /*|| ( bInAir && !bDuckJump )*/ )
  4278. {
  4279. FinishUnDuck();
  4280. }
  4281. else
  4282. {
  4283. // Calc parametric time
  4284. float flDuckFraction = SimpleSpline( 1.0f - FractionUnDucked( nDuckMilliseconds ) );
  4285. SetDuckedEyeOffset( flDuckFraction );
  4286. player->m_Local.m_bDucking = true;
  4287. }
  4288. }
  4289. }
  4290. else
  4291. {
  4292. // Still under something where we can't unduck, so make sure we reset this timer so
  4293. // that we'll unduck once we exit the tunnel, etc.
  4294. if ( player->m_Local.m_nDuckTimeMsecs != GAMEMOVEMENT_DUCK_TIME )
  4295. {
  4296. SetDuckedEyeOffset(1.0f);
  4297. player->m_Local.m_nDuckTimeMsecs = GAMEMOVEMENT_DUCK_TIME;
  4298. player->m_Local.m_bDucked = true;
  4299. player->m_Local.m_bDucking = false;
  4300. player->AddFlag( FL_DUCKING );
  4301. }
  4302. else
  4303. {
  4304. int nDuckMilliseconds = MAX( 0, GAMEMOVEMENT_DUCK_TIME - player->m_Local.m_nDuckTimeMsecs );
  4305. // Finish ducking immediately if duck time is over
  4306. if ( nDuckMilliseconds <= TIME_TO_UNDUCK_MSECS )
  4307. {
  4308. // Calc parametric time
  4309. float flDuckFraction = SimpleSpline( 1.0f - FractionUnDucked( nDuckMilliseconds ) );
  4310. SetDuckedEyeOffset( flDuckFraction );
  4311. player->m_Local.m_bDucking = true;
  4312. }
  4313. }
  4314. }
  4315. }
  4316. }
  4317. }
  4318. // HACK: (jimd 5/25/2006) we have a reoccuring bug (#50063 in Tracker) where the player's
  4319. // view height gets left at the ducked height while the player is standing, but we haven't
  4320. // been able to repro it to find the cause. It may be fixed now due to a change I'm
  4321. // also making in UpdateDuckJumpEyeOffset but just in case, this code will sense the
  4322. // problem and restore the eye to the proper position. It doesn't smooth the transition,
  4323. // but it is preferable to leaving the player's view too low.
  4324. //
  4325. // If the player is still alive and not an observer, check to make sure that
  4326. // his view height is at the standing height.
  4327. else if ( !IsDead() && !player->IsObserver() && !player->IsInAVehicle() )
  4328. {
  4329. if ( ( player->m_Local.m_nDuckJumpTimeMsecs == 0 ) && ( fabs(player->GetViewOffset().z - GetPlayerViewOffset( false ).z) > 0.1 ) )
  4330. {
  4331. // we should rarely ever get here, so assert so a coder knows when it happens
  4332. AssertMsgOnce( 0, "Restoring player view height\n" );
  4333. // set the eye height to the non-ducked height
  4334. SetDuckedEyeOffset(0.0f);
  4335. }
  4336. }
  4337. }
  4338. static ConVar sv_optimizedmovement( "sv_optimizedmovement", "1", FCVAR_REPLICATED | FCVAR_DEVELOPMENTONLY );
  4339. //-----------------------------------------------------------------------------
  4340. // Purpose:
  4341. //-----------------------------------------------------------------------------
  4342. void CPortalGameMovement::PlayerMove()
  4343. {
  4344. VPROF( "CPortalGameMovement::PlayerMove" );
  4345. CheckParameters();
  4346. // clear output applied velocity
  4347. mv->m_outWishVel.Init();
  4348. mv->m_outJumpVel.Init();
  4349. MoveHelper( )->ResetTouchList(); // Assume we don't touch anything
  4350. ReduceTimers();
  4351. AngleVectors (mv->m_vecViewAngles, &m_vecForward, &m_vecRight, &m_vecUp ); // Determine movement angles
  4352. // Always try and unstick us unless we are using a couple of the movement modes
  4353. MoveType_t moveType = player->GetMoveType();
  4354. if ( moveType != MOVETYPE_NOCLIP &&
  4355. moveType != MOVETYPE_NONE &&
  4356. moveType != MOVETYPE_ISOMETRIC &&
  4357. moveType != MOVETYPE_OBSERVER &&
  4358. !player->pl.deadflag )
  4359. {
  4360. if ( CheckInterval( STUCK ) )
  4361. {
  4362. if ( CheckStuck() )
  4363. {
  4364. // Can't move, we're stuck
  4365. return;
  4366. }
  4367. }
  4368. }
  4369. // Now that we are "unstuck", see where we are (player->GetWaterLevel() and type, player->GetGroundEntity()).
  4370. if ( player->GetMoveType() != MOVETYPE_WALK ||
  4371. mv->m_bGameCodeMovedPlayer ||
  4372. !sv_optimizedmovement.GetBool() )
  4373. {
  4374. CategorizePosition();
  4375. }
  4376. else
  4377. {
  4378. if ( -DotProduct( mv->m_vecVelocity, m_vGravityDirection ) > 200.0f )
  4379. {
  4380. SetGroundEntity( NULL );
  4381. }
  4382. }
  4383. // Store off the starting water level
  4384. m_nOldWaterLevel = player->GetWaterLevel();
  4385. // If we are not on ground, store off how fast we are moving down
  4386. if ( player->GetGroundEntity() == NULL )
  4387. {
  4388. player->m_Local.m_flFallVelocity = DotProduct( mv->m_vecVelocity, m_vGravityDirection );
  4389. }
  4390. m_nOnLadder = 0;
  4391. player->UpdateStepSound( player->m_pSurfaceData, mv->GetAbsOrigin(), mv->m_vecVelocity );
  4392. UpdateDuckJumpEyeOffset();
  4393. Duck();
  4394. // Don't run ladder code if dead on on a train
  4395. if ( !player->pl.deadflag && !(player->GetFlags() & FL_ONTRAIN) )
  4396. {
  4397. // If was not on a ladder now, but was on one before,
  4398. // get off of the ladder
  4399. // TODO: this causes lots of weirdness.
  4400. //bool bCheckLadder = CheckInterval( LADDER );
  4401. //if ( bCheckLadder || player->GetMoveType() == MOVETYPE_LADDER )
  4402. {
  4403. if ( !LadderMove() &&
  4404. ( player->GetMoveType() == MOVETYPE_LADDER ) )
  4405. {
  4406. // Clear ladder stuff unless player is dead or riding a train
  4407. // It will be reset immediately again next frame if necessary
  4408. player->SetMoveType( MOVETYPE_WALK );
  4409. player->SetMoveCollide( MOVECOLLIDE_DEFAULT );
  4410. }
  4411. }
  4412. }
  4413. // Handle movement modes.
  4414. switch (player->GetMoveType())
  4415. {
  4416. case MOVETYPE_NONE:
  4417. break;
  4418. case MOVETYPE_NOCLIP:
  4419. FullNoClipMove( sv_noclipspeed.GetFloat(), sv_noclipaccelerate.GetFloat() );
  4420. break;
  4421. case MOVETYPE_FLY:
  4422. case MOVETYPE_FLYGRAVITY:
  4423. FullTossMove();
  4424. break;
  4425. case MOVETYPE_LADDER:
  4426. FullLadderMove();
  4427. break;
  4428. case MOVETYPE_VPHYSICS:
  4429. case MOVETYPE_WALK:
  4430. FullWalkMove();
  4431. break;
  4432. case MOVETYPE_ISOMETRIC:
  4433. //IsometricMove();
  4434. // Could also try: FullTossMove();
  4435. FullWalkMove();
  4436. break;
  4437. case MOVETYPE_OBSERVER:
  4438. FullObserverMove(); // clips against world&players
  4439. break;
  4440. default:
  4441. DevMsg( 1, "Bogus pmove player movetype %i on (%i) 0=cl 1=sv\n", player->GetMoveType(), player->IsServer());
  4442. break;
  4443. }
  4444. }
  4445. // Expose our interface.
  4446. static CPortalGameMovement g_GameMovement;
  4447. IGameMovement *g_pGameMovement = ( IGameMovement * )&g_GameMovement;
  4448. EXPOSE_SINGLE_INTERFACE_GLOBALVAR(CGameMovement, IGameMovement,INTERFACENAME_GAMEMOVEMENT, g_GameMovement );