Team Fortress 2 Source Code as on 22/4/2020
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

752 lines
22 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: Special handling for Portal usable ladders
  4. //
  5. //=============================================================================//
  6. #include "cbase.h"
  7. #include "hl_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 "prop_portal_shared.h"
  14. #include "rumble_shared.h"
  15. #if defined( CLIENT_DLL )
  16. #include "c_portal_player.h"
  17. #include "c_rumble.h"
  18. #else
  19. #include "portal_player.h"
  20. #include "env_player_surface_trigger.h"
  21. #include "portal_gamestats.h"
  22. #include "physicsshadowclone.h"
  23. #include "recipientfilter.h"
  24. #include "SoundEmitterSystem/isoundemittersystembase.h"
  25. #endif
  26. // memdbgon must be the last include file in a .cpp file!!!
  27. #include "tier0/memdbgon.h"
  28. ConVar sv_player_trace_through_portals("sv_player_trace_through_portals", "1", FCVAR_REPLICATED | FCVAR_CHEAT, "Causes player movement traces to trace through portals." );
  29. ConVar sv_player_funnel_into_portals("sv_player_funnel_into_portals", "1", FCVAR_REPLICATED | FCVAR_ARCHIVE | FCVAR_ARCHIVE_XBOX, "Causes the player to auto correct toward the center of floor portals." );
  30. class CReservePlayerSpot;
  31. #define PORTAL_FUNNEL_AMOUNT 6.0f
  32. extern bool g_bAllowForcePortalTrace;
  33. extern bool g_bForcePortalTrace;
  34. static inline CBaseEntity *TranslateGroundEntity( CBaseEntity *pGroundEntity )
  35. {
  36. #ifndef CLIENT_DLL
  37. CPhysicsShadowClone *pClone = dynamic_cast<CPhysicsShadowClone *>(pGroundEntity);
  38. if( pClone && pClone->IsUntransformedClone() )
  39. {
  40. CBaseEntity *pSource = pClone->GetClonedEntity();
  41. if( pSource )
  42. return pSource;
  43. }
  44. #endif //#ifndef CLIENT_DLL
  45. return pGroundEntity;
  46. }
  47. //-----------------------------------------------------------------------------
  48. // Purpose: Portal specific movement code
  49. //-----------------------------------------------------------------------------
  50. class CPortalGameMovement : public CHL2GameMovement
  51. {
  52. typedef CGameMovement BaseClass;
  53. public:
  54. CPortalGameMovement();
  55. bool m_bInPortalEnv;
  56. // Overrides
  57. virtual void ProcessMovement( CBasePlayer *pPlayer, CMoveData *pMove );
  58. virtual bool CheckJumpButton( void );
  59. void FunnelIntoPortal( CProp_Portal *pPortal, Vector &wishdir );
  60. virtual void AirAccelerate( Vector& wishdir, float wishspeed, float accel );
  61. virtual void AirMove( void );
  62. virtual void PlayerRoughLandingEffects( float fvol );
  63. virtual void CategorizePosition( void );
  64. // Traces the player bbox as it is swept from start to end
  65. virtual void TracePlayerBBox( const Vector& start, const Vector& end, unsigned int fMask, int collisionGroup, trace_t& pm );
  66. // Tests the player position
  67. virtual CBaseHandle TestPlayerPosition( const Vector& pos, int collisionGroup, trace_t& pm );
  68. virtual void Duck( void ); // Check for a forced duck
  69. virtual int CheckStuck( void );
  70. virtual void SetGroundEntity( trace_t *pm );
  71. private:
  72. CPortal_Player *GetPortalPlayer();
  73. };
  74. //-----------------------------------------------------------------------------
  75. // Purpose:
  76. //-----------------------------------------------------------------------------
  77. CPortalGameMovement::CPortalGameMovement()
  78. {
  79. }
  80. //-----------------------------------------------------------------------------
  81. // Purpose:
  82. //-----------------------------------------------------------------------------
  83. inline CPortal_Player *CPortalGameMovement::GetPortalPlayer()
  84. {
  85. return static_cast< CPortal_Player * >( player );
  86. }
  87. //-----------------------------------------------------------------------------
  88. // Purpose:
  89. // Input : *pMove -
  90. //-----------------------------------------------------------------------------
  91. void CPortalGameMovement::ProcessMovement( CBasePlayer *pPlayer, CMoveData *pMove )
  92. {
  93. Assert( pMove && pPlayer );
  94. float flStoreFrametime = gpGlobals->frametime;
  95. //!!HACK HACK: Adrian - slow down all player movement by this factor.
  96. //!!Blame Yahn for this one.
  97. gpGlobals->frametime *= pPlayer->GetLaggedMovementValue();
  98. ResetGetPointContentsCache();
  99. // Cropping movement speed scales mv->m_fForwardSpeed etc. globally
  100. // Once we crop, we don't want to recursively crop again, so we set the crop
  101. // flag globally here once per usercmd cycle.
  102. m_iSpeedCropped = SPEED_CROPPED_RESET;
  103. player = pPlayer;
  104. mv = pMove;
  105. mv->m_flMaxSpeed = sv_maxspeed.GetFloat();
  106. m_bInPortalEnv = (((CPortal_Player *)pPlayer)->m_hPortalEnvironment != NULL);
  107. g_bAllowForcePortalTrace = m_bInPortalEnv;
  108. g_bForcePortalTrace = m_bInPortalEnv;
  109. // Run the command.
  110. PlayerMove();
  111. FinishMove();
  112. g_bAllowForcePortalTrace = false;
  113. g_bForcePortalTrace = false;
  114. #ifndef CLIENT_DLL
  115. pPlayer->UnforceButtons( IN_DUCK );
  116. pPlayer->UnforceButtons( IN_JUMP );
  117. #endif
  118. //This is probably not needed, but just in case.
  119. gpGlobals->frametime = flStoreFrametime;
  120. }
  121. //-----------------------------------------------------------------------------
  122. // Purpose: Base jump behavior, plus an anim event
  123. // Input : -
  124. // Output : Returns true on success, false on failure.
  125. //-----------------------------------------------------------------------------
  126. bool CPortalGameMovement::CheckJumpButton()
  127. {
  128. if ( BaseClass::CheckJumpButton() && GetPortalPlayer() )
  129. {
  130. GetPortalPlayer()->DoAnimationEvent( PLAYERANIMEVENT_JUMP, 0 );
  131. return true;
  132. }
  133. return false;
  134. }
  135. void CPortalGameMovement::FunnelIntoPortal( CProp_Portal *pPortal, Vector &wishdir )
  136. {
  137. // Make sure there's a portal
  138. if ( !pPortal )
  139. return;
  140. // Get portal vectors
  141. Vector vPortalForward, vPortalRight, vPortalUp;
  142. pPortal->GetVectors( &vPortalForward, &vPortalRight, &vPortalUp );
  143. // Make sure it's a floor portal
  144. if ( vPortalForward.z < 0.8f )
  145. return;
  146. vPortalRight.z = 0.0f;
  147. vPortalUp.z = 0.0f;
  148. VectorNormalize( vPortalRight );
  149. VectorNormalize( vPortalUp );
  150. // Make sure the player is looking downward
  151. CPortal_Player *pPlayer = GetPortalPlayer();
  152. Vector vPlayerForward;
  153. pPlayer->EyeVectors( &vPlayerForward );
  154. if ( vPlayerForward.z > -0.1f )
  155. return;
  156. Vector vPlayerOrigin = pPlayer->GetAbsOrigin();
  157. Vector vPlayerToPortal = pPortal->GetAbsOrigin() - vPlayerOrigin;
  158. // Make sure the player is trying to air control, they're falling downward and they are vertically close to the portal
  159. if ( fabsf( wishdir[ 0 ] ) > 64.0f || fabsf( wishdir[ 1 ] ) > 64.0f || mv->m_vecVelocity[ 2 ] > -165.0f || vPlayerToPortal.z < -512.0f )
  160. return;
  161. // Make sure we're in the 2D portal rectangle
  162. if ( ( vPlayerToPortal.Dot( vPortalRight ) * vPortalRight ).Length() > PORTAL_HALF_WIDTH * 1.5f )
  163. return;
  164. if ( ( vPlayerToPortal.Dot( vPortalUp ) * vPortalUp ).Length() > PORTAL_HALF_HEIGHT * 1.5f )
  165. return;
  166. if ( vPlayerToPortal.z > -8.0f )
  167. {
  168. // We're too close the the portal to continue correcting, but zero the velocity so our fling velocity is nice
  169. mv->m_vecVelocity[ 0 ] = 0.0f;
  170. mv->m_vecVelocity[ 1 ] = 0.0f;
  171. }
  172. else
  173. {
  174. // Funnel toward the portal
  175. float fFunnelX = vPlayerToPortal.x * PORTAL_FUNNEL_AMOUNT - mv->m_vecVelocity[ 0 ];
  176. float fFunnelY = vPlayerToPortal.y * PORTAL_FUNNEL_AMOUNT - mv->m_vecVelocity[ 1 ];
  177. wishdir[ 0 ] += fFunnelX;
  178. wishdir[ 1 ] += fFunnelY;
  179. }
  180. }
  181. //-----------------------------------------------------------------------------
  182. // Purpose:
  183. // Input : wishdir -
  184. // accel -
  185. //-----------------------------------------------------------------------------
  186. void CPortalGameMovement::AirAccelerate( Vector& wishdir, float wishspeed, float accel )
  187. {
  188. int i;
  189. float addspeed, accelspeed, currentspeed;
  190. float wishspd;
  191. wishspd = wishspeed;
  192. if (player->pl.deadflag)
  193. return;
  194. if (player->m_flWaterJumpTime)
  195. return;
  196. // Cap speed
  197. if (wishspd > 60.0f)
  198. wishspd = 60.0f;
  199. // Determine veer amount
  200. currentspeed = mv->m_vecVelocity.Dot(wishdir);
  201. // See how much to add
  202. addspeed = wishspd - currentspeed;
  203. // If not adding any, done.
  204. if (addspeed <= 0)
  205. return;
  206. // Determine acceleration speed after acceleration
  207. accelspeed = accel * wishspeed * gpGlobals->frametime * player->m_surfaceFriction;
  208. // Cap it
  209. if (accelspeed > addspeed)
  210. accelspeed = addspeed;
  211. // Adjust pmove vel.
  212. for (i=0 ; i<3 ; i++)
  213. {
  214. mv->m_vecVelocity[i] += accelspeed * wishdir[i];
  215. mv->m_outWishVel[i] += accelspeed * wishdir[i];
  216. }
  217. }
  218. //-----------------------------------------------------------------------------
  219. // Purpose:
  220. //-----------------------------------------------------------------------------
  221. void CPortalGameMovement::AirMove( void )
  222. {
  223. int i;
  224. Vector wishvel;
  225. float fmove, smove;
  226. Vector wishdir;
  227. float wishspeed;
  228. Vector forward, right, up;
  229. AngleVectors (mv->m_vecViewAngles, &forward, &right, &up); // Determine movement angles
  230. // Copy movement amounts
  231. fmove = mv->m_flForwardMove;
  232. smove = mv->m_flSideMove;
  233. // Zero out z components of movement vectors
  234. forward[2] = 0;
  235. right[2] = 0;
  236. VectorNormalize(forward); // Normalize remainder of vectors
  237. VectorNormalize(right); //
  238. for (i=0 ; i<2 ; i++) // Determine x and y parts of velocity
  239. wishvel[i] = forward[i]*fmove + right[i]*smove;
  240. wishvel[2] = 0; // Zero out z part of velocity
  241. VectorCopy (wishvel, wishdir); // Determine maginitude of speed of move
  242. //
  243. // Don't let the player screw their fling because of adjusting into a floor portal
  244. //
  245. if ( mv->m_vecVelocity[ 0 ] * mv->m_vecVelocity[ 0 ] + mv->m_vecVelocity[ 1 ] * mv->m_vecVelocity[ 1 ] > MIN_FLING_SPEED * MIN_FLING_SPEED )
  246. {
  247. if ( mv->m_vecVelocity[ 0 ] > MIN_FLING_SPEED * 0.5f && wishdir[ 0 ] < 0.0f )
  248. wishdir[ 0 ] = 0.0f;
  249. else if ( mv->m_vecVelocity[ 0 ] < -MIN_FLING_SPEED * 0.5f && wishdir[ 0 ] > 0.0f )
  250. wishdir[ 0 ] = 0.0f;
  251. if ( mv->m_vecVelocity[ 1 ] > MIN_FLING_SPEED * 0.5f && wishdir[ 1 ] < 0.0f )
  252. wishdir[ 1 ] = 0.0f;
  253. else if ( mv->m_vecVelocity[ 1 ] < -MIN_FLING_SPEED * 0.5f && wishdir[ 1 ] > 0.0f )
  254. wishdir[ 1 ] = 0.0f;
  255. }
  256. //
  257. // Try to autocorrect the player to fall into the middle of the portal
  258. //
  259. else if ( sv_player_funnel_into_portals.GetBool() )
  260. {
  261. int iPortalCount = CProp_Portal_Shared::AllPortals.Count();
  262. if( iPortalCount != 0 )
  263. {
  264. CProp_Portal **pPortals = CProp_Portal_Shared::AllPortals.Base();
  265. for( int i = 0; i != iPortalCount; ++i )
  266. {
  267. CProp_Portal *pTempPortal = pPortals[i];
  268. if( pTempPortal->IsActivedAndLinked() )
  269. {
  270. FunnelIntoPortal( pTempPortal, wishdir );
  271. }
  272. }
  273. }
  274. }
  275. wishspeed = VectorNormalize(wishdir);
  276. //
  277. // clamp to server defined max speed
  278. //
  279. if ( wishspeed != 0 && (wishspeed > mv->m_flMaxSpeed))
  280. {
  281. VectorScale (wishvel, mv->m_flMaxSpeed/wishspeed, wishvel);
  282. wishspeed = mv->m_flMaxSpeed;
  283. }
  284. AirAccelerate( wishdir, wishspeed, 15.0f );
  285. // Add in any base velocity to the current velocity.
  286. VectorAdd(mv->m_vecVelocity, player->GetBaseVelocity(), mv->m_vecVelocity );
  287. TryPlayerMove();
  288. // 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?)
  289. VectorSubtract( mv->m_vecVelocity, player->GetBaseVelocity(), mv->m_vecVelocity );
  290. }
  291. void CPortalGameMovement::PlayerRoughLandingEffects( float fvol )
  292. {
  293. BaseClass::PlayerRoughLandingEffects( fvol );
  294. #ifndef CLIENT_DLL
  295. if ( fvol >= 1.0 )
  296. {
  297. // Play the future shoes sound
  298. CRecipientFilter filter;
  299. filter.AddRecipientsByPAS( player->GetAbsOrigin() );
  300. CSoundParameters params;
  301. if ( CBaseEntity::GetParametersForSound( "PortalPlayer.FallRecover", params, NULL ) )
  302. {
  303. EmitSound_t ep( params );
  304. ep.m_nPitch = 125.0f - player->m_Local.m_flFallVelocity * 0.03f; // lower pitch the harder they land
  305. ep.m_flVolume = MIN( player->m_Local.m_flFallVelocity * 0.00075f - 0.38, 1.0f ); // louder the harder they land
  306. CBaseEntity::EmitSound( filter, player->entindex(), ep );
  307. }
  308. }
  309. #endif
  310. }
  311. void TracePlayerBBoxForGround2( const Vector& start, const Vector& end, const Vector& minsSrc,
  312. const Vector& maxsSrc, IHandleEntity *player, unsigned int fMask,
  313. int collisionGroup, trace_t& pm )
  314. {
  315. VPROF( "TracePlayerBBoxForGround" );
  316. CPortal_Player *pPortalPlayer = dynamic_cast<CPortal_Player *>(player->GetRefEHandle().Get());
  317. CProp_Portal *pPlayerPortal = pPortalPlayer->m_hPortalEnvironment;
  318. #ifndef CLIENT_DLL
  319. if( pPlayerPortal && pPlayerPortal->m_PortalSimulator.IsReadyToSimulate() == false )
  320. pPlayerPortal = NULL;
  321. #endif
  322. Ray_t ray;
  323. Vector mins, maxs;
  324. float fraction = pm.fraction;
  325. Vector endpos = pm.endpos;
  326. // Check the -x, -y quadrant
  327. mins = minsSrc;
  328. maxs.Init( MIN( 0, maxsSrc.x ), MIN( 0, maxsSrc.y ), maxsSrc.z );
  329. ray.Init( start, end, mins, maxs );
  330. if( pPlayerPortal )
  331. UTIL_Portal_TraceRay( pPlayerPortal, ray, fMask, player, collisionGroup, &pm );
  332. else
  333. UTIL_TraceRay( ray, fMask, player, collisionGroup, &pm );
  334. if ( pm.m_pEnt && pm.plane.normal[2] >= 0.7)
  335. {
  336. pm.fraction = fraction;
  337. pm.endpos = endpos;
  338. return;
  339. }
  340. // Check the +x, +y quadrant
  341. mins.Init( MAX( 0, minsSrc.x ), MAX( 0, minsSrc.y ), minsSrc.z );
  342. maxs = maxsSrc;
  343. ray.Init( start, end, mins, maxs );
  344. if( pPlayerPortal )
  345. UTIL_Portal_TraceRay( pPlayerPortal, ray, fMask, player, collisionGroup, &pm );
  346. else
  347. UTIL_TraceRay( ray, fMask, player, collisionGroup, &pm );
  348. if ( pm.m_pEnt && pm.plane.normal[2] >= 0.7)
  349. {
  350. pm.fraction = fraction;
  351. pm.endpos = endpos;
  352. return;
  353. }
  354. // Check the -x, +y quadrant
  355. mins.Init( minsSrc.x, MAX( 0, minsSrc.y ), minsSrc.z );
  356. maxs.Init( MIN( 0, maxsSrc.x ), maxsSrc.y, maxsSrc.z );
  357. ray.Init( start, end, mins, maxs );
  358. if( pPlayerPortal )
  359. UTIL_Portal_TraceRay( pPlayerPortal, ray, fMask, player, collisionGroup, &pm );
  360. else
  361. UTIL_TraceRay( ray, fMask, player, collisionGroup, &pm );
  362. if ( pm.m_pEnt && pm.plane.normal[2] >= 0.7)
  363. {
  364. pm.fraction = fraction;
  365. pm.endpos = endpos;
  366. return;
  367. }
  368. // Check the +x, -y quadrant
  369. mins.Init( MAX( 0, minsSrc.x ), minsSrc.y, minsSrc.z );
  370. maxs.Init( maxsSrc.x, MIN( 0, maxsSrc.y ), maxsSrc.z );
  371. ray.Init( start, end, mins, maxs );
  372. if( pPlayerPortal )
  373. UTIL_Portal_TraceRay( pPlayerPortal, ray, fMask, player, collisionGroup, &pm );
  374. else
  375. UTIL_TraceRay( ray, fMask, player, collisionGroup, &pm );
  376. if ( pm.m_pEnt && pm.plane.normal[2] >= 0.7)
  377. {
  378. pm.fraction = fraction;
  379. pm.endpos = endpos;
  380. return;
  381. }
  382. pm.fraction = fraction;
  383. pm.endpos = endpos;
  384. }
  385. //-----------------------------------------------------------------------------
  386. // Purpose:
  387. // Input : &input -
  388. //-----------------------------------------------------------------------------
  389. void CPortalGameMovement::CategorizePosition( void )
  390. {
  391. Vector point;
  392. trace_t pm;
  393. // if the player hull point one unit down is solid, the player
  394. // is on ground
  395. // see if standing on something solid
  396. // Doing this before we move may introduce a potential latency in water detection, but
  397. // doing it after can get us stuck on the bottom in water if the amount we move up
  398. // is less than the 1 pixel 'threshold' we're about to snap to. Also, we'll call
  399. // this several times per frame, so we really need to avoid sticking to the bottom of
  400. // water on each call, and the converse case will correct itself if called twice.
  401. CheckWater();
  402. // observers don't have a ground entity
  403. if ( player->IsObserver() )
  404. return;
  405. point[0] = mv->GetAbsOrigin()[0];
  406. point[1] = mv->GetAbsOrigin()[1];
  407. point[2] = mv->GetAbsOrigin()[2] - 2;
  408. Vector bumpOrigin;
  409. bumpOrigin = mv->GetAbsOrigin();
  410. // Shooting up really fast. Definitely not on ground.
  411. // On ladder moving up, so not on ground either
  412. // NOTE: 145 is a jump.
  413. if ( mv->m_vecVelocity[2] > 140 ||
  414. ( mv->m_vecVelocity[2] > 0.0f && player->GetMoveType() == MOVETYPE_LADDER ) )
  415. {
  416. SetGroundEntity( NULL );
  417. }
  418. else
  419. {
  420. // Try and move down.
  421. TracePlayerBBox( bumpOrigin, point, MASK_PLAYERSOLID, COLLISION_GROUP_PLAYER_MOVEMENT, pm );
  422. // If we hit a steep plane, we are not on ground
  423. if ( pm.plane.normal[2] < 0.7)
  424. {
  425. // Test four sub-boxes, to see if any of them would have found shallower slope we could
  426. // actually stand on
  427. TracePlayerBBoxForGround2( bumpOrigin, point, GetPlayerMins(), GetPlayerMaxs(), mv->m_nPlayerHandle.Get(), MASK_PLAYERSOLID, COLLISION_GROUP_PLAYER_MOVEMENT, pm );
  428. if ( pm.plane.normal[2] < 0.7)
  429. {
  430. SetGroundEntity( NULL ); // too steep
  431. // probably want to add a check for a +z velocity too!
  432. if ( ( mv->m_vecVelocity.z > 0.0f ) && ( player->GetMoveType() != MOVETYPE_NOCLIP ) )
  433. {
  434. player->m_surfaceFriction = 0.25f;
  435. }
  436. }
  437. else
  438. {
  439. SetGroundEntity( &pm ); // Otherwise, point to index of ent under us.
  440. }
  441. }
  442. else
  443. {
  444. SetGroundEntity( &pm ); // Otherwise, point to index of ent under us.
  445. }
  446. // If we are on something...
  447. if (player->GetGroundEntity() != NULL)
  448. {
  449. // Then we are not in water jump sequence
  450. player->m_flWaterJumpTime = 0;
  451. // If we could make the move, drop us down that 1 pixel
  452. if ( player->GetWaterLevel() < WL_Waist && !pm.startsolid && !pm.allsolid )
  453. {
  454. // check distance we would like to move -- this is supposed to just keep up
  455. // "on the ground" surface not stap us back to earth (i.e. on move origin to
  456. // end position when the ground is within .5 units away) (2 units)
  457. if( pm.fraction )
  458. // if( pm.fraction < 0.5)
  459. {
  460. mv->SetAbsOrigin( pm.endpos );
  461. }
  462. }
  463. }
  464. #ifndef CLIENT_DLL
  465. //Adrian: vehicle code handles for us.
  466. if ( player->IsInAVehicle() == false )
  467. {
  468. // If our gamematerial has changed, tell any player surface triggers that are watching
  469. IPhysicsSurfaceProps *physprops = MoveHelper()->GetSurfaceProps();
  470. surfacedata_t *pSurfaceProp = physprops->GetSurfaceData( pm.surface.surfaceProps );
  471. char cCurrGameMaterial = pSurfaceProp->game.material;
  472. if ( !player->GetGroundEntity() )
  473. {
  474. cCurrGameMaterial = 0;
  475. }
  476. // Changed?
  477. if ( player->m_chPreviousTextureType != cCurrGameMaterial )
  478. {
  479. CEnvPlayerSurfaceTrigger::SetPlayerSurface( player, cCurrGameMaterial );
  480. }
  481. player->m_chPreviousTextureType = cCurrGameMaterial;
  482. }
  483. #endif
  484. }
  485. }
  486. void CPortalGameMovement::Duck( void )
  487. {
  488. return BaseClass::Duck();
  489. }
  490. int CPortalGameMovement::CheckStuck( void )
  491. {
  492. if( BaseClass::CheckStuck() )
  493. {
  494. CPortal_Player *pPortalPlayer = GetPortalPlayer();
  495. #ifndef CLIENT_DLL
  496. if( pPortalPlayer->IsAlive() )
  497. g_PortalGameStats.Event_PlayerStuck( pPortalPlayer );
  498. #endif
  499. //try to fix it, then recheck
  500. Vector vIndecisive;
  501. if( pPortalPlayer->m_hPortalEnvironment )
  502. {
  503. pPortalPlayer->m_hPortalEnvironment->GetVectors( &vIndecisive, NULL, NULL );
  504. }
  505. else
  506. {
  507. vIndecisive.Init( 0.0f, 0.0f, 1.0f );
  508. }
  509. Vector ptOldOrigin = pPortalPlayer->GetAbsOrigin();
  510. if( pPortalPlayer->m_hPortalEnvironment )
  511. {
  512. if( !FindClosestPassableSpace( pPortalPlayer, vIndecisive ) )
  513. {
  514. #ifndef CLIENT_DLL
  515. DevMsg( "Hurting the player for FindClosestPassableSpaceFailure!" );
  516. CTakeDamageInfo info( pPortalPlayer, pPortalPlayer, vec3_origin, vec3_origin, 1e10, DMG_CRUSH );
  517. pPortalPlayer->OnTakeDamage( info );
  518. #endif
  519. }
  520. //make sure we didn't get put behind the portal >_<
  521. Vector ptCurrentOrigin = pPortalPlayer->GetAbsOrigin();
  522. if( vIndecisive.Dot( ptCurrentOrigin - ptOldOrigin ) < 0.0f )
  523. {
  524. 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
  525. }
  526. }
  527. mv->SetAbsOrigin( pPortalPlayer->GetAbsOrigin() );
  528. return BaseClass::CheckStuck();
  529. }
  530. else
  531. {
  532. return 0;
  533. }
  534. }
  535. void CPortalGameMovement::SetGroundEntity( trace_t *pm )
  536. {
  537. #ifndef CLIENT_DLL
  538. if ( !player->GetGroundEntity() && pm && pm->m_pEnt )
  539. {
  540. IGameEvent *event = gameeventmanager->CreateEvent( "portal_player_touchedground" );
  541. if ( event )
  542. {
  543. event->SetInt( "userid", player->GetUserID() );
  544. gameeventmanager->FireEvent( event );
  545. }
  546. }
  547. #endif
  548. BaseClass::SetGroundEntity( pm );
  549. }
  550. void CPortalGameMovement::TracePlayerBBox( const Vector& start, const Vector& end, unsigned int fMask, int collisionGroup, trace_t& pm )
  551. {
  552. VPROF( "CGameMovement::TracePlayerBBox" );
  553. CPortal_Player *pPortalPlayer = (CPortal_Player *)((CBaseEntity *)mv->m_nPlayerHandle.Get());
  554. Ray_t ray;
  555. ray.Init( start, end, GetPlayerMins(), GetPlayerMaxs() );
  556. #ifdef CLIENT_DLL
  557. CTraceFilterSimple traceFilter( mv->m_nPlayerHandle.Get(), collisionGroup );
  558. #else
  559. CTraceFilterSimple baseFilter( mv->m_nPlayerHandle.Get(), collisionGroup );
  560. CTraceFilterTranslateClones traceFilter( &baseFilter );
  561. #endif
  562. UTIL_Portal_TraceRay_With( pPortalPlayer->m_hPortalEnvironment, ray, fMask, &traceFilter, &pm );
  563. // If we're moving through a portal and failed to hit anything with the above ray trace
  564. // Use UTIL_Portal_TraceEntity to test this movement through a portal and override the trace with the result
  565. if ( pm.fraction == 1.0f && UTIL_DidTraceTouchPortals( ray, pm ) && sv_player_trace_through_portals.GetBool() )
  566. {
  567. trace_t tempTrace;
  568. UTIL_Portal_TraceEntity( pPortalPlayer, start, end, fMask, &traceFilter, &tempTrace );
  569. if ( tempTrace.DidHit() && tempTrace.fraction < pm.fraction && !tempTrace.startsolid && !tempTrace.allsolid )
  570. {
  571. pm = tempTrace;
  572. }
  573. }
  574. }
  575. CBaseHandle CPortalGameMovement::TestPlayerPosition( const Vector& pos, int collisionGroup, trace_t& pm )
  576. {
  577. TracePlayerBBox( pos, pos, MASK_PLAYERSOLID, collisionGroup, pm ); //hook into the existing portal special trace functionality
  578. //Ray_t ray;
  579. //ray.Init( pos, pos, GetPlayerMins(), GetPlayerMaxs() );
  580. //UTIL_TraceRay( ray, MASK_PLAYERSOLID, mv->m_nPlayerHandle.Get(), collisionGroup, &pm );
  581. if( pm.startsolid && pm.m_pEnt && (pm.contents & MASK_PLAYERSOLID) )
  582. {
  583. #ifdef _DEBUG
  584. AssertMsgOnce( false, "The player got stuck on something. Break to investigate." ); //happens enough to just leave in a perma-debugger
  585. //this next trace is PURELY for tracking down how the player got stuck. Nothing new is discovered over the same trace about 10 lines up
  586. TracePlayerBBox( pos, pos, MASK_PLAYERSOLID, collisionGroup, pm );
  587. #endif
  588. return pm.m_pEnt->GetRefEHandle();
  589. }
  590. #ifndef CLIENT_DLL
  591. else if ( pm.startsolid && pm.m_pEnt && CPSCollisionEntity::IsPortalSimulatorCollisionEntity( pm.m_pEnt ) )
  592. {
  593. // Stuck in a portal environment object, so unstick them!
  594. CPortal_Player *pPortalPlayer = (CPortal_Player *)((CBaseEntity *)mv->m_nPlayerHandle.Get());
  595. pPortalPlayer->SetStuckOnPortalCollisionObject();
  596. return INVALID_EHANDLE_INDEX;
  597. }
  598. #endif
  599. else
  600. {
  601. return INVALID_EHANDLE_INDEX;
  602. }
  603. }
  604. // Expose our interface.
  605. static CPortalGameMovement g_GameMovement;
  606. IGameMovement *g_pGameMovement = ( IGameMovement * )&g_GameMovement;
  607. EXPOSE_SINGLE_INTERFACE_GLOBALVAR(CGameMovement, IGameMovement,INTERFACENAME_GAMEMOVEMENT, g_GameMovement );