//========= Copyright Valve Corporation, All rights reserved. ============// // // Purpose: Auto Repair // // $NoKeywords: $ //=============================================================================// #include "cbase.h" #include "tf_gamemovement_recon.h" #include "tf_movedata.h" #include "in_buttons.h" #define TIME_WALL_SUPPRESSION_JUMP 100 #define TIME_WALL_SUPPRESSION_IMPACT 400 #define TIME_WALL_STICK 50 #define TIME_STRAFE_STICK 50 #define TIME_LEAP_STICK 300 #define TIME_WALL_ACTIVATE_JUMP 300 #define TIME_WALL_INVALID -99999 #define MAX_VERTICAL_SPEED 400 //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- CTFGameMovementRecon::CTFGameMovementRecon() { m_pReconData = NULL; m_vStandMins = RECONCLASS_HULL_STAND_MIN; m_vStandMaxs = RECONCLASS_HULL_STAND_MAX; m_vStandViewOffset = RECONCLASS_VIEWOFFSET_STAND; m_vDuckMins = RECONCLASS_HULL_DUCK_MIN; m_vDuckMaxs = RECONCLASS_HULL_DUCK_MAX; m_vDuckViewOffset = RECONCLASS_VIEWOFFSET_DUCK; m_bPerformingAirMove = false; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CTFGameMovementRecon::ProcessClassMovement( CBaseTFPlayer *pPlayer, CTFMoveData *pTFMove ) { // Get the class specific data from the TFMoveData structure Assert( PlayerClassReconData_t::PLAYERCLASS_ID == pTFMove->m_nClassID ); m_pReconData = &pTFMove->ReconData(); // to test pass it through!! BaseClass::ProcessMovement( (CBasePlayer *)pPlayer, pTFMove ); // Set the jump count appropriately... // If we've hit the ground, we can double jump again... if ((player->GetGroundEntity() != NULL) && (pTFMove->ReconData().m_flStickTime == TIME_WALL_INVALID)) { m_pReconData->m_nJumpCount = 0; ResetWallImpact( (CTFMoveData*)mv ); } } void CTFGameMovementRecon::PostPlayerMove( void ) { BaseClass::PostPlayerMove( ); if (m_pReconData->m_flStickTime != TIME_WALL_INVALID) { // We're stuck, so stick! mv->m_vecVelocity.Init( 0.0f, 0.0f, 0.0f ); } } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CTFGameMovementRecon::UpdateTimers( void ) { BaseClass::UpdateTimers(); CTFMoveData *pTFMove = TFMove(); if ( !pTFMove ) return; float frame_msec = 1000.0f * gpGlobals->frametime; // Decrement the recon timers. if ( pTFMove->ReconData().m_flSuppressionJumpTime != TIME_WALL_INVALID ) { pTFMove->ReconData().m_flSuppressionJumpTime -= frame_msec; if ( pTFMove->ReconData().m_flSuppressionJumpTime <= 0.0f ) { pTFMove->ReconData().m_flSuppressionJumpTime = TIME_WALL_INVALID; } } if ( pTFMove->ReconData().m_flSuppressionImpactTime != TIME_WALL_INVALID ) { pTFMove->ReconData().m_flSuppressionImpactTime -= frame_msec; if ( pTFMove->ReconData().m_flSuppressionImpactTime <= 0.0f ) { pTFMove->ReconData().m_flSuppressionImpactTime = TIME_WALL_INVALID; } } if ( pTFMove->ReconData().m_flActiveJumpTime != TIME_WALL_INVALID ) { pTFMove->ReconData().m_flActiveJumpTime -= frame_msec; if ( pTFMove->ReconData().m_flActiveJumpTime <= 0.0f ) { pTFMove->ReconData().m_flActiveJumpTime = TIME_WALL_INVALID; } } if ( pTFMove->ReconData().m_flStickTime != TIME_WALL_INVALID ) { pTFMove->ReconData().m_flStickTime -= frame_msec; if ( pTFMove->ReconData().m_flStickTime <= 0.0f ) { pTFMove->ReconData().m_flStickTime = TIME_WALL_INVALID; // Restore velocity at this time pTFMove->m_vecVelocity = pTFMove->ReconData().m_vecUnstickVelocity; } } } //----------------------------------------------------------------------------- // Implement this if you want to know when the player collides during OnPlayerMove //----------------------------------------------------------------------------- void CTFGameMovementRecon::OnTryPlayerMoveCollision( trace_t &tr ) { if ( !m_bPerformingAirMove ) return; // Only keep track of world collisions if ( tr.DidHitWorld() ) { CTFMoveData *pTFMove = TFMove(); if ( pTFMove ) { if ( ( pTFMove->ReconData().m_flSuppressionJumpTime == TIME_WALL_INVALID ) && ( pTFMove->ReconData().m_flSuppressionImpactTime == TIME_WALL_INVALID ) ) { // No walljumps off of mostly horizontal surfaces... if ( fabs( tr.plane.normal.z ) > 0.9f ) return; // No walljumps off of the same plane as the last one... if ( (pTFMove->ReconData().m_flImpactDist == tr.plane.dist) && (VectorsAreEqual(pTFMove->ReconData().m_vecImpactNormal, tr.plane.normal, 1e-2) ) ) { return; } // If you hit a wall, no double jumps for you pTFMove->ReconData().m_nJumpCount = 2; // Play an impact sound MoveHelper()->StartSound( pTFMove->m_vecAbsOrigin, "Recon.WallJump" ); pTFMove->ReconData().m_vecImpactNormal = tr.plane.normal; pTFMove->ReconData().m_flImpactDist = tr.plane.dist; pTFMove->ReconData().m_flActiveJumpTime = TIME_WALL_ACTIVATE_JUMP; pTFMove->ReconData().m_flSuppressionImpactTime = TIME_WALL_SUPPRESSION_IMPACT; } } } } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CTFGameMovementRecon::AirMove() { m_bPerformingAirMove = true; // When in the air, recon travels ballistically if ( TFMove()->ReconData().m_nJumpCount ) { // Add in any base velocity to the current velocity. VectorAdd( mv->m_vecVelocity, player->GetBaseVelocity(), mv->m_vecVelocity ); TryPlayerMove(); } else { // But if we're falling (or coming up off ladders), treat it normally BaseClass::AirMove(); } m_bPerformingAirMove = false; } //----------------------------------------------------------------------------- // Purpose: Check the jump button to make various jumps //----------------------------------------------------------------------------- bool CTFGameMovementRecon::CheckWaterJump() { // See if we are waterjumping. If so, decrement count and return. if (player->m_flWaterJumpTime) { player->m_flWaterJumpTime -= gpGlobals->frametime; if (player->m_flWaterJumpTime < 0) player->m_flWaterJumpTime = 0; return true; } // If we are in the water most of the way... if ( player->GetWaterLevel() >= 2 ) { // swimming, not jumping SetGroundEntity( NULL ); if(player->GetWaterType() == CONTENTS_WATER) // We move up a certain amount mv->m_vecVelocity[2] = 100; else if (player->GetWaterType() == CONTENTS_SLIME) mv->m_vecVelocity[2] = 80; // play swiming sound if ( player->m_flSwimSoundTime <= 0 ) { // Don't play sound again for 1 second player->m_flSwimSoundTime = 1000; PlaySwimSound(); } return true; } return false; } //----------------------------------------------------------------------------- // Resets the impact time //----------------------------------------------------------------------------- void CTFGameMovementRecon::ResetWallImpact( CTFMoveData *pTFMove ) { if ( pTFMove->ReconData().m_flActiveJumpTime != TIME_WALL_INVALID ) { pTFMove->ReconData().m_flActiveJumpTime = TIME_WALL_INVALID; pTFMove->ReconData().m_flSuppressionImpactTime = TIME_WALL_INVALID; pTFMove->ReconData().m_vecImpactNormal.Init( 9999, 9999, 9999 ); pTFMove->ReconData().m_flImpactDist = -9999.0f; } } //----------------------------------------------------------------------------- // Purpose: Check the jump button to make various jumps //----------------------------------------------------------------------------- bool CTFGameMovementRecon::CheckWallJump( CTFMoveData *pTFMove ) { if ( player->GetGroundEntity() != NULL ) return false; if ( pTFMove->ReconData().m_flActiveJumpTime == TIME_WALL_INVALID ) return false; // Play a jump sound PlayStepSound( m_pSurfaceData, 1.0, true ); Vector jumpDir; if ( ( pTFMove->m_nButtons & ( IN_MOVELEFT | IN_MOVERIGHT ) ) ) { AngleVectors( pTFMove->m_vecViewAngles, NULL, &jumpDir, NULL ); // Apply strafe jump... jumpDir *= ( pTFMove->m_nButtons & IN_MOVELEFT ) ? -1.0f : 1.0f; jumpDir.z = 0.0f; if ( pTFMove->m_nButtons & ( IN_FORWARD | IN_BACK ) ) { Vector forward; AngleVectors( pTFMove->m_vecViewAngles, &forward, NULL, NULL ); forward *= 0.5f; forward *= ( pTFMove->m_nButtons & IN_BACK ) ? -1.0f : 1.0f; forward.z = 0.0; jumpDir += forward; } VectorNormalize( jumpDir ); jumpDir *= 400; } else { AngleVectors( pTFMove->m_vecViewAngles, &jumpDir, NULL, NULL ); jumpDir *= ( pTFMove->m_nButtons & IN_BACK ) ? -1.0f : 1.0f; jumpDir.z = 0.0; VectorNormalize( jumpDir ); jumpDir *= 400; } pTFMove->ReconData().m_flStickTime = TIME_WALL_STICK; pTFMove->ReconData().m_vecUnstickVelocity.Init( jumpDir.x, jumpDir.y, pTFMove->m_vecVelocity[2] + 1.5 * sqrt(2 * 800 * 45.0) ); if (pTFMove->ReconData().m_vecUnstickVelocity.GetZ() > MAX_VERTICAL_SPEED) pTFMove->ReconData().m_vecUnstickVelocity.SetZ( MAX_VERTICAL_SPEED ); pTFMove->m_vecVelocity.Init( 0, 0, 0 ); // Don't allow jump into wall float normalComponent = DotProduct( pTFMove->ReconData().m_vecUnstickVelocity, pTFMove->ReconData().m_vecImpactNormal ); if ( normalComponent < 0 ) { Vector vUnstickVel; VectorMA( pTFMove->ReconData().m_vecUnstickVelocity, -normalComponent, pTFMove->ReconData().m_vecImpactNormal, vUnstickVel ); pTFMove->ReconData().m_vecUnstickVelocity = vUnstickVel; } return true; } //----------------------------------------------------------------------------- // Purpose: Check the jump button to make various jumps //----------------------------------------------------------------------------- bool CTFGameMovementRecon::CheckBackJump( bool bWasInAir ) { if ((mv->m_nButtons & IN_BACK) == 0) return false; Vector jumpDir, right; AngleVectors( mv->m_vecViewAngles, &jumpDir, &right, NULL ); jumpDir.z = 0.0f; jumpDir *= -1.0f; if (mv->m_nButtons & (IN_MOVELEFT | IN_MOVERIGHT)) { // Apply strafe jump... right *= (mv->m_nButtons & IN_MOVELEFT) ? -1.0f : 1.0f; right.z = 0.0f; // Make us not jump quite at a 45% angle if both are selected right *= 0.5f; jumpDir += right; } VectorNormalize( jumpDir ); float flGroundFactor = 1.0f; if ((m_pSurfaceData) /*&& (!bWasInAir)*/ ) { flGroundFactor = m_pSurfaceData->game.jumpFactor; } jumpDir *= 150 * flGroundFactor; // Dampen current motion mv->m_vecVelocity[0] *= 0.5f; mv->m_vecVelocity[1] *= 0.5f; float flSideFactor = (bWasInAir) ? 2.0f : 1.0f; float flUpFactor = (bWasInAir) ? 0.5f : 1.5f; flSideFactor *= flGroundFactor; flUpFactor *= flGroundFactor; mv->m_vecVelocity[0] += flSideFactor * jumpDir.x; mv->m_vecVelocity[1] += flSideFactor * jumpDir.y; mv->m_vecVelocity[2] += flUpFactor * sqrt(2 * 800 * 45.0); mv->m_vecVelocity[0] = clamp( mv->m_vecVelocity[0], -200, 200 ); mv->m_vecVelocity[1] = clamp( mv->m_vecVelocity[1], -200, 200 ); if (mv->m_vecVelocity[2] > MAX_VERTICAL_SPEED) mv->m_vecVelocity[2] = MAX_VERTICAL_SPEED; return true; } //----------------------------------------------------------------------------- // Purpose: Check the jump button to make various jumps //----------------------------------------------------------------------------- bool CTFGameMovementRecon::CheckStrafeJump( bool bWasInAir ) { if ( (mv->m_nButtons & (IN_MOVELEFT | IN_MOVERIGHT)) == 0 ) return false; if (mv->m_nButtons & IN_FORWARD) return false; Vector jumpDir; AngleVectors( mv->m_vecViewAngles, NULL, &jumpDir, NULL ); // Apply strafe jump... jumpDir *= (mv->m_nButtons & IN_MOVELEFT) ? -1.0f : 1.0f; jumpDir.z = 0.0f; VectorNormalize( jumpDir ); float flGroundFactor = 1.0f; if ((m_pSurfaceData) /*&& (!bWasInAir)*/ ) { flGroundFactor = m_pSurfaceData->game.jumpFactor; } jumpDir *= 300 * flGroundFactor; // Dampen current motion mv->m_vecVelocity[0] *= 0.5f; mv->m_vecVelocity[1] *= 0.5f; mv->m_vecVelocity[0] += jumpDir.x; mv->m_vecVelocity[1] += jumpDir.y; if (!bWasInAir) mv->m_vecVelocity[2] += flGroundFactor * sqrt(2 * 800 * 45.0); // 2 * gravity * height else mv->m_vecVelocity[2] += 0.5f * sqrt(2 * 800 * 45.0); // 2 * gravity * height mv->m_vecVelocity[0] = clamp( mv->m_vecVelocity[0], -400, 400 ); mv->m_vecVelocity[1] = clamp( mv->m_vecVelocity[1], -400, 400 ); if (mv->m_vecVelocity[2] > MAX_VERTICAL_SPEED) mv->m_vecVelocity[2] = MAX_VERTICAL_SPEED; return true; } //----------------------------------------------------------------------------- // Purpose: Check the jump button to make various jumps //----------------------------------------------------------------------------- bool CTFGameMovementRecon::CheckForwardJump( bool bWasInAir ) { // If we are ducking... if ( ( player->m_Local.m_bDucking ) || ( player->GetFlags() & FL_DUCKING ) ) { // d = 0.5 * g * t^2 - distance traveled with linear accel // t = sqrt(2.0 * 45 / g) - how long to fall 45 units // v = g * t - velocity at the end (just invert it to jump up that high) // v = g * sqrt(2.0 * 45 / g ) // v^2 = g * g * 2.0 * 45 / g // v = sqrt( g * 2.0 * 45 ) mv->m_vecVelocity[2] = sqrt(2 * 800 * 45.0); // 2 * gravity * height } else { Vector forward, right; AngleVectors( mv->m_vecViewAngles, &forward, &right, NULL ); forward.z = 0.0; if ((mv->m_nButtons & IN_FORWARD) == 0) { forward.x = forward.y = 0.0f; } if (mv->m_nButtons & (IN_MOVELEFT | IN_MOVERIGHT)) { // Apply strafe jump... right *= (mv->m_nButtons & IN_MOVELEFT) ? -1.0f : 1.0f; right.z = 0.0f; // Make us not jump quite at a 45% angle if both are selected right *= 0.5f; forward += right; } VectorNormalize( forward ); // Slow down by the speed factor float flGroundFactor = 1.0f; if ((m_pSurfaceData) /* && (!bWasInAir) */ ) { flGroundFactor = m_pSurfaceData->game.jumpFactor; } forward *= 400 * flGroundFactor; // Dampen current motion mv->m_vecVelocity[0] *= 0.5f; mv->m_vecVelocity[1] *= 0.5f; mv->m_vecVelocity[0] += forward.x; mv->m_vecVelocity[1] += forward.y; float flUpFactor = (bWasInAir) ? 0.7f : 1.0f; flUpFactor *= flGroundFactor; mv->m_vecVelocity[2] += flUpFactor * MAX_VERTICAL_SPEED; // Limit their velocity in X and Y. We don't want to just clamp because that will change the // direction we're moving in. for ( int i=0; i < 2; i++ ) { float flAbs = fabs( mv->m_vecVelocity[i] ); if ( flAbs > 400 ) { mv->m_vecVelocity[0] *= (400.0f / flAbs); mv->m_vecVelocity[1] *= (400.0f / flAbs); } } if (mv->m_vecVelocity[2] > MAX_VERTICAL_SPEED) mv->m_vecVelocity[2] = MAX_VERTICAL_SPEED; } return true; } //----------------------------------------------------------------------------- // Purpose: Check the jump button to make various jumps //----------------------------------------------------------------------------- bool CTFGameMovementRecon::CheckJumpButton() { // FIXME: Refactor this so we don't have this complicated duplicate // code here + in gamemovement.cpp if ( player->pl.deadflag ) { mv->m_nOldButtons |= IN_JUMP ; // don't jump again until released return false; } // Water jumps! if ( CheckWaterJump() ) return false; if ( mv->m_nOldButtons & IN_JUMP ) return false; // don't pogo stick CTFMoveData *pTFMove = static_cast( mv ); // Check for wall jump... if ( !CheckWallJump( pTFMove ) ) { // If we already did one air jump, can't do another if ( (player->GetGroundEntity() == NULL ) && ( pTFMove->ReconData().m_nJumpCount > 1) ) { mv->m_nOldButtons |= IN_JUMP; return false; // in air, so no effect } pTFMove->ReconData().m_nJumpCount += 1; // Am I doing a double-jump? bool bWasInAir = (player->GetGroundEntity() == NULL); // In the air now. SetGroundEntity( NULL ); PlayStepSound( m_pSurfaceData, 1.0, true ); if (!CheckBackJump(bWasInAir)) { if (CheckStrafeJump(bWasInAir)) { // Can't double jump out of a roll.... pTFMove->ReconData().m_nJumpCount += 1; } else { CheckForwardJump(bWasInAir); } } } pTFMove->ReconData().m_flSuppressionJumpTime = TIME_WALL_SUPPRESSION_JUMP; FinishGravity(); mv->m_outWishVel = mv->m_vecVelocity; mv->m_outStepHeight += 0.1f; // Flag that we jumped. mv->m_nOldButtons |= IN_JUMP; // don't jump again until released return true; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- const Vector &CTFGameMovementRecon::GetPlayerMins( bool bDucked ) const { return bDucked ? m_vDuckMins : m_vStandMins; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- const Vector &CTFGameMovementRecon::GetPlayerMaxs( bool bDucked ) const { return bDucked ? m_vDuckMaxs : m_vStandMaxs; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- const Vector &CTFGameMovementRecon::GetPlayerViewOffset( bool bDucked ) const { return bDucked ? m_vDuckViewOffset : m_vStandViewOffset; }