void CPlayerStateJump::Land( CPlayer &player, const bool isHeavyWeapon, float frameTime ) { if(gEnv->bMultiplayer && IsJumping()) { m_jumpLock = min(g_pGameCVars->pl_jump_baseTimeAddedPerJump + (m_jumpLock * g_pGameCVars->pl_jump_currentTimeMultiplierOnJump), g_pGameCVars->pl_jump_maxTimerValue); } const float fHeightofEntity = player.GetEntity()->GetWorldPos().z; if (player.IsClient()) { CPlayerStateUtil::ApplyFallDamage( player, m_startFallingHeight, fHeightofEntity ); } // TODO: Physics sync. const float fallSpeed = player.m_stats.fallSpeed; Landed(player, isHeavyWeapon, fabsf(player.GetActorPhysics().velocityDelta.z)); // fallspeed might be incorrect on a dedicated server (pos is synced from client, but also smoothed). player.m_stats.wasHit = false; SetJumpState(player, JState_None); if(player.m_stats.fallSpeed) { player.m_stats.fallSpeed = 0.0f; const float worldWaterLevel = player.m_playerStateSwim_WaterTestProxy.GetWaterLevel(); if(fHeightofEntity < worldWaterLevel) { player.CreateScriptEvent("jump_splash", worldWaterLevel-fHeightofEntity); } } }
bool CPlayerStateUtil::ShouldJump( CPlayer& player, const SActorFrameMovementParams& movement ) { const bool allowJump = movement.jump && !player.IsJumping(); //m_playerStateJump.IsJumping(); if (allowJump) { // TODO: We need a TryJump (stephenn). // This is needed when jumping while standing directly under something that causes an immediate land, // before and without even being airborne for one frame. // PlayerMovement set m_stats.onGround=0.0f when the jump is triggered, // which prevents the on ground timer from before the jump to be inherited // and incorrectly and prematurely used to identify landing in MP. const SPlayerStats& stats = *player.GetActorStats(); const SActorPhysics& actorPhysics = player.GetActorPhysics(); const float fUnconstrainedZ = actorPhysics.velocityUnconstrained.z; bool jumpFailed = (stats.onGround > 0.0f) && (fUnconstrainedZ <= 0.0f); const float onGroundTime = 0.2f; if( ((stats.onGround > onGroundTime) || player.IsRemote()) ) // && !jumpFailed ) { return true; } else { CCCPOINT_IF(true, PlayerMovement_PressJumpWhileNotAllowedToJump); } } return false; }
bool CPlayerStateSwim::DetectJump( CPlayer& player, const SActorFrameMovementParams& movement, float frameTime, float* pVerticalSpeedModifier) const { const float minInWaterTime = 0.35f; const CPlayerStateSwim_WaterTestProxy& waterProxy = player.m_playerStateSwim_WaterTestProxy; const bool allowJump = waterProxy.GetSwimmingTimer() > minInWaterTime; if (allowJump) { // we broke the surface at a velocity enough to dolphin jump. if( !m_onSurface && (waterProxy.GetRelativeWaterLevel() > -GetSwimParams().m_swimDolphinJumpDepth) ) { const float velZ = player.GetActorPhysics().velocity.z; if( (velZ > GetSwimParams().m_swimDolphinJumpThresholdSpeed) ) { if( pVerticalSpeedModifier ) { *pVerticalSpeedModifier = GetSwimParams().m_swimDolphinJumpSpeedModification; } return true; } } } return false; }
bool CPlayerStateJump::UpdateCommon( CPlayer& player, const bool isHeavyWeapon, const Vec3 &move, float frameTime, Vec3* pDesiredVel ) { GetDesiredVelocity(move, player, pDesiredVel); const SActorPhysics& actorPhysics = player.GetActorPhysics(); // generate stats. if (actorPhysics.velocity*actorPhysics.gravity>0.0f) { const float fHeightofEntity = player.GetEntity()->GetWorldTM().GetTranslation().z; m_startFallingHeight= (float)__fsel(-player.m_stats.fallSpeed, fHeightofEntity, max(m_startFallingHeight, fHeightofEntity)); player.m_stats.fallSpeed = -actorPhysics.velocity.z; } if (!gEnv->bMultiplayer && player.IsInPickAndThrowMode() && (player.m_stats.fallSpeed > 10.f)) player.ExitPickAndThrow(); // inAir is set to 0.0f if we're swimming later - before refactoring this test happened *after* that, hence this test is here. m_jumpLock = (float)__fsel(-fabsf(player.m_stats.inAir), max(0.0f, m_jumpLock - frameTime), m_jumpLock); return true; }
const Vec3 CPlayerStateJump::CalculateInAirJumpExtraVelocity( const CPlayer& player, const Vec3& desiredVelocity ) const { const SPlayerStats& stats = player.m_stats; const float speedUpFactor = 0.175f; Vec3 jumpExtraVelocity(0.0f, 0.0f, 0.0f); const SActorPhysics& actorPhysics = player.GetActorPhysics(); if (actorPhysics.velocity.z > 0.0f) { //Note: Desired velocity is flat (not 'z' component), so jumpHeight should not be altered jumpExtraVelocity = desiredVelocity * speedUpFactor; } else { //Note: this makes the jump feel less 'floaty', by accelerating the player slightly down // and compensates the extra traveled distance when going up const float g = actorPhysics.gravity.len(); if (g > 0.0f) { const float jumpHeightScale = 1.0f; const float jumpHeight = player.m_params.jumpHeight * jumpHeightScale; const float estimatedLandTime = sqrt_tpl(2.0f*jumpHeight*(1.0f/g)) * (1.0f - speedUpFactor); assert(estimatedLandTime > 0.0f); if (estimatedLandTime > 0.0f) { const float requiredGravity = (2.0f*jumpHeight)/(estimatedLandTime * estimatedLandTime); const float initialAccelerationScale = clamp_tpl((-actorPhysics.velocity.z * 0.6f), 0.0f, 1.0f); jumpExtraVelocity = (requiredGravity - g) * actorPhysics.gravity.GetNormalized() * initialAccelerationScale; } } } return jumpExtraVelocity; }
void CPlayerStateUtil::ApplyFallDamage( CPlayer& player, const float startFallingHeight, const float fHeightofEntity ) { CRY_ASSERT(player.IsClient()); // Zero downwards impact velocity used for fall damage calculations if player was in water within the last 0.5 seconds. // Strength jumping straight up and down should theoretically land with a safe velocity, // but together with the water surface stickyness the velocity can sometimes go above the safe impact velocity threshold. // DEPRECATED: comment left for prosterity in case dedicated server problems re-appear (author cannot test it). // On dedicated server the player can still be flying this frame as well, // since synced pos from client is interpolated/smoothed and will not land immediately, // even though the velocity is set to zero. // Thus we need to use the velocity change instead of landing to identify impact. // DT: 12475: Falling a huge distance to a ledge grab results in no damage. // Now using the last velocity because when ledge grabbing the velocity is unchanged for this frame, thus zero damage is applied. // Assuming this a physics lag issue, using the last good velocity should be more-or-less ok. const float downwardsImpactSpeed = -(float)__fsel(-(player.m_playerStateSwim_WaterTestProxy.GetSwimmingTimer() + 0.5f), player.GetActorPhysics().velocityUnconstrainedLast.z, 0.0f); const SPlayerStats& stats = *player.GetActorStats(); CRY_ASSERT(NumberValid(downwardsImpactSpeed)); const float MIN_FALL_DAMAGE_DISTANCE = 3.0f; const float fallDist = startFallingHeight - fHeightofEntity; if ((downwardsImpactSpeed > 0.0f) && (fallDist > MIN_FALL_DAMAGE_DISTANCE)) { const SPlayerHealth& healthCVars = g_pGameCVars->pl_health; float velSafe = healthCVars.fallDamage_SpeedSafe; float velFatal = healthCVars.fallDamage_SpeedFatal; float velFraction = (float)__fsel(-(velFatal - velSafe), 1.0f , (downwardsImpactSpeed - velSafe) * (float)__fres(velFatal - velSafe)); CRY_ASSERT(NumberValid(velFraction)); if (velFraction > 0.0f) { //Stop crouching after taking falling damage if(player.GetStance() == STANCE_CROUCH) { static_cast<CPlayerInput*>(player.GetPlayerInput())->ClearCrouchAction(); } velFraction = powf(velFraction, gEnv->bMultiplayer ? healthCVars.fallDamage_CurveAttackMP : healthCVars.fallDamage_CurveAttack); const float maxHealth = player.GetMaxHealth(); const float currentHealth = player.GetHealth(); HitInfo hit; hit.dir.zero(); hit.type = CGameRules::EHitType::Fall; hit.shooterId = hit.targetId = hit.weaponId = player.GetEntityId(); const float maxDamage = (float)__fsel(velFraction - 1.0f, maxHealth, max(0.0f, (gEnv->bMultiplayer?maxHealth:currentHealth) - healthCVars.fallDamage_health_threshold)); hit.damage = velFraction * maxDamage; g_pGame->GetGameRules()->ClientHit(hit); #ifdef PLAYER_MOVEMENT_DEBUG_ENABLED player.GetMovementDebug().LogFallDamage(player.GetEntity(), velFraction, downwardsImpactSpeed, hit.damage); } else { player.GetMovementDebug().LogFallDamageNone(player.GetEntity(), downwardsImpactSpeed); } #else } #endif }
void CPlayerStateUtil::UpdateRemotePlayersInterpolation( CPlayer& player, const SActorFrameMovementParams& movement, SCharacterMoveRequest& frameRequest ) { if (!gEnv->bMultiplayer) return; ////////////////////////////////////////////////////////////////////////// /// Interpolation for remote players const bool isRemoteClient = player.IsRemote(); const bool doVelInterpolation = isRemoteClient && player.GetPlayerInput(); if (doVelInterpolation) { if (g_pGameCVars->pl_clientInertia >= 0.0f) { player.m_inertia = g_pGameCVars->pl_clientInertia; } player.m_inertiaAccel = player.m_inertia; const bool isNetJumping = g_pGameCVars->pl_velocityInterpSynchJump && movement.jump && isRemoteClient; if (isNetJumping) { #ifdef STATE_DEBUG if (g_pGameCVars->pl_debugInterpolation) { CryWatch("SetVel (%f, %f, %f)", player.m_jumpVel.x, player.m_jumpVel.y, player.m_jumpVel.z); } #endif //_RELEASE frameRequest.velocity = player.m_jumpVel; frameRequest.type = eCMT_JumpAccumulate; } else { CNetPlayerInput *playerInput = static_cast<CNetPlayerInput*>(player.GetPlayerInput()); Vec3 interVel, desiredVel; bool inAir; desiredVel = playerInput->GetDesiredVelocity(inAir); interVel = desiredVel; IPhysicalEntity* pPhysEnt = player.GetEntity()->GetPhysics(); CRY_ASSERT_MESSAGE(pPhysEnt, "Entity not physicalised! TomB would like to look at this."); const bool isPlayerInAir = player.IsInAir(); if (inAir && isPlayerInAir) { if (pPhysEnt && (g_pGameCVars->pl_velocityInterpAirDeltaFactor < 1.0f)) { pe_status_dynamics dynStat; pPhysEnt->GetStatus(&dynStat); Vec3 velDiff = interVel - dynStat.v; interVel = dynStat.v + (velDiff * g_pGameCVars->pl_velocityInterpAirDeltaFactor); frameRequest.velocity.z = interVel.z; frameRequest.velocity.z -= player.GetActorPhysics().gravity.z*gEnv->pTimer->GetFrameTime(); frameRequest.type = eCMT_Fly; } } frameRequest.velocity.x = interVel.x; frameRequest.velocity.y = interVel.y; #ifdef STATE_DEBUG if (pPhysEnt && g_pGameCVars->pl_debugInterpolation) { pe_status_dynamics dynStat; pPhysEnt->GetStatus(&dynStat); CryWatch("Cur: (%f %f %f) Des: (%f %f %f)", dynStat.v.x, dynStat.v.y, dynStat.v.z, desiredVel.x, desiredVel.y, desiredVel.z); CryWatch("Req: (%f %f %f) Type (%d)", frameRequest.velocity.x, frameRequest.velocity.y, frameRequest.velocity.z, frameRequest.type); } #endif //!_RELEASE } } }
bool CPlayerStateUtil::IsOnGround( CPlayer& player ) { return( !player.GetActorPhysics().flags.AreAnyFlagsActive(SActorPhysics::EActorPhysicsFlags_Flying) ); }
void CPlayerStateGround::OnPrePhysicsUpdate( CPlayer& player, const SActorFrameMovementParams &movement, float frameTime, const bool isHeavyWeapon, const bool isPlayer ) { const Matrix34A baseMtx = Matrix34A(player.GetBaseQuat()); Matrix34A baseMtxZ(baseMtx * Matrix33::CreateScale(Vec3Constants<float>::fVec3_OneZ)); baseMtxZ.SetTranslation(Vec3Constants<float>::fVec3_Zero); const CAutoAimManager& autoAimManager = g_pGame->GetAutoAimManager(); const EntityId closeCombatTargetId = autoAimManager.GetCloseCombatSnapTarget(); const IActor* pCloseCombatTarget = isPlayer && closeCombatTargetId && player.IsClient() ? g_pGame->GetIGameFramework()->GetIActorSystem()->GetActor(closeCombatTargetId) : NULL; if (pCloseCombatTarget) { ProcessAlignToTarget(autoAimManager, player, pCloseCombatTarget); } else { // This is to restore inertia if the ProcessAlignToTarget set it previously. if( m_inertiaIsZero ) { CPlayerStateUtil::RestorePlayerPhysics( player ); m_inertiaIsZero = false; } //process movement const bool isRemote = isPlayer && !player.IsClient(); Vec3 move(ZERO); CPlayerStateUtil::CalculateGroundOrJumpMovement( player, movement, isHeavyWeapon, move ); player.GetMoveRequest().type = eCMT_Normal; //apply movement Vec3 desiredVel(ZERO); Vec3 entityPos = player.GetEntity()->GetWorldPos(); Vec3 entityRight(player.GetBaseQuat().GetColumn0()); hwvec3 xmDesiredVel = HWV3Zero(); hwmtx33 xmBaseMtxZ; HWMtx33LoadAligned(xmBaseMtxZ, baseMtxZ); hwmtx33 xmBaseMtxZOpt = HWMtx33GetOptimized(xmBaseMtxZ); hwvec3 xmMove = HWVLoadVecUnaligned(&move); simdf fGroundNormalZ; #ifdef STATE_DEBUG bool debugJumping = (g_pGameCVars->pl_debug_jumping != 0); #endif const SPlayerStats& stats = *player.GetActorStats(); { xmDesiredVel = xmMove; Vec3 groundNormal = player.GetActorPhysics().groundNormal; if(!gEnv->bMultiplayer) { if (player.IsAIControlled()) fGroundNormalZ = SIMDFLoadFloat(square(groundNormal.z)); else fGroundNormalZ = SIMDFLoadFloat(groundNormal.z); } else { //If the hill steepness is greater than our minimum threshold if(groundNormal.z > 1.f - cosf(g_pGameCVars->pl_movement.mp_slope_speed_multiplier_minHill)) { //Check if we are trying to move up or downhill groundNormal.z = 0.f; groundNormal.Normalize(); Vec3 moveDir = move; moveDir.z = 0.f; moveDir.Normalize(); float normalDotMove = groundNormal.Dot(moveDir); //Apply speed multiplier based on moving up/down hill and hill steepness float multiplier = normalDotMove < 0.f ? g_pGameCVars->pl_movement.mp_slope_speed_multiplier_uphill : g_pGameCVars->pl_movement.mp_slope_speed_multiplier_downhill; fGroundNormalZ = SIMDFLoadFloat(1.f - (1.f - player.GetActorPhysics().groundNormal.z) * multiplier); } else { fGroundNormalZ = SIMDFLoadFloat(1.0f); } } const float depthHi = g_pGameCVars->cl_shallowWaterDepthHi; const float depthLo = g_pGameCVars->cl_shallowWaterDepthLo; const float relativeBottomDepth = player.m_playerStateSwim_WaterTestProxy.GetRelativeBottomDepth(); if( relativeBottomDepth > depthLo ) { // Shallow water speed slowdown float shallowWaterMultiplier = 1.0f; shallowWaterMultiplier = isPlayer ? g_pGameCVars->cl_shallowWaterSpeedMulPlayer : g_pGameCVars->cl_shallowWaterSpeedMulAI; shallowWaterMultiplier = max(shallowWaterMultiplier, 0.1f); assert(shallowWaterMultiplier <= 1.0f); float shallowWaterDepthSpan = (depthHi - depthLo); shallowWaterDepthSpan = max(0.1f, shallowWaterDepthSpan); float slowdownFraction = (relativeBottomDepth - depthLo) / shallowWaterDepthSpan; slowdownFraction = clamp_tpl(slowdownFraction, 0.0f, 1.0f); shallowWaterMultiplier = LERP(1.0f, shallowWaterMultiplier, slowdownFraction); //avoid branch if m_stats.relativeBottomDepth <= 0.0f; shallowWaterMultiplier = (float)__fsel(-relativeBottomDepth, 1.0f, shallowWaterMultiplier); simdf vfShallowWaterMultiplier = SIMDFLoadFloat(shallowWaterMultiplier); xmDesiredVel = HWVMultiplySIMDF(xmDesiredVel, vfShallowWaterMultiplier); } } // Slow down on sloped terrain, simply proportional to the slope. xmDesiredVel = HWVMultiplySIMDF(xmDesiredVel, fGroundNormalZ); //be sure desired velocity is flat to the ground hwvec3 vDesiredVelVert = HWMtx33RotateVecOpt(xmBaseMtxZOpt, xmDesiredVel); xmDesiredVel = HWVSub(xmDesiredVel, vDesiredVelVert); HWVSaveVecUnaligned(&desiredVel, xmDesiredVel); if (isPlayer) { Vec3 modifiedSlopeNormal = player.GetActorPhysics().groundNormal; float h = Vec2(modifiedSlopeNormal.x, modifiedSlopeNormal.y).GetLength(); // TODO: OPT: sqrt(x*x+y*y) float v = modifiedSlopeNormal.z; float slopeAngleCur = RAD2DEG(atan2_tpl(h, v)); const float divisorH = (float)__fsel(-h, 1.0f, h); const float divisorV = (float)__fsel(-v, 1.0f, v); const float invV = __fres(divisorV); const float invH = __fres(divisorH); const float slopeAngleHor = 10.0f; const float slopeAngleVer = 50.0f; float slopeAngleFraction = clamp_tpl((slopeAngleCur - slopeAngleHor) * __fres(slopeAngleVer - slopeAngleHor), 0.0f, 1.0f); slopeAngleFraction = slopeAngleFraction * slopeAngleFraction * slopeAngleFraction; float slopeAngleMod = LERP(0.0f, 90.0f, slopeAngleFraction); float s, c; sincos_tpl(DEG2RAD(slopeAngleMod), &s, &c); const float hMultiplier = (float)__fsel(-h, 1.0f, s * invH); const float vMultiplier = (float)__fsel(-v, 1.0f, c * invV); modifiedSlopeNormal.x *= hMultiplier; modifiedSlopeNormal.y *= hMultiplier; modifiedSlopeNormal.z *= vMultiplier; //Normalize the slope normal if possible const float fSlopeNormalLength = modifiedSlopeNormal.len(); const float fSlopeNormalLengthSafe = (float)__fsel(fSlopeNormalLength - 0.000001f, fSlopeNormalLength, 1.0f); modifiedSlopeNormal = modifiedSlopeNormal * __fres(fSlopeNormalLengthSafe); float alignment = min(modifiedSlopeNormal * desiredVel, 0.0f); // Also affect air control (but not as much), to prevent jumping up against steep slopes. alignment *= (float)__fsel(-fabsf(stats.onGround), LERP(0.7f, 1.0f, 1.0f - clamp_tpl(modifiedSlopeNormal.z * 100.0f, 0.0f, 1.0f)), 1.0f); modifiedSlopeNormal.z = modifiedSlopeNormal.z; desiredVel -= modifiedSlopeNormal * alignment; #ifdef STATE_DEBUG if (debugJumping) { player.DebugGraph_AddValue("GroundSlope", slopeAngleCur); player.DebugGraph_AddValue("GroundSlopeMod", slopeAngleMod); } #endif } Vec3 newVelocity = desiredVel; const float fNewSpeed = newVelocity.len(); const float fVelocityMultiplier = (float)__fsel(fNewSpeed - 22.0f, __fres(fNewSpeed+FLT_EPSILON) * 22.0f, 1.0f); // TODO: Maybe we should tell physics about this new velocity ? Or maybe SPlayerStats::velocity ? (stephenn). player.GetMoveRequest().velocity = newVelocity * (stats.flashBangStunMult * fVelocityMultiplier); #ifdef STATE_DEBUG if(g_pGameCVars->pl_debug_movement > 0) { const char* filter = g_pGameCVars->pl_debug_filter->GetString(); const char* name = player.GetEntity()->GetName(); if ((strcmp(filter, "0") == 0) || (strcmp(filter, name) == 0)) { float white[] = {1.0f,1.0f,1.0f,1.0f}; gEnv->pRenderer->Draw2dLabel(20, 450, 2.0f, white, false, "Speed: %.3f m/s", player.GetMoveRequest().velocity.len()); if(g_pGameCVars->pl_debug_movement > 1) { gEnv->pRenderer->Draw2dLabel(35, 470, 1.8f, white, false, "Stance Speed: %.3f m/s - (%sSprinting)", player.GetStanceMaxSpeed(player.GetStance()), player.IsSprinting() ? "" : "Not "); } } } #endif } if( isPlayer ) { CheckForVaultTrigger(player, frameTime); } }
void CPlayerStateJump::StartJump( CPlayer& player, const bool isHeavyWeapon, const float fVerticalSpeedModifier ) { const SActorPhysics& actorPhysics = player.GetActorPhysics(); const SPlayerStats& stats = *player.GetActorStats(); const float onGroundTime = 0.2f; float g = actorPhysics.gravity.len(); const float jumpHeightScale = 1.0f; const float jumpHeight = player.GetActorParams().jumpHeight * jumpHeightScale; float playerZ = player.GetEntity()->GetWorldPos().z; float expectedJumpEndHeight = playerZ + jumpHeight; pe_player_dimensions dimensions; IPhysicalEntity *pPhysics = player.GetEntity()->GetPhysics(); if (pPhysics && pPhysics->GetParams(&dimensions)) { float physicsBottom = dimensions.heightCollider - dimensions.sizeCollider.z; if (dimensions.bUseCapsule) { physicsBottom -= dimensions.sizeCollider.x; } expectedJumpEndHeight += physicsBottom; } float jumpSpeed = 0.0f; if (g > 0.0f) { jumpSpeed = sqrt_tpl(2.0f*jumpHeight*(1.0f/g)) * g; if( isHeavyWeapon ) { jumpSpeed *= g_pGameCVars->pl_movement.nonCombat_heavy_weapon_speed_scale; } } //this is used to easily find steep ground float slopeDelta = (Vec3Constants<float>::fVec3_OneZ - actorPhysics.groundNormal).len(); SetJumpState(player, JState_Jump); Vec3 jumpVec(ZERO); bool bNormalJump = true; player.PlaySound(CPlayer::ESound_Jump); OnSpecialMove(player, IPlayerEventListener::eSM_Jump); CCCPOINT_IF( player.IsClient(), PlayerMovement_LocalPlayerNormalJump); CCCPOINT_IF(!player.IsClient(), PlayerMovement_NonLocalPlayerNormalJump); { // This was causing the vertical jumping speed to be much slower. float verticalMult = max(1.0f - m_jumpLock, 0.3f); const Quat baseQuat = player.GetBaseQuat(); jumpVec += baseQuat.GetColumn2() * jumpSpeed * verticalMult; jumpVec.z += fVerticalSpeedModifier; #ifdef STATE_DEBUG if (g_pGameCVars->pl_debugInterpolation > 1) { CryWatch("Jumping: vec from player BaseQuat only = (%f, %f, %f)", jumpVec.x, jumpVec.y, jumpVec.z); } #endif if (g_pGameCVars->pl_adjustJumpAngleWithFloorNormal && actorPhysics.groundNormal.len2() > 0.0f) { float vertical = clamp_tpl((actorPhysics.groundNormal.z - 0.25f) / 0.5f, 0.0f, 1.0f); Vec3 modifiedJumpDirection = LERP(actorPhysics.groundNormal, Vec3(0,0,1), vertical); jumpVec = modifiedJumpDirection * jumpVec.len(); } #ifdef STATE_DEBUG if (g_pGameCVars->pl_debugInterpolation > 1) { CryWatch("Jumping (%f, %f, %f)", jumpVec.x, jumpVec.y, jumpVec.z); } #endif } NETINPUT_TRACE(player.GetEntityId(), jumpVec); FinalizeVelocity( player, jumpVec ); if (!player.IsRemote()) { player.HasJumped(player.GetMoveRequest().velocity); } IPhysicalEntity* pPhysEnt = player.GetEntity()->GetPhysics(); if (pPhysEnt != NULL) { SAnimatedCharacterParams params = player.m_pAnimatedCharacter->GetParams(); pe_player_dynamics pd; pd.kAirControl = player.GetAirControl()* g_pGameCVars->pl_jump_control.air_control_scale; pd.kAirResistance = player.GetAirResistance() * g_pGameCVars->pl_jump_control.air_resistance_scale; params.inertia = player.GetInertia() * g_pGameCVars->pl_jump_control.air_inertia_scale; if(player.IsRemote() && (g_pGameCVars->pl_velocityInterpAirControlScale > 0)) { pd.kAirControl = g_pGameCVars->pl_velocityInterpAirControlScale; } pPhysEnt->SetParams(&pd); // Let Animated character handle the inertia player.SetAnimatedCharacterParams(params); } #if 0 if (debugJumping) { Vec3 entityPos = m_player.GetEntity()->GetWorldPos(); gEnv->pRenderer->GetIRenderAuxGeom()->DrawLine(entityPos, ColorB(255,255,255,255), entityPos, ColorB(255,255,0,255), 2.0f); gEnv->pRenderer->GetIRenderAuxGeom()->DrawLine(entityPos+Vec3(0,0,2), ColorB(255,255,255,255), entityPos+Vec3(0,0,2) + desiredVel, ColorB(0,255,0,255), 2.0f); gEnv->pRenderer->GetIRenderAuxGeom()->DrawLine(entityPos, ColorB(255,255,255,255), entityPos + jumpVec, ColorB(0,255,255,255), 2.0f); gEnv->pRenderer->DrawLabel(entityPos - entityRight * 1.0f + Vec3(0,0,3.0f), 1.5f, "Velo[%2.3f = %2.3f, %2.3f, %2.3f]", m_request.velocity.len(), m_request.velocity.x, m_request.velocity.y, m_request.velocity.z); } #endif m_expectedJumpEndHeight = expectedJumpEndHeight; m_bSprintJump = player.IsSprinting(); }
void CPlayerStateJump::GetDesiredVelocity( const Vec3 & move, const CPlayer &player, Vec3* pDesiredVel ) const { // Generate jump velocity. SIMDFConstant(xmfMaxMove, 1.0f); simdf fGroundNormalZ = xmfMaxMove; hwvec3 xmMove = HWVLoadVecUnaligned(&move); if( move.len2() > 0.01f ) { const Matrix34A baseMtx = Matrix34A(player.GetBaseQuat()); Matrix34A baseMtxZ(baseMtx * Matrix33::CreateScale(Vec3(0,0,1))); baseMtxZ.SetTranslation(Vec3(0,0,0)); hwmtx33 xmBaseMtxZ; HWMtx33LoadAligned(xmBaseMtxZ, baseMtxZ); hwmtx33 xmBaseMtxZOpt = HWMtx33GetOptimized(xmBaseMtxZ); hwvec3 xmDesiredVel = HWV3Zero(); if (player.IsRemote()) { xmDesiredVel = xmMove; } else { hwvec3 xmVelocity = HWVLoadVecUnaligned(&player.GetActorPhysics().velocity); SIMDFConstant(xmfZero, 0.0f); SIMDFConstant(xmfDiffMultiplier, 0.3f); SIMDFConstant(xmfMaxDiff, 0.1f); SIMDFConstant(xmfMinMove, 0.5f); SIMDFConstant(xmfOnePointFive, 1.5f); hwvec3 xmMoveFlat = HWVSub(xmMove, HWMtx33RotateVecOpt(xmBaseMtxZOpt, xmMove)); hwvec3 xmCurrVelFlat = HWVSub(xmVelocity, HWMtx33RotateVecOpt(xmBaseMtxZOpt, xmVelocity)); simdf xmfCurrVelSizeSq = HWV3LengthSq(xmCurrVelFlat); hwvec3 xmMoveFlatNormalized = HWV3Normalize(xmMoveFlat); hwvec3 xmCurDirFlatTemp = HWV3Normalize(xmCurrVelFlat); hwvec3 xmCurVelFlatNormalized = HWVSelectSIMDF(xmCurDirFlatTemp, HWV3Zero(), SIMDFLessThanEqual(xmfCurrVelSizeSq, xmfZero)); simdf fDot = HWV3Dot(xmMoveFlatNormalized, xmCurVelFlatNormalized); hwvec3 xmScaledMoveFlat = HWVMultiplySIMDF(xmMoveFlat, SIMDFClamp(fDot, xmfMinMove, xmfMaxMove)); simdf fMoveMult = SIMDFMax(SIMDFMult(SIMDFAbs(fDot), xmfDiffMultiplier), xmfMaxDiff); hwvec3 xmReducedMove = HWVMultiplySIMDF(HWVSub(xmMoveFlat, xmCurrVelFlat), fMoveMult); xmDesiredVel = HWVSelectSIMDF( xmScaledMoveFlat, xmReducedMove, SIMDFLessThan( fDot, xmfZero )); simdf xmfDesiredVelSizeSq = HWV3LengthSq(xmDesiredVel); hwvec3 xmDesiredVelNorm = HWV3Normalize(xmDesiredVel); hwvec3 xmClampedVel = HWVMultiplySIMDF(xmDesiredVelNorm, SIMDFMax( xmfOnePointFive, SIMDFSqrt(xmfCurrVelSizeSq))); xmDesiredVel = HWVSelectSIMDF( xmClampedVel, xmDesiredVel, SIMDFLessThan( xmfDesiredVelSizeSq, xmfCurrVelSizeSq)); } HWVSaveVecUnaligned(pDesiredVel, xmDesiredVel); } }
void CPlayerStateJump::Landed(CPlayer& player, const bool isHeavyWeapon, float fallSpeed) { #ifdef STATE_DEBUG bool remoteControlled = false; IVehicle* pVehicle = player.GetLinkedVehicle(); if(pVehicle) { IVehicleSeat* pVehicleSeat = pVehicle->GetSeatForPassenger(player.GetEntityId()); if(pVehicleSeat && pVehicleSeat->IsRemoteControlled()) { remoteControlled = true; } } CRY_ASSERT_MESSAGE( player.GetLinkedEntity()==NULL || remoteControlled, "Cannot 'land' when you're linked to another entity!" ); #endif const SPlayerStats& stats = player.m_stats; Vec3 playerPosition = player.GetEntity()->GetWorldPos(); IPhysicalEntity *phys = player.GetEntity()->GetPhysics(); IMaterialEffects *mfx = gEnv->pGame->GetIGameFramework()->GetIMaterialEffects(); const SActorPhysics& actorPhysics = player.GetActorPhysics(); int matID = actorPhysics.groundMaterialIdx != -1 ? actorPhysics.groundMaterialIdx : mfx->GetDefaultSurfaceIndex(); const float fHeightofEntity = playerPosition.z; const float worldWaterLevel = player.m_playerStateSwim_WaterTestProxy.GetWaterLevel(); TMFXEffectId effectId = mfx->GetEffectId("bodyfall", matID); if (effectId != InvalidEffectId) { SMFXRunTimeEffectParams params; Vec3 direction = Vec3(0,0,0); if (IMovementController *pMV = player.GetMovementController()) { SMovementState state; pMV->GetMovementState(state); direction = state.aimDirection; } params.pos = playerPosition + direction; //params.soundSemantic = eSoundSemantic_Player_Foley; float landFallParamVal = (float)__fsel( -(fallSpeed - 7.5f), 0.25f, 0.75f); params.AddAudioRtpc("landfall", landFallParamVal); const float speedParamVal = min(fabsf((actorPhysics.velocity.z * 0.1f)), 1.0f); params.AddAudioRtpc("speed", speedParamVal); mfx->ExecuteEffect(effectId, params); } bool heavyLanded = false; IItem* pCurrentItem = player.GetCurrentItem(); CWeapon* pCurrentWeapon = pCurrentItem ? static_cast<CWeapon*>(pCurrentItem->GetIWeapon()) : NULL; if (fallSpeed > 0.0f && player.IsPlayer()) { if(!gEnv->bMultiplayer) { const float verticalSpeed = fabs(fallSpeed); const float speedForHeavyLand = g_pGameCVars->pl_health.fallSpeed_HeavyLand; if ((verticalSpeed >= speedForHeavyLand) && (player.GetPickAndThrowEntity() == 0) && !player.IsDead()) { if ( !isHeavyWeapon ) { if (pCurrentWeapon) { pCurrentWeapon->FumbleGrenade(); pCurrentWeapon->CancelCharge(); } player.StartInteractiveActionByName("HeavyLand", false); } heavyLanded = true; } } } if(player.m_isClient) { if (fallSpeed > 0.0f) { const float fallIntensityMultiplier = stats.wasHit ? g_pGameCVars->pl_fall_intensity_hit_multiplier : g_pGameCVars->pl_fall_intensity_multiplier; const float fallIntensityMax = g_pGameCVars->pl_fall_intensity_max; const float fallTimeMultiplier = g_pGameCVars->pl_fall_time_multiplier; const float fallTimeMax = g_pGameCVars->pl_fall_time_max; const float zoomMultiplayer = (pCurrentWeapon && pCurrentWeapon->IsZoomed()) ? 0.2f : 1.0f; const float direction = ((cry_rand()%2)==0) ? -1.0f : 1.0f; const float intensity = clamp_tpl(fallIntensityMultiplier*fallSpeed*zoomMultiplayer, 0.0f, fallIntensityMax); const float shakeTime = clamp_tpl(fallTimeMultiplier*fallSpeed*zoomMultiplayer, 0.0f, fallTimeMax); const Vec3 rotation = Vec3(-0.5f, 0.15f*direction, 0.05f*direction); if (CScreenEffects* pGameScreenEffects = g_pGame->GetScreenEffects()) { pGameScreenEffects->CamShake(rotation*intensity, Vec3(0, 0, 0), shakeTime, shakeTime, 0.05f, CScreenEffects::eCS_GID_Player); } IForceFeedbackSystem* pForceFeedback = g_pGame->GetIGameFramework()->GetIForceFeedbackSystem(); assert(pForceFeedback); ForceFeedbackFxId fxId = pForceFeedback->GetEffectIdByName("landFF"); pForceFeedback->PlayForceFeedbackEffect(fxId, SForceFeedbackRuntimeParams(intensity, 0.0f)); if(fallSpeed > 7.0f) { player.PlaySound(CPlayer::ESound_Fall_Drop); } CPlayer::EPlayerSounds playerSound = heavyLanded ? CPlayer::ESound_Gear_HeavyLand : CPlayer::ESound_Gear_Land; player.PlaySound(playerSound, true); } CCCPOINT(PlayerMovement_LocalPlayerLanded); } if( gEnv->pAISystem ) { // Notify AI //If silent feet active, ignore here const float noiseSupression = 0.0f; const float fAISoundRadius = (g_pGameCVars->ai_perception.landed_baseRadius + (g_pGameCVars->ai_perception.landed_speedMultiplier * fallSpeed)) * (1.0f - noiseSupression); SAIStimulus stim(AISTIM_SOUND, AISOUND_MOVEMENT_LOUD, player.GetEntityId(), 0, player.GetEntity()->GetWorldPos() + player.GetEyeOffset(), ZERO, fAISoundRadius); gEnv->pAISystem->RegisterStimulus(stim); } // Record 'Land' telemetry stats. CStatsRecordingMgr::TryTrackEvent(&player, eGSE_Land, fallSpeed); if (fallSpeed > 0.0f) { player.CreateScriptEvent( heavyLanded ? "heavylanded" : "landed",stats.fallSpeed); } }
bool CPlayerStateSwim::OnPrePhysicsUpdate( CPlayer& player, const SActorFrameMovementParams& movement, float frameTime ) { const CPlayerStateSwim_WaterTestProxy& waterProxy = player.m_playerStateSwim_WaterTestProxy; CPlayerStateUtil::PhySetFly( player ); const SPlayerStats& stats = player.m_stats; #ifdef STATE_DEBUG const bool debug = (g_pGameCVars->cl_debugSwimming != 0); #endif const Vec3 entityPos = player.GetEntity()->GetWorldPos(); const Quat baseQuat = player.GetBaseQuat(); const Vec3 vRight(baseQuat.GetColumn0()); Vec3 velocity = player.GetActorPhysics().velocity; // Underwater timer, sounds update and surface wave speed. if (waterProxy.IsHeadUnderWater()) { m_headUnderWaterTimer += frameTime; if (m_headUnderWaterTimer <= -0.0f && !m_onSurface ) { player.PlaySound(CPlayer::ESound_DiveIn, true, "speed", velocity.len()); m_headUnderWaterTimer = 0.0f; } player.PlaySound(CPlayer::ESound_Underwater, true); } else { m_headUnderWaterTimer -= frameTime; if (m_headUnderWaterTimer >= 0.0f && (waterProxy.IsHeadComingOutOfWater() || m_onSurface)) { player.PlaySound(CPlayer::ESound_DiveOut, true, "speed", velocity.len()); m_headUnderWaterTimer = 0.0f; } player.PlaySound(CPlayer::ESound_Underwater, false); } m_headUnderWaterTimer = clamp_tpl( m_headUnderWaterTimer, -0.2f, 0.2f ); // Apply water flow velocity to the player Vec3 gravity; pe_params_buoyancy buoyancy[s_maxWaterVolumesToConsider]; if (int count = gEnv->pPhysicalWorld->CheckAreas(entityPos, gravity, &buoyancy[0], s_maxWaterVolumesToConsider)) { for(int i = 0; i < count && i < s_maxWaterVolumesToConsider; ++i) { // 0 is water if( buoyancy[i].iMedium == 0 ) { velocity += (buoyancy[i].waterFlow * frameTime); } } } // Calculate desired acceleration (user input) Vec3 desiredWorldVelocity(ZERO); Vec3 acceleration(ZERO); { Vec3 desiredLocalNormalizedVelocity(ZERO); Vec3 desiredLocalVelocity(ZERO); const Quat viewQuat = player.GetViewQuat(); const float backwardMultiplier = (float)__fsel(movement.desiredVelocity.y, 1.0f, g_pGameCVars->pl_swimBackSpeedMul); desiredLocalNormalizedVelocity.x = movement.desiredVelocity.x * g_pGameCVars->pl_swimSideSpeedMul; desiredLocalNormalizedVelocity.y = movement.desiredVelocity.y * backwardMultiplier; float sprintMultiplier = 1.0f; if ((player.IsSprinting()) && !player.IsCinematicFlagActive(SPlayerStats::eCinematicFlag_RestrictMovement)) { sprintMultiplier = GetSwimParams().m_swimSprintSpeedMul; // Higher speed multiplier when sprinting while looking up const float upFraction = clamp_tpl(viewQuat.GetFwdZ(), 0.0f, 1.0f); sprintMultiplier *= LERP(1.0f, GetSwimParams().m_swimUpSprintSpeedMul, upFraction); } const float baseSpeed = player.GetStanceMaxSpeed(STANCE_SWIM); desiredLocalVelocity.x = desiredLocalNormalizedVelocity.x * sprintMultiplier * baseSpeed; desiredLocalVelocity.y = desiredLocalNormalizedVelocity.y * sprintMultiplier * baseSpeed; desiredLocalVelocity.z = desiredLocalNormalizedVelocity.z * g_pGameCVars->pl_swimVertSpeedMul * baseSpeed; // The desired movement is applied in view-space, not in entity-space, since entity does not necessarily pitch while swimming. desiredWorldVelocity += viewQuat.GetColumn0() * desiredLocalVelocity.x; desiredWorldVelocity += viewQuat.GetColumn1() * desiredLocalVelocity.y; // though, apply up/down in world space. desiredWorldVelocity.z += desiredLocalVelocity.z; #ifdef STATE_DEBUG if (debug) { gEnv->pRenderer->DrawLabel(entityPos - vRight * 1.5f + Vec3(0,0,0.8f), 1.5f, "BaseSpeed %1.3f", baseSpeed); gEnv->pRenderer->DrawLabel(entityPos - vRight * 1.5f + Vec3(0,0,1.0f), 1.5f, "SprintMul %1.2f", sprintMultiplier); gEnv->pRenderer->DrawLabel(entityPos - vRight * 1.5f + Vec3(0,0,0.6f), 1.5f, "MoveN[%1.3f, %1.3f, %1.3f]", desiredLocalNormalizedVelocity.x, desiredLocalNormalizedVelocity.y, desiredLocalNormalizedVelocity.z); gEnv->pRenderer->DrawLabel(entityPos - vRight * 1.5f + Vec3(0,0,0.5f), 1.5f, "VeloL[%1.3f, %1.3f, %1.3f]", desiredLocalVelocity.x, desiredLocalVelocity.y, desiredLocalVelocity.z); gEnv->pRenderer->DrawLabel(entityPos - vRight * 1.5f + Vec3(0,0,0.4f), 1.5f, "VeloW[%1.3f, %1.3f, %1.3f]", desiredWorldVelocity.x, desiredWorldVelocity.y, desiredWorldVelocity.z); } #endif //Remove a bit of control when entering the water const float userControlFraction = (float)__fsel(0.3f - waterProxy.GetSwimmingTimer(), 0.2f, 1.0f); acceleration += desiredWorldVelocity * userControlFraction; } // Apply acceleration (frame-rate independent) const float accelerateDelayInv = 3.333f; velocity += acceleration * (frameTime * accelerateDelayInv); #ifdef STATE_DEBUG if( debug ) { gEnv->pRenderer->DrawLabel(entityPos - vRight * 1.5f - Vec3(0,0,0.2f), 1.5f, " Axx[%1.3f, %1.3f, %1.3f]", acceleration.x, acceleration.y, acceleration.z); } #endif //-------------------- const float relativeWaterLevel = waterProxy.GetRelativeWaterLevel() + 0.1f; const float surfaceDistanceFraction = clamp_tpl(fabsf(relativeWaterLevel), 0.0f, 1.0f); float surfaceProximityInfluence = 1.0f - surfaceDistanceFraction; const float verticalVelocityFraction = clamp_tpl((fabsf(desiredWorldVelocity.z) - 0.3f) * 2.5f, 0.0f, 1.0f); surfaceProximityInfluence = surfaceProximityInfluence * (1.0f - verticalVelocityFraction); // Apply velocity dampening (frame-rate independent) Vec3 damping(ZERO); const float zSpeedPreDamping = velocity.z; { damping.x = fabsf(velocity.x); damping.y = fabsf(velocity.y); // Vertical damping is special, to allow jumping out of water with higher speed, // and also not sink too deep when falling down ito the water after jump or such. float zDamp = 1.0f + (6.0f * clamp_tpl((-velocity.z - 1.0f) * 0.333f, 0.0f, 1.0f)); zDamp *= 1.0f - surfaceProximityInfluence; damping.z = fabsf(velocity.z) * zDamp; const float stopDelayInv = 3.333f; damping *= (frameTime * stopDelayInv); velocity.x = (float)__fsel((fabsf(velocity.x) - damping.x), (velocity.x - fsgnf(velocity.x) * damping.x), 0.0f); velocity.y = (float)__fsel((fabsf(velocity.y) - damping.y), (velocity.y - fsgnf(velocity.y) * damping.y), 0.0f); velocity.z = (float)__fsel((fabsf(velocity.z) - damping.z), (velocity.z - fsgnf(velocity.z) * damping.z), 0.0f); //Make sure you can not swim above the surface if ((relativeWaterLevel >= 0.0f) && (velocity.z > 0.0f)) { velocity.z = 0.0f; } } // Decide if we're on the surface and therefore need to be kept there.. if( relativeWaterLevel > -0.2f && relativeWaterLevel < 1.0f && fabs_tpl( zSpeedPreDamping ) < 0.5f ) { if( !waterProxy.IsHeadUnderWater() ) { m_onSurface = true; } } else { // we only leave the surface if the player moves, otherwise we try and keep the // player on the surface, even if they currently arent m_onSurface = false; } // Calculate and apply surface movement to the player. float speedDelta = 0.0f; if( m_onSurface ) { const float newWaterLevel = waterProxy.GetWaterLevel(); const float waterLevelDelta = clamp_tpl(newWaterLevel - m_lastWaterLevel, -1.0f, 1.0f ); const float newCheckedTime = waterProxy.GetWaterLevelTimeUpdated(); const float timeDelta = newCheckedTime - m_lastWaterLevelTime; if( timeDelta > FLT_EPSILON ) { speedDelta = waterLevelDelta/timeDelta; velocity.z += speedDelta; } m_lastWaterLevel = newWaterLevel; m_lastWaterLevelTime = newCheckedTime; } // Set request type and velocity player.GetMoveRequest().type = eCMT_Fly; player.GetMoveRequest().velocity = velocity; #ifdef STATE_DEBUG // DEBUG VELOCITY if (debug) { gEnv->pRenderer->DrawLabel(entityPos - vRight * 1.5f - Vec3(0,0,0.0f), 1.5f, "Velo[%1.3f, %1.3f, %1.3f]", velocity.x, velocity.y, velocity.z); gEnv->pRenderer->DrawLabel(entityPos - vRight * 1.5f - Vec3(0,0,0.4f), 1.5f, "Damp[%1.3f, %1.3f, %1.3f]", damping.x, damping.y, damping.z); gEnv->pRenderer->DrawLabel(entityPos - vRight * 1.5f - Vec3(0,0,0.6f), 1.5f, "FrameTime %1.4f", frameTime); gEnv->pRenderer->DrawLabel(entityPos - vRight * 1.5f + Vec3(0,0,0.3f), 1.5f, "DeltaSpeed[%1.3f]", speedDelta ); //if (bNewSwimJumping) //gEnv->pRenderer->DrawLabel(entityPos - vRight * 0.15f + Vec3(0,0,0.6f), 2.0f, "JUMP"); } #endif return true; }