//-------------------------------------------------------------------- bool CWeapon::PreActionAttack(bool startFire) { // Melee while pressing SHIFT for SP CPlayer *pPlayer = static_cast<CPlayer*>(GetOwnerActor()); if(!pPlayer) return false; //if(gEnv->bMultiplayer) { if(startFire && pPlayer->IsSprinting()) { //Stop sprinting, start firing SPlayerStats *pStats = static_cast<SPlayerStats*>(pPlayer->GetActorStats()); if(pStats) { pStats->bSprinting = false; pStats->bIgnoreSprinting = true; } } else if(!startFire) { //Stop firing, continue sprinting SPlayerStats *pStats = static_cast<SPlayerStats*>(pPlayer->GetActorStats()); if(pStats) pStats->bIgnoreSprinting = false; } } return false; }
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 CWeapon::OnActionSprint(EntityId actorId, const ActionId& actionId, int activationMode, float value) { //If zoomed, you can not sprint (in SP only) if ((gEnv->bMultiplayer == false) && IsZoomed()) return false; CActor* pOwnerActor = GetOwnerActor(); CPlayer *pPlayer = 0; SPlayerStats *pStats = 0; if (pOwnerActor && pOwnerActor->IsPlayer()) { pPlayer = static_cast<CPlayer*>(pOwnerActor); pStats = static_cast<SPlayerStats*>(pPlayer->GetActorStats()); } if (activationMode == eAAM_OnPress) { if (pPlayer) { if (pStats && pStats->bIgnoreSprinting && !m_isFiring && m_delayedFireActionTimeOut <= 0.f && m_delayedMeleeActionTimeOut <= 0.f) pStats->bIgnoreSprinting = false; } } return false; }
bool CWeapon::CheckSprint() { CPlayer *pPlayer = static_cast<CPlayer*>(GetOwnerActor()); if(!pPlayer || pPlayer->IsSliding()) return false; SPlayerStats *pStats = static_cast<SPlayerStats*>(pPlayer->GetActorStats()); assert(pStats); if(pPlayer->IsSprinting()) { pStats->bIgnoreSprinting = true; float delayScale = pPlayer->GetModifiableValues().GetValue(kPMV_WeaponFireFromSprintTimeScale); m_delayedFireActionTimeOut = GetParams().sprintToFireDelay * delayScale; return true; } else if (m_delayedFireActionTimeOut > 0.f) { return true; } return false; }
void CPlayerStateGround::OnEnter( CPlayer& player ) { player.GetActorStats()->inAir = 0.0f; // Ensure inertia is set! CPlayerStateUtil::RestorePlayerPhysics( player ); }
void CCameraTracking::UpdateAutoFollow(const SViewParams &viewParams, const CPlayer &hero) { if(g_pGameCVars->cl_cam_auto_follow_rate > 0.0f) //auto-tracking { //if there is an obstacle nearby, don't try to rotate into it float lastObstacleDist = (m_vLastObstaclePos - viewParams.position).len(); if(lastObstacleDist < g_fAutoTrackObstacleDistance) return; if(hero.GetActorStats()->speedFlat < g_pGameCVars->cl_cam_auto_follow_movement_speed) { //only rotate when player moves m_fAutoRotateSpeed = 0.0f; return; } //get camera direction Vec3 camDir = -m_pCamRayScan->GetRayDir(eRAY_CENTER); camDir.z = 0.0f; camDir.Normalize(); //get Player direction Vec3 heroDirection = (hero.GetAnimatedCharacter()->GetAnimLocation().q * FORWARD_DIRECTION); //compute angle between directions float dt = camDir.Dot(heroDirection); dt = clamp(dt, -1.0f, 1.0f); float angle = cry_acosf(dt); //check angle being bigger than threshold if(angle > g_pGameCVars->cl_cam_auto_follow_threshold) { float moveSpeed = max(0.002f, gf_PI*m_fFrameTime*g_pGameCVars->cl_cam_auto_follow_rate); m_fAutoRotateSpeed = InterpolateTo(max(0.002f, m_fAutoRotateSpeed), moveSpeed, 0.2f); //compute rotation direction by taking height part of cross-prod float dirVal = camDir.x * heroDirection.y - camDir.y * heroDirection.x; CCameraInputHelper *const pCamHelper = hero.GetCameraInputHelper(); CRY_ASSERT(pCamHelper); if(dirVal > 0) //rotate right pCamHelper->SetTrackingDelta(-m_fAutoRotateSpeed, 0.0f); else //rotate left pCamHelper->SetTrackingDelta(m_fAutoRotateSpeed, 0.0f); } else m_fAutoRotateSpeed = 0.0f; } }
void CPlayerStateSwim_WaterTestProxy::DebugDraw(const CPlayer& player, const Vec3& referencePosition) { // DEBUG RENDERING const SPlayerStats& stats = *player.GetActorStats(); const bool debugSwimming = (g_pGameCVars->cl_debugSwimming != 0); if (debugSwimming && (m_playerWaterLevel > -10.0f) && (m_playerWaterLevel < 10.0f)) { const Vec3 surfacePosition(referencePosition.x, referencePosition.y, m_waterLevel); const Vec3 vRight(player.GetBaseQuat().GetColumn0()); const static ColorF referenceColor(1,1,1,1); const static ColorF surfaceColor1(0,0.5f,1,1); const static ColorF surfaceColor0(0,0,0.5f,0); const static ColorF bottomColor(0,0.5f,0,1); gEnv->pRenderer->GetIRenderAuxGeom()->DrawSphere(referencePosition, 0.1f, referenceColor); gEnv->pRenderer->GetIRenderAuxGeom()->DrawLine(referencePosition, surfaceColor1, surfacePosition, surfaceColor1, 2.0f); gEnv->pRenderer->GetIRenderAuxGeom()->DrawSphere(surfacePosition, 0.2f, surfaceColor1); gEnv->pRenderer->DrawLabel(referencePosition + vRight * 0.5f, 1.5f, "WaterLevel %3.2f (Head underwater: %d)", m_playerWaterLevel, m_headUnderwater ? 1 : 0); const static int lines = 16; const static float radius0 = 0.5f; const static float radius1 = 1.0f; const static float radius2 = 2.0f; for (int i = 0; i < lines; ++i) { float radians = ((float)i / (float)lines) * gf_PI2; Vec3 offset0(radius0 * cos_tpl(radians), radius0 * sin_tpl(radians), 0); Vec3 offset1(radius1 * cos_tpl(radians), radius1 * sin_tpl(radians), 0); Vec3 offset2(radius2 * cos_tpl(radians), radius2 * sin_tpl(radians), 0); gEnv->pRenderer->GetIRenderAuxGeom()->DrawLine(surfacePosition+offset0, surfaceColor0, surfacePosition+offset1, surfaceColor1, 2.0f); gEnv->pRenderer->GetIRenderAuxGeom()->DrawLine(surfacePosition+offset1, surfaceColor1, surfacePosition+offset2, surfaceColor0, 2.0f); } if (m_bottomLevel > 0.0f) { const Vec3 bottomPosition(referencePosition.x, referencePosition.y, m_bottomLevel); gEnv->pRenderer->GetIRenderAuxGeom()->DrawLine(referencePosition, bottomColor, bottomPosition, bottomColor, 2.0f); gEnv->pRenderer->GetIRenderAuxGeom()->DrawSphere(bottomPosition, 0.2f, bottomColor); gEnv->pRenderer->DrawLabel(bottomPosition + Vec3(0,0,0.5f) - vRight * 0.5f, 1.5f, "BottomDepth %3.3f", m_waterLevel - m_bottomLevel); } } }
//-------------------------------------------------------------------- bool CWeapon::PreMeleeAttack() { CPlayer *pPlayer = static_cast<CPlayer*>(GetOwnerActor()); if(!pPlayer) return false; SPlayerStats* pStats = static_cast<SPlayerStats*>(pPlayer->GetActorStats()); assert(pStats); if (!pPlayer->IsSprinting()) return true; pStats->bIgnoreSprinting = true; float delayScale = pPlayer->GetModifiableValues().GetValue(kPMV_WeaponFireFromSprintTimeScale); m_delayedMeleeActionTimeOut = GetParams().sprintToMeleeDelay * delayScale; return (m_delayedMeleeActionTimeOut <= 0.f); }
bool CPlayerStateUtil::IsMovingForward( const CPlayer& player, const SActorFrameMovementParams& movement ) { const float fSpeedFlatSelector = player.GetActorStats()->speedFlat - 0.1f; bool movingForward = (fSpeedFlatSelector > 0.1f); if (!gEnv->bMultiplayer || player.IsClient()) { // IsMovingForward() doesn't return particularly reliable results for client players in MP, I think because of interpolation innacuracies on the Y velocity. this was causing client players to // incorrectly report that they're no longer moving forwards, whereas all that had happened was they had stopped sprinting - and this in turn was messing up the MP sprint stamina regeneration delays. // client players have also been seen to stop sprinting due to turning lightly, whereas on their local machine they were still sprinting fine... // so i've tried changing it so IsMovingForward() is only taken into account on local clients (and in SP!). [tlh] const float thresholdSinAngle = sin_tpl((3.141592f * 0.5f) - DEG2RAD(g_pGameCVars->pl_power_sprint.foward_angle)); const float currentSinAngle = movement.desiredVelocity.y; movingForward = movingForward && (currentSinAngle > thresholdSinAngle); } return movingForward; }
void CPlayerStateUtil::UpdatePlayerPhysicsStats( CPlayer& player, SActorPhysics& actorPhysics, float frameTime ) { const int currentFrameID = gEnv->pRenderer->GetFrameID(); if( actorPhysics.lastFrameUpdate < currentFrameID ) { pe_status_living livStat; if( !CPlayerStateUtil::GetPhysicsLivingStat( player, &livStat ) ) { return; } SPlayerStats& stats = *player.GetActorStats(); const Vec3 newVelocity = livStat.vel-livStat.velGround; actorPhysics.velocityDelta = newVelocity - actorPhysics.velocity; actorPhysics.velocity = newVelocity; actorPhysics.velocityUnconstrainedLast = actorPhysics.velocityUnconstrained; actorPhysics.velocityUnconstrained = livStat.velUnconstrained; actorPhysics.flags.SetFlags( SActorPhysics::EActorPhysicsFlags_WasFlying, actorPhysics.flags.AreAnyFlagsActive(SActorPhysics::EActorPhysicsFlags_Flying) ); actorPhysics.flags.SetFlags( SActorPhysics::EActorPhysicsFlags_Flying, livStat.bFlying > 0 ); actorPhysics.flags.SetFlags( SActorPhysics::EActorPhysicsFlags_Stuck, livStat.bStuck > 0 ); Vec3 flatVel(player.m_pPlayerRotation->GetBaseQuat().GetInverted()*newVelocity); flatVel.z = 0; stats.speedFlat = flatVel.len(); if(player.IsInAir()) { stats.maxAirSpeed = max(stats.maxAirSpeed, newVelocity.GetLengthFast()); } else { stats.maxAirSpeed = 0.f; } float fSpeedFlatSelector = stats.speedFlat - 0.1f; const float groundNormalBlend = clamp(frameTime * 6.666f, 0.0f, 1.0f); actorPhysics.groundNormal = LERP(actorPhysics.groundNormal, livStat.groundSlope, groundNormalBlend); if (livStat.groundSurfaceIdxAux > 0) actorPhysics.groundMaterialIdx = livStat.groundSurfaceIdxAux; else actorPhysics.groundMaterialIdx = livStat.groundSurfaceIdx; actorPhysics.groundHeight = livStat.groundHeight; EntityId newGroundColliderId = 0; if (livStat.pGroundCollider) { IEntity* pEntity = gEnv->pEntitySystem->GetEntityFromPhysics(livStat.pGroundCollider); newGroundColliderId = pEntity ? pEntity->GetId() : 0; } if( newGroundColliderId != actorPhysics.groundColliderId ) { if( actorPhysics.groundColliderId ) { if( IGameObject* pGameObject = gEnv->pGame->GetIGameFramework()->GetGameObject( actorPhysics.groundColliderId ) ) { SGameObjectEvent event( eGFE_StoodOnChange, eGOEF_ToExtensions ); event.ptr = &player; event.paramAsBool = false; pGameObject->SendEvent(event); } } if( newGroundColliderId ) { if( IGameObject* pGameObject = gEnv->pGame->GetIGameFramework()->GetGameObject( newGroundColliderId ) ) { SGameObjectEvent event( eGFE_StoodOnChange, eGOEF_ToExtensions ); event.ptr = &player; event.paramAsBool = true; pGameObject->SendEvent(event); } } actorPhysics.groundColliderId = newGroundColliderId; } IPhysicalEntity *pPhysEnt = player.GetEntity()->GetPhysics(); if( pPhysEnt ) { pe_status_dynamics dynStat; pPhysEnt->GetStatus(&dynStat); actorPhysics.angVelocity = dynStat.w; actorPhysics.mass = dynStat.mass; pe_player_dynamics simPar; if (pPhysEnt->GetParams(&simPar) != 0) { actorPhysics.gravity = simPar.gravity; } } actorPhysics.lastFrameUpdate = currentFrameID; #ifdef PLAYER_MOVEMENT_DEBUG_ENABLED player.GetMovementDebug().DebugGraph_AddValue("PhysVelo", livStat.vel.GetLength()); player.GetMovementDebug().DebugGraph_AddValue("PhysVeloX", livStat.vel.x); player.GetMovementDebug().DebugGraph_AddValue("PhysVeloY", livStat.vel.y); player.GetMovementDebug().DebugGraph_AddValue("PhysVeloZ", livStat.vel.z); player.GetMovementDebug().DebugGraph_AddValue("PhysVeloUn", livStat.velUnconstrained.GetLength()); player.GetMovementDebug().DebugGraph_AddValue("PhysVeloUnX", livStat.velUnconstrained.x); player.GetMovementDebug().DebugGraph_AddValue("PhysVeloUnY", livStat.velUnconstrained.y); player.GetMovementDebug().DebugGraph_AddValue("PhysVeloUnZ", livStat.velUnconstrained.z); player.GetMovementDebug().DebugGraph_AddValue("PhysVelReq", livStat.velRequested.GetLength()); player.GetMovementDebug().DebugGraph_AddValue("PhysVelReqX", livStat.velRequested.x); player.GetMovementDebug().DebugGraph_AddValue("PhysVelReqY", livStat.velRequested.y); player.GetMovementDebug().DebugGraph_AddValue("PhysVelReqZ", livStat.velRequested.z); player.GetMovementDebug().LogVelocityStats(player.GetEntity(), livStat, stats.downwardsImpactVelocity, stats.fallSpeed); #endif } }
//-------------------------------------- void CThrow::DoDrop() { m_pWeapon->HideItem(true); if (m_throwableId) { IEntity *pEntity = gEnv->pEntitySystem->GetEntity(m_throwableId); if (pEntity) { IPhysicalEntity *pPE = pEntity->GetPhysics(); if (pPE && (pPE->GetType() == PE_RIGID || pPE->GetType() == PE_PARTICLE)) { Vec3 hit = GetProbableHit(WEAPON_HIT_RANGE); Vec3 pos = GetFiringPos(hit); CActor *pActor = m_pWeapon->GetOwnerActor(); IMovementController *pMC = pActor ? pActor->GetMovementController() : 0; if (pMC) { SMovementState info; pMC->GetMovementState(info); float speed = 2.5f; CPlayer *pPlayer = static_cast<CPlayer *>(m_pWeapon->GetOwnerActor()); if(info.aimDirection.z < -0.1f) { if(pPlayer) { if(SPlayerStats *pStats = static_cast<SPlayerStats *>(pPlayer->GetActorStats())) { if(pStats->grabbedHeavyEntity) { speed = 4.0f; } } } } if(CheckForIntersections(pPE, info.eyeDirection)) { Matrix34 newTM = pEntity->GetWorldTM(); newTM.SetTranslation(newTM.GetTranslation() - (info.eyeDirection * 0.4f)); pEntity->SetWorldTM(newTM, ENTITY_XFORM_POS); pe_action_set_velocity asv; asv.v = (-info.eyeDirection * speed); pPE->Action(&asv); } else { pe_action_set_velocity asv; asv.v = (info.eyeDirection * speed); pPE->Action(&asv); } SEntityEvent entityEvent; entityEvent.event = ENTITY_EVENT_PICKUP; entityEvent.nParam[0] = 0; if (pPlayer) { entityEvent.nParam[1] = pPlayer->GetEntityId(); } entityEvent.fParam[0] = speed; pEntity->SendEvent( entityEvent ); } } } if (m_throwableAction) { m_throwableAction->execute(m_pWeapon); } } }
bool CCameraTracking::IdentifyObstacle(const Vec3 &vCamDir, const CPlayer &hero) { //check player direction Vec3 newDir = -hero.GetEntity()->GetForwardDir(); newDir.z += vCamDir.z; newDir.normalize(); //compute rotation speed const float fHeroSpeedModifier = clamp(hero.GetActorStats()->speedFlat / 4.0f, 0.3f, 1.0f); const float fNewSpeed = g_pGameCVars->cl_cam_tracking_rotation_speed * m_fFrameTime * fHeroSpeedModifier; m_fSpeed = InterpolateTo(m_fSpeed, fNewSpeed, (fNewSpeed>m_fSpeed)?0.1f:0.3f); //m_fSpeed = (g_fInterpolationRate * m_fSpeed + speed) * g_fInterpolationWeight; //get ray data from camera ray tests ray_hit *pRayHit = m_pCamRayScan->GetHit(eRAY_TOP_RIGHT); if(!pRayHit || pRayHit->dist == 0.0f) pRayHit = m_pCamRayScan->GetHit(eRAY_BOTTOM_RIGHT); bool bHitsRight = (pRayHit && pRayHit->dist > 0.0f); Vec3 dirRight = (pRayHit)?-(m_pCamRayScan->GetRayDir(eRAY_TOP_RIGHT)):Vec3(ZERO); //ray data left side pRayHit = m_pCamRayScan->GetHit(eRAY_TOP_LEFT); if(!pRayHit || pRayHit->dist == 0.0f) pRayHit = m_pCamRayScan->GetHit(eRAY_BOTTOM_LEFT); bool bHitsLeft = (pRayHit && pRayHit->dist > 0.0f); Vec3 dirLeft = (pRayHit)?-(m_pCamRayScan->GetRayDir(eRAY_TOP_LEFT)):Vec3(ZERO); //left or right if(bHitsRight ^ bHitsLeft) { //find rotation direction if(!bHitsRight && !bHitsLeft) { if(m_eLastDirYaw == eTD_LEFT) //continue last direction newDir = dirLeft; else newDir = dirRight; } else if(!bHitsRight) { m_eLastDirYaw = eTD_RIGHT; newDir = dirRight; } else { m_eLastDirYaw = eTD_LEFT; newDir = dirLeft; } //compute yaw/pitch for target position float newYaw = 0.0f; float newPitch = 0.0f; float newDist = 0.0f; CartesianToSpherical(newDir * m_curCamOrientation.m_fDist, newYaw, newPitch, newDist); newYaw += gf_PI; //now interpolate to target //compute delta yaw m_fYawDelta = (newYaw - m_curCamOrientation.m_fYaw) * m_fSpeed; if(m_eLastDirYaw == eTD_RIGHT && m_fYawDelta < 0.0f || m_eLastDirYaw == eTD_LEFT && m_fYawDelta > 0.0f) m_fYawDelta *= -1.0f; } //compute top/bottom rotation //ray data top side pRayHit = m_pCamRayScan->GetHit(eRAY_TOP_CENTER); bool bHitsTop = (pRayHit && pRayHit->dist > 0.0f)?true:false; Vec3 vDirTop = (pRayHit)?-(m_pCamRayScan->GetRayDir(eRAY_TOP_CENTER)):Vec3(ZERO); //ray data bottom side pRayHit = m_pCamRayScan->GetHit(eRAY_BOTTOM_CENTER); bool bHitsBottom = (pRayHit && pRayHit->dist > 0.0f)?true:false; Vec3 vDirBottom = (pRayHit)?-(m_pCamRayScan->GetRayDir(eRAY_BOTTOM_CENTER)):Vec3(ZERO); //top or bottom (if not left or right) if(g_pGameCVars->cl_cam_tracking_allow_pitch && (bHitsTop ^ bHitsBottom) && !(bHitsRight ^ bHitsLeft)) { //find rotation direction if(!bHitsTop && !bHitsBottom) { if(m_eLastDirPitch == eTD_TOP) //continue last direction newDir = vDirTop; else newDir = vDirBottom; } else if(!bHitsBottom) { m_eLastDirPitch = eTD_BOTTOM; newDir = vDirBottom; } else { m_eLastDirPitch = eTD_TOP; newDir = vDirTop; } //compute yaw/pitch for target position float newYaw = 0.0f; float newPitch = 0.0f; float newDist = 0.0f; //newdist (raydist) will be ignored CartesianToSpherical(newDir, newYaw, newPitch, newDist); //compute delta pitch m_fPitchDelta = (newPitch - m_curCamOrientation.m_fPitch) * m_fSpeed * 10.0f; } //if all rays hit - don't bother! //this is a termination condition when the camera is pulled through geometry if(bHitsLeft & bHitsRight & bHitsBottom & bHitsTop) { if(m_bViewCovered) { //if obstacle behind player //if(g_rHit.dist > 0.0f) //this is a strange fix, but it's working better and is much cheaper than a raycast if(fabsf(m_fYawDelta) < 0.01f && fabsf(m_fPitchDelta) > 0.001f) return false; } m_bViewCovered = true; } else m_bViewCovered = false; return true; }
//------------------------------------------------------------------------ // returns true if entity is killed, false if it is not bool CGameRulesMPDamageHandling::SvOnHit( const HitInfo &hitInfo ) { const HitTypeInfo * pHitTypeInfo = m_pGameRules->GetHitTypeInfo(hitInfo.type); #if !defined(_RELEASE) if(!pHitTypeInfo) CryFatalError("By ::SvOnHit() all hit info should have a hit type that is valid and registered in the GameRules. This isn't true of type %d!", hitInfo.type); #endif SDamageHandling damageHandling(&hitInfo, 1.0f); float damage = hitInfo.damage; IActorSystem* pActorSystem = g_pGame->GetIGameFramework()->GetIActorSystem(); CActor *pTargetActor = static_cast<CActor*>(pActorSystem->GetActor(hitInfo.targetId)); CActor *pShooterActor = static_cast<CActor*>(pActorSystem->GetActor(hitInfo.shooterId)); CPlayer* pShooterPlayer = (pShooterActor && pShooterActor->IsPlayer()) ? static_cast<CPlayer*>(pShooterActor) : NULL ; bool isPlayer = pTargetActor != NULL && pTargetActor->IsPlayer(); #ifndef _RELEASE //--- Fix to allow the damage handling to work for these entity classes in the same way as for Players static IEntityClass* sDamEntClass = gEnv->pEntitySystem->GetClassRegistry()->FindClass("DamageTestEnt"); isPlayer |= pTargetActor != NULL && pTargetActor->GetEntity()->GetClass() == sDamEntClass; #endif CPlayer* pPlayer = isPlayer ? static_cast<CPlayer*>(pTargetActor) : NULL; const bool isMelee = ((pHitTypeInfo->m_flags & CGameRules::EHitTypeFlag::IsMeleeAttack) != 0); const bool checkHeadshots = ((pHitTypeInfo->m_flags & CGameRules::EHitTypeFlag::IgnoreHeadshots) == 0); bool bIsHeadShot = false; if(pPlayer && hitInfo.partId >= 0 && checkHeadshots) { bIsHeadShot = pPlayer->IsHeadShot(hitInfo); if (!bIsHeadShot && g_pGameCVars->g_mpHeadshotsOnly) { damage = 0.f; } } //If the player has died more than kTimeToAllowKillsAfterDeath seconds ago, we disallow any damage they apply, unless the hit type is flagged as allowing it. static const float kTimeToAllowKillsAfterDeath = 0.05f; if(pTargetActor && pShooterPlayer && !hitInfo.hitViaProxy && ((pHitTypeInfo->m_flags & CGameRules::EHitTypeFlag::AllowPostDeathDamage) == 0) && pShooterActor->IsDead() && (gEnv->pTimer->GetFrameStartTime().GetSeconds() - pShooterPlayer->GetDeathTime()) > kTimeToAllowKillsAfterDeath) { damage = 0.0f; } IGameRulesStateModule *stateModule = m_pGameRules->GetStateModule(); IGameRulesRoundsModule* pRoundsModule = m_pGameRules->GetRoundsModule(); if ( (stateModule != NULL && (stateModule->GetGameState() == IGameRulesStateModule::EGRS_PostGame)) || (pRoundsModule!= NULL && !pRoundsModule->IsInProgress() )) { // No damage allowed once the game has ended, except in cases where it would cause graphical glitches if (hitInfo.type != CGameRules::EHitType::PunishFall) { damage = 0.0f; } } IEntity *pTarget = gEnv->pEntitySystem->GetEntity(hitInfo.targetId); #if defined(SERVER_CHECKS) if(damage != 0.0f) { int nNewCheckCounter = m_checkCounter + 1; if (CItem *pItem = static_cast<CItem *>(g_pGame->GetIGameFramework()->GetIItemSystem()->GetItem(hitInfo.weaponId))) { if(CWeapon * pWeapon = static_cast<CWeapon*>(pItem->GetIWeapon())) { int nFireModeFromShotId = GetFireModeFromShotId(hitInfo.shotId); static const int kCheckFreq = 7; if(pShooterActor && nNewCheckCounter == kCheckFreq) { float fDamageAtXMeters = 0.0f; float fDistance2D, fDistanceMax, fNetLagSeconds; CServerCheatMonitor::GetHitValidationInfo(*pShooterActor, hitInfo, fDistance2D, fDistanceMax, fNetLagSeconds); bool bDoDamageValidation = false; if(isMelee) { if(CMelee * pMelee = pWeapon->GetMelee()) { //This check can't be used for everything because the default firemode returns '0.f' for range and only CMelee extends it // the horizontal player speed is x 2.0f as the players could have potentially immediately turned and run away from each other float fMeleeRangeError = fDistance2D - (pMelee->GetRange() + (CServerCheatMonitor::kMaxHorizontalPlayerSpeed * fNetLagSeconds * 2.0f)); if(fMeleeRangeError > 0.1f) { g_pGame->GetAntiCheatManager()->FlagActivity(eCT_MeleeRange, pShooterActor->GetChannelId(), fMeleeRangeError); } fDamageAtXMeters = pMelee->GetDamageAmountAtXMeters(fDistance2D); bDoDamageValidation = true; } } else { ////////////////////////////////////////////////////////////////////// // Verify that the hit is from a valid shot DoShotValidation(hitInfo, pHitTypeInfo, pShooterActor); ////////////////////////////////////////////////////////////////////// CFireMode * pFireMode = static_cast<CFireMode*>(pWeapon->GetFireMode(nFireModeFromShotId)); if(pFireMode) { const SFireModeParams * pParams = pFireMode->GetShared(); char projectileClassName[128]; g_pGame->GetIGameFramework()->GetNetworkSafeClassName(projectileClassName, sizeof(projectileClassName), hitInfo.projectileClassId); IEntityClass * pProjectileClass = gEnv->pEntitySystem->GetClassRegistry()->FindClass(projectileClassName); if((pProjectileClass && (pProjectileClass == pParams->fireparams.ammo_type_class || pProjectileClass == pParams->plantparams.ammo_type_class))) { float fBulletSpeed = 100.f; const SAmmoParams * pAmmoParams = g_pGame->GetWeaponSystem()->GetAmmoParams(pFireMode->GetAmmoType()); if(pAmmoParams) { fBulletSpeed = pAmmoParams->speed; } //Might have been closer when the shot was fired const float fMaxTimeSinceShot = ((fDistanceMax / fBulletSpeed) * 2.0f) + fNetLagSeconds; //Should be less than this. Laaaaarge fudge factor float fDistance_Conservative = fDistance2D - (fMaxTimeSinceShot * CServerCheatMonitor::kMaxHorizontalPlayerSpeed); fDamageAtXMeters = pFireMode->GetDamageAmountAtXMeters(fDistance_Conservative); } } else if(pHitTypeInfo->m_flags & CGameRules::EHitTypeFlag::ValidationRequired) { CryStackStringT<char, 256> invalidFireModeMessage; invalidFireModeMessage.Format("Invalid fire mode, weapon: '%s', firemode: %d", pWeapon->GetEntity()->GetName(), nFireModeFromShotId); g_pGame->GetAntiCheatManager()->FlagActivity(eCT_ValidHitInfo, pShooterActor->GetChannelId(), invalidFireModeMessage.c_str()); } } float fDamageFromWeapon = hitInfo.damage; if(fDamageFromWeapon > fDamageAtXMeters && fDamageAtXMeters > 0.0f) { //Log the ratio of actual damage to expected damage, e.g. 1.2 x expected CryStackStringT<char, 256> excessiveDamageMessage; excessiveDamageMessage.Format("Weapon '%s', firemode %d", pWeapon->GetEntity()->GetName(), nFireModeFromShotId); g_pGame->GetAntiCheatManager()->FlagActivity(eCT_WeaponDamage, pShooterActor->GetChannelId(), fDamageFromWeapon / fDamageAtXMeters, excessiveDamageMessage.c_str()); } if(pTargetActor) { CServerCheatMonitor::ValidateTargetActorPositionAgainstHit(*pTargetActor, hitInfo, fNetLagSeconds); } nNewCheckCounter = 0; } else { nNewCheckCounter = kCheckFreq - 1; } } } m_checkCounter = nNewCheckCounter; } // Update the shotcounter for tracking headshots and traversal times if(pShooterPlayer && (pHitTypeInfo->m_flags & CGameRules::EHitTypeFlag::ValidationRequired)) { char netName[128]; g_pGame->GetIGameFramework()->GetNetworkSafeClassName(netName, sizeof(netName), hitInfo.projectileClassId); IEntityClass * pProjectileClass = gEnv->pEntitySystem->GetClassRegistry()->FindClass(netName); if (pProjectileClass) { CShotCounter* pShotCounter = pShooterPlayer->GetShotCounter(); pShotCounter->RecordHit(hitInfo, bIsHeadShot); } } #endif // SERVER_CHECKS IEntityClass* pTargetClass = pTarget ? pTarget->GetClass() : NULL; // Check for friendly fire if( bool bCheckFriendlyFire = ((hitInfo.targetId!=hitInfo.shooterId) && (hitInfo.type!=CGameRules::EHitType::EventDamage)) ) { if(IVehicle* pTargetVehicle = g_pGame->GetIGameFramework()->GetIVehicleSystem()->GetVehicle(hitInfo.targetId)) { if(IActor* pDriverTargetVehicle = pTargetVehicle->GetDriver()) { // Vehicle driver shot own vehicle (same as shooting yourself), don't do friendly fire. bCheckFriendlyFire = pDriverTargetVehicle->GetEntityId()!=hitInfo.shooterId; } } if(bCheckFriendlyFire) { if (m_pGameRules->GetTeamCount() > 1) { int shooterTeamId = m_pGameRules->GetTeam(hitInfo.shooterId); int targetTeamId = m_pGameRules->GetTeam(hitInfo.targetId); if (shooterTeamId && (shooterTeamId == targetTeamId)) { damage = GetFriendlyFireDamage(damage, hitInfo, pTargetActor); } } } } if (damage <= 0.f) { // If the hit isn't doing anything bail, this means any hit that gets past here has damage associated with it and thus wants to // display a hit indicator return false; } if (pPlayer) { if(hitInfo.partId >= 0 && !isMelee) { damageHandling.damageMultiplier *= pPlayer->GetBodyDamageMultiplier(hitInfo); } if (isMelee) { damageHandling.damageMultiplier *= g_pGameCVars->pl_melee.damage_multiplier_mp; } } damage *= damageHandling.damageMultiplier; HitInfo hitInfoWithDamage = hitInfo; hitInfoWithDamage.damage = damage; if(pPlayer) { SActorStats* pStats = pPlayer->GetActorStats(); float actorHealth = pPlayer->GetHealth(); if(pStats) { #ifndef _RELEASE if (g_pGameCVars->g_LogDamage) { char weaponClassName[64], projectileClassName[64]; CryLog ("[DAMAGE] %s '%s' took %.3f '%s' damage (%.3f x %.3f) weapon=%s projectile=%s", pPlayer->GetEntity()->GetClass()->GetName(), pPlayer->GetEntity()->GetName(), damage, m_pGameRules->GetHitType(hitInfo.type), hitInfo.damage, damageHandling.damageMultiplier, g_pGame->GetIGameFramework()->GetNetworkSafeClassName(weaponClassName, sizeof(weaponClassName), hitInfo.weaponClassId) ? weaponClassName : "none", g_pGame->GetIGameFramework()->GetNetworkSafeClassName(projectileClassName, sizeof(projectileClassName), hitInfo.projectileClassId) ? projectileClassName : "none"); } #endif if(pStats->bStealthKilling && actorHealth <= damage) { if(pPlayer->GetStealthKill().GetTargetId() != hitInfoWithDamage.shooterId) { pPlayer->StoreDelayedKillingHitInfo(hitInfoWithDamage); } hitInfoWithDamage.damage = 0; } else if (pStats->bStealthKilled && hitInfoWithDamage.type != CGameRules::EHitType::StealthKill) { hitInfoWithDamage.damage = 0; } } } bool bKilled = SvOnHitScaled(hitInfoWithDamage); return bKilled; }
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(); }
//------------------------------------------------------------------------- bool CWeapon::OnActionModify(EntityId actorId, const ActionId& actionId, int activationMode, float value) { if (IsZoomed() || IsZoomingInOrOut()) return false; if (CanModify() && ((!IsReloading() && !IsBusy()) || AreAnyItemFlagsSet(eIF_Modifying))) { if (m_fm) m_fm->StopFire(); if (AreAnyItemFlagsSet(eIF_Modifying)) { m_enterModifyAction = 0; PlayAction(GetFragmentIds().leave_modify, 0); s_dofSpeed = fres(-g_pGameCVars->i_weapon_customisation_transition_time); s_dofValue = 1.0f; s_focusValue = -1.0f; GetScheduler()->TimerAction(g_pGameCVars->i_weapon_customisation_transition_time, CSchedulerAction<ScheduleLayer_Leave>::Create(this), false); SetItemFlags( eIF_Transitioning ); ClearItemFlags( eIF_Modifying ); GetGameObject()->InvokeRMI(CItem::SvRequestLeaveModify(), CItem::EmptyParams(), eRMI_ToServer); } else { gEnv->p3DEngine->SetPostEffectParam("Dof_Active", 1.0f); gEnv->p3DEngine->SetPostEffectParam("Dof_UseMask", 0.f); gEnv->p3DEngine->SetPostEffectParam("Dof_FocusRange", -1.f); gEnv->p3DEngine->SetPostEffectParam("Dof_FocusMin", 0.5f); gEnv->p3DEngine->SetPostEffectParam("Dof_FocusMax", 1.0f); gEnv->p3DEngine->SetPostEffectParam("Dof_FocusLimit", 1.5f); gEnv->p3DEngine->SetPostEffectParam("Dof_FocusMinZ", 0.0f); gEnv->p3DEngine->SetPostEffectParam("Dof_FocusMinZScale", 0.0f); m_itemLowerMode = eILM_Raised; TagState tagState = TAG_STATE_EMPTY; m_enterModifyAction = new CItemAction(PP_PlayerAction, GetFragmentIds().enter_modify, tagState); PlayFragment(m_enterModifyAction); s_dofSpeed = fres(g_pGameCVars->i_weapon_customisation_transition_time); s_dofValue = 0.0f; SetItemFlags(eIF_Transitioning); GetScheduler()->TimerAction(g_pGameCVars->i_weapon_customisation_transition_time, CSchedulerAction<ScheduleLayer_Enter>::Create(this), false); SetItemFlags(eIF_Modifying); CPlayer *pPlayer = static_cast<CPlayer*>(GetOwnerActor()); if (pPlayer) { SPlayerStats *pStats = static_cast<SPlayerStats*>(pPlayer->GetActorStats()); assert(pStats); pStats->bIgnoreSprinting = true; } GetGameObject()->InvokeRMI(CItem::SvRequestEnterModify(), CItem::EmptyParams(), eRMI_ToServer); } } return true; }
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 }
//-------------------------------------------------------------------------------------------------- // Name: SpawnScreenExplosionEffect // Desc: Spawns screen explosion effect //-------------------------------------------------------------------------------------------------- void CExplosionGameEffect::SpawnScreenExplosionEffect(const SExplosionContainer &explosionContainer) { // Disclaimer: this code was originally from GameRulesClientServer::ProcessClientExplosionScreenFX() const ExplosionInfo& explosionInfo = explosionContainer.m_explosionInfo; if(explosionInfo.pressure < 1.0f) return; IActor *pClientActor = g_pGame->GetIGameFramework()->GetClientActor(); if(pClientActor != NULL && !pClientActor->IsDead()) { CPlayer* pPlayer = static_cast<CPlayer*>(pClientActor); bool hasFlashBangEffect = explosionInfo.blindAmount > 0.0f; // Flashbang friends and self?... if(hasFlashBangEffect) { bool flashBangSelf = true; bool flashBangFriends = false; #ifndef _RELEASE flashBangSelf = g_pGameCVars->g_flashBangSelf != 0; flashBangFriends = g_pGameCVars->g_flashBangFriends != 0; #endif bool ownFlashBang = pPlayer->GetEntityId() == explosionInfo.shooterId; if((!flashBangSelf && ownFlashBang) || // FlashBang self? ((g_pGame->GetGameRules()->GetFriendlyFireRatio()<=0.0f) && (!flashBangFriends) && (!ownFlashBang) && pPlayer->IsFriendlyEntity(explosionInfo.shooterId))) // FlashBang friends? { return; } } // Distance float dist = (pClientActor->GetEntity()->GetWorldPos() - explosionInfo.pos).len(); // Is the explosion in Player's FOV (let's suppose the FOV a bit higher, like 80) SMovementState state; if(IMovementController *pMV = pClientActor->GetMovementController()) { pMV->GetMovementState(state); } Vec3 eyeToExplosion = explosionInfo.pos - state.eyePosition; Vec3 eyeDir = pClientActor->GetLinkedVehicle() ? pPlayer->GetVehicleViewDir() : state.eyeDirection; eyeToExplosion.Normalize(); float eyeDirectionDP = eyeDir.Dot(eyeToExplosion); bool inFOV = (eyeDirectionDP > 0.68f); // All explosions have radial blur (default 30m radius) const float maxBlurDistance = (explosionInfo.maxblurdistance >0.0f) ? explosionInfo.maxblurdistance : 30.0f; if((maxBlurDistance > 0.0f) && (g_pGameCVars->g_radialBlur > 0.0f) && (explosionInfo.radius > 0.5f)) { if (inFOV && (dist < maxBlurDistance)) { const int intersectionObjTypes = ent_static | ent_terrain; const unsigned int intersectionFlags = rwi_stop_at_pierceable|rwi_colltype_any; m_deferredScreenEffects.RequestRayCast(CDeferredExplosionEffect::eDET_RadialBlur, explosionInfo.pos, -eyeToExplosion, dist, maxBlurDistance, intersectionObjTypes, intersectionFlags, NULL, 0); } } // Flashbang effect if(hasFlashBangEffect && ((dist < (explosionInfo.radius*g_pGameCVars->g_flashBangNotInFOVRadiusFraction)) || (inFOV && (dist < explosionInfo.radius)))) { ray_hit hit; const int intersectionObjTypes = ent_static | ent_terrain; const unsigned int intersectionFlags = rwi_stop_at_pierceable|rwi_colltype_any; const int intersectionMaxHits = 1; int collision = gEnv->pPhysicalWorld->RayWorldIntersection( explosionInfo.pos, -eyeToExplosion*dist, intersectionObjTypes, intersectionFlags, &hit, intersectionMaxHits); // If there was no obstacle between flashbang grenade and player if(!collision) { bool enabled = true; if(enabled) { CCCPOINT (FlashBang_Explode_BlindLocalPlayer); float timeScale = max(0.0f, 1 - (dist/explosionInfo.radius)); float lookingAt = max(g_pGameCVars->g_flashBangMinFOVMultiplier, (eyeDirectionDP + 1)*0.5f); float time = explosionInfo.flashbangScale * timeScale *lookingAt; // time is determined by distance to explosion CRY_ASSERT_MESSAGE(pClientActor->IsPlayer(),"Effect shouldn't be spawned if not a player"); SPlayerStats* pStats = static_cast<SPlayerStats*>(pPlayer->GetActorStats()); NET_BATTLECHATTER(BC_Blinded, pPlayer); if(pClientActor->GetEntityId() == explosionInfo.shooterId) { g_pGame->GetPersistantStats()->IncrementClientStats(EIPS_BlindSelf); } else { g_pGame->GetGameRules()->SuccessfulFlashBang(explosionInfo, time); } pPlayer->StartFlashbangEffects(time, explosionInfo.shooterId); gEnv->p3DEngine->SetPostEffectParam("Flashbang_Time", time); gEnv->p3DEngine->SetPostEffectParam("FlashBang_BlindAmount", explosionInfo.blindAmount); gEnv->p3DEngine->SetPostEffectParam("Flashbang_DifractionAmount", time); gEnv->p3DEngine->SetPostEffectParam("Flashbang_Active", 1.0f); CRecordingSystem *pRecordingSystem = g_pGame->GetRecordingSystem(); if (pRecordingSystem) { pRecordingSystem->OnPlayerFlashed(time, explosionInfo.blindAmount); } } } else { CCCPOINT (FlashBang_Explode_NearbyButBlockedByGeometry); } } else if(inFOV && (dist < explosionInfo.radius)) { if(explosionInfo.damage>10.0f || explosionInfo.pressure>100.0f) { // Add some angular impulse to the client actor depending on distance, direction... float dt = (1.0f - dist/explosionInfo.radius); dt = dt * dt; float angleZ = gf_PI*0.15f*dt; float angleX = gf_PI*0.15f*dt; if (pClientActor) { static_cast<CActor*>(pClientActor)->AddAngularImpulse(Ang3(cry_random(-angleX*0.5f,angleX),0.0f,cry_random(-angleZ,angleZ)),0.0f,dt*2.0f); } } } } }//-------------------------------------------------------------------------------------------------
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); } }