void CCameraPolar::Set(const Vec3 &vDir) { //directions must be normalized CRY_ASSERT(fabsf(vDir.len() - 1.0f) < g_fCamError); //yaw in 0 .. 2PI, starting at FORWARD (0,1,0) m_fYaw = cry_acosf(vDir.y); if(vDir.x > 0) m_fYaw = gf_PI2 - m_fYaw; //pitch in 0 .. PI, starting at UP (0,0,1) m_fPitch = fabsf(cry_acosf(vDir.z)); m_fPitch = clamp(m_fPitch, g_fCameraPolarMinPitch, g_fCameraPolarMaxPitch); }
float rappleAction::returnAngleBetweenTwoVec(Vec3 start, Vec3 end) { float fA, fB, fDot; fA = sqrtf(start.x * start.x + start.y * start.y + start.z + start.z); fB = sqrtf(end.x * end.x + end.y * end.y + end.z + end.z); fDot = start.x * end.x + start.y * end.y + start.z + end.z; return cry_acosf(fDot / (fA * fB)); }
//------------------------------------------------------------------------ bool CGunTurret::IsAiming(const Vec3 &pos, float treshold) const { Vec3 tdir=(pos-GetWeaponPos()).normalized(); float dot = GetWeaponDir().Dot(tdir); float angle=RAD2DEG(cry_acosf(CLAMP(dot,-1.f,1.f))); if(cry_fabsf(angle)>treshold) return false; return true; }
EStatus Update(float timePassed) { if (IsTransitioningOut()) { const float ROTATION_LERP_SPEED = 10.0f; //--- Blend body rotation to match current view Ang3 targetViewDir = m_player.GetAngles(); Quat targetRotation = Quat::CreateRotationZ(targetViewDir.z); Quat newRotation = Quat::CreateNlerp(m_player.GetEntity()->GetRotation(), targetRotation, timePassed*ROTATION_LERP_SPEED); m_player.GetEntity()->SetRotation(newRotation); } else { static uint32 leanParamCRC = gEnv->pSystem->GetCrc32Gen()->GetCRC32Lowercase("SlideFactor"); const Matrix34 &worldTM = m_player.GetEntity()->GetWorldTM(); const Vec3 baseRgt = worldTM.GetColumn0(); const Vec3 baseFwd = worldTM.GetColumn1(); const Vec3 lookFwd = m_player.GetViewQuatFinal().GetColumn1(); const float leanAngle = cry_acosf(baseFwd.Dot(lookFwd)); float targetLeanFactor = clamp(leanAngle / MAX_LEAN_ANGLE, 0.0f, 1.0f); if (baseRgt.Dot(lookFwd) < 0.0f) { targetLeanFactor *= -1.0f; } CWeapon *pWeapon = m_player.GetWeapon(m_player.GetCurrentItemId()); if (pWeapon) { IFireMode *pFiremode = pWeapon->GetFireMode(pWeapon->GetCurrentFireMode()); if (pFiremode && (pFiremode->GetNextShotTime() > 0.0f)) { targetLeanFactor = 0.0f; } } const float delta = targetLeanFactor - m_leanFactor; const float step = LEAN_RATE * timePassed; const float newLeanFactor = (float)__fsel(delta, min(m_leanFactor + step, targetLeanFactor), max(m_leanFactor - step, targetLeanFactor)); SWeightData weightData; weightData.weights[0] = newLeanFactor; SetParam(leanParamCRC, weightData); m_leanFactor = newLeanFactor; if (GetRootScope().IsDifferent(m_fragmentID, m_fragTags)) { SetFragment(m_fragmentID, m_fragTags); } } return TPlayerAction::Update(timePassed); }
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 CProjectile::Ricochet(EventPhysCollision *pCollision) { IActor *pActor = g_pGame->GetIGameFramework()->GetClientActor(); if (!pActor) return; Vec3 dir=pCollision->vloc[0]; dir.NormalizeSafe(); float dot=pCollision->n.Dot(dir); if (dot>=0.0f) // backface return; float b=0,f=0; uint matPierceability=0; if (!gEnv->pPhysicalWorld->GetSurfaceParameters(pCollision->idmat[1], b, f, matPierceability)) return; matPierceability&=sf_pierceable_mask; float probability=0.25+0.25*(MAX(0,7-matPierceability)/7.0f); if ((matPierceability && matPierceability>=8) || Random()>probability) return; f32 cosine = dir.Dot(-pCollision->n); if (cosine>1.0f)cosine=1.0f; if (cosine<-1.0f) cosine=-1.0f; float angle=RAD2DEG( cry_fabsf(cry_acosf(cosine)) ); if (angle<10.0f) return; Vec3 ricochetDir = -2.0f*dot*pCollision->n+dir; ricochetDir.NormalizeSafe(); Ang3 angles=Ang3::GetAnglesXYZ(Matrix33::CreateRotationVDir(ricochetDir)); float rx=Random()-0.5f; float rz=Random()-0.5f; angles.x+=rx*DEG2RAD(10.0f); angles.z+=rz*DEG2RAD(10.0f); ricochetDir=Matrix33::CreateRotationXYZ(angles).GetColumn(1).normalized(); Lineseg line(pCollision->pt, pCollision->pt+ricochetDir*20.0f); Vec3 player = pActor->GetEntity()->GetWorldPos(); float t; float distanceSq=Distance::Point_LinesegSq(player, line, t); if (distanceSq < 7.5*7.5 && (t>=0.0f && t<=1.0f)) { if (distanceSq >= 0.25*0.25) { Sphere s; s.center = player; s.radius = 6.0f; Vec3 entry,exit; int intersect=Intersect::Lineseg_Sphere(line, s, entry,exit); if (intersect) // one entry or one entry and one exit { if (intersect==0x2) entry=pCollision->pt; RicochetSound(entry, ricochetDir); //gEnv->pRenderer->GetIRenderAuxGeom()->DrawLine(entry, ColorB(255, 255, 255, 255), entry+ricochetDir, ColorB(255, 255, 255, 255), 2); } } } }
void CCameraInputHelper::UpdateCameraInput(Ang3 &deltaRotation, const float frameTimeClamped, const float frameTimeNormalised) { // Use new control scheme, // or, if we're just entering/leaving it then adjust camera/character orientations as appropriate. //--- Translate left stick into a deltaRotation.z (and a move direction) Vec3 vcStick=Vec3(m_pHeroInput->m_moveStickLR, m_pHeroInput->m_moveStickUD,0); // Stick vector in view space // -m_moveStickLR removes flicker, but inverses direction.. static Vec3 vcStickPrev=Vec3(0,1,0); float stickMag=vcStick.len(); if(stickMag>0.001f) vcStick*=1/stickMag; else vcStick=vcStickPrev; // should hold previous value/direction unless we're in transition. vcStickPrev=vcStick; Matrix33 camViewMtx = m_pHero->m_camViewMtxFinal; //in flight mode the actual camera matrix changes //the mtxFinal itself stays untouched, so that the camera can move back after the flight if(CCameraFlight::GetInstance()->IsCameraFlightActive()) { Vec3 vCamDir = CCameraFlight::GetInstance()->GetLookingDirection(); camViewMtx.SetRotationVDir(vCamDir, 0.0f); } const Matrix34& entityWorldTM = m_pHero->GetEntity()->GetWorldTM(); Vec3 vwEntPos = entityWorldTM.GetTranslation(); Vec3 vwDrawFrom = vwEntPos+Vec3(0,0,0.8f); Vec3 vwEntityFront = entityWorldTM.GetColumn1(); Vec3 vwEntityRight = entityWorldTM.GetColumn0(); Vec3 vCamRef = camViewMtx*Vec3(0,1,0); Vec3 vwStick = camViewMtx*vcStick; // Stick vector in world space Vec3 vwfStick = Vec3FlattenXY(vwStick); Vec3 vwfEntityFront=Vec3FlattenXY(vwEntityFront); Vec3 vwfEntityRight=Vec3FlattenXY(vwEntityRight); float dotRight=vwfStick.Dot(vwfEntityRight); float dotFront=vwfStick.Dot(vwfEntityFront); dotFront = clamp(dotFront,-1.0f,1.0f); bool enteredNavFromCombat = false; //(m_bNavCombatModeChanged && !bIsCombatMode); //--- Just moved into nav mode from combat if(enteredNavFromCombat) { // so set directions appropriately. float dotFront2=(vCamRef).Dot(Vec3(0,1,0)); float dotRight2=(camViewMtx*Vec3(1,0,0)).Dot(Vec3(0,1,0)); float yaw = 0.0f; dotFront2 = clamp(dotFront2,-1.0f,1.0f); if(dotRight2>=0) { yaw=-cry_acosf(dotFront2); } else { yaw=cry_acosf(dotFront2); } m_fYaw = yaw; } //this is the actual entity rotation (yaw movement) CCameraOverrides *pCamOverride = g_pGame->GetCameraManager()->GetCamOverrides(); int eCamOverrideMode = pCamOverride->GetCameraOverride(); //the ECO_LOWCOVER fading is a hack, causing problems when inputs are changed during the fade //in combat mode all inputs have to be passed through to synchronize the camera direction in other modes if((stickMag>0.01f && eCamOverrideMode != ECO_LOWCOVER)) { if(dotRight>=0) { deltaRotation.z=-cry_acosf(dotFront); } else { deltaRotation.z=cry_acosf(dotFront); } } else deltaRotation.z=0; float maxDeltaRot = 7.85f; // safety value //g_pGameCVars->cl_nav_SprintMaxTurnRate; Limit( maxDeltaRot, 0.f, gf_PI * 10.f ); // limit to 3600 degrees per second, i.e. 'pretty fast' maxDeltaRot *= frameTimeClamped; // In Nav mode movement is always in the characters forward direction. UpdatePitchYawDriver(stickMag, frameTimeNormalised); m_bNavCombatModeChanged=false; }
//------------------------------------------------------------------------ void CScriptControlledPhysics::OnPostStep(EventPhysPostStep *pPostStep) { pe_action_set_velocity av; bool moving=m_moving; bool rotating=m_rotating; float dt=pPostStep->dt; if(m_moving) { Vec3 current=pPostStep->pos; Vec3 target=m_moveTarget; Vec3 delta=target-current; float distance=delta.len(); Vec3 dir=delta; if(distance>0.01f) dir *= (1.0f/distance); if(distance<0.01f) { m_speed=0.0f; m_moving=false; pPostStep->pos=target; av.v=ZERO; } else { float a=m_speed/m_stopTime; float d=m_speed*m_stopTime-0.5f*a*m_stopTime*m_stopTime; if(distance<=(d+0.01f)) m_acceleration=(distance-m_speed*m_stopTime)/(m_stopTime*m_stopTime); m_speed=m_speed+m_acceleration*dt; if(m_speed>=m_maxSpeed) { m_speed=m_maxSpeed; m_acceleration=0.0f; } else if(m_speed*dt>distance) m_speed=distance/dt; else if(m_speed<0.05f) m_speed=0.05f; av.v=dir*m_speed; } } if(m_rotating) { Quat current=pPostStep->q; Quat target=m_rotationTarget; Quat rotation=target*!current; float angle=cry_acosf(CLAMP(rotation.w, -1.0f, 1.0f))*2.0f; float original=angle; if(angle>gf_PI) angle=angle-gf_PI2; else if(angle<-gf_PI) angle=angle+gf_PI2; if(cry_fabsf(angle)<0.001f) { m_rotationSpeed=0.0f; m_rotating=false; pPostStep->q=m_rotationTarget; av.w=ZERO; } else { float a=m_rotationSpeed/m_rotationStopTime; float d=m_rotationSpeed*m_stopTime-0.5f*a*m_rotationStopTime*m_rotationStopTime; if(cry_fabsf(angle)<d+0.001f) m_rotationAcceleration=(angle-m_rotationSpeed*m_rotationStopTime)/(m_rotationStopTime*m_rotationStopTime); m_rotationSpeed=m_rotationSpeed+sgn(angle)*m_rotationAcceleration*dt; if(cry_fabsf(m_rotationSpeed*dt)>cry_fabsf(angle)) m_rotationSpeed=angle/dt; else if(cry_fabsf(m_rotationSpeed)<0.001f) m_rotationSpeed=sgn(m_rotationSpeed)*0.001f; else if(cry_fabsf(m_rotationSpeed)>=m_rotationMaxSpeed) { m_rotationSpeed=sgn(m_rotationSpeed)*m_rotationMaxSpeed; m_rotationAcceleration=0.0f; } } if(cry_fabsf(angle)>=0.001f) av.w=(rotation.v/cry_sinf(original*0.5f)).normalized(); av.w*=m_rotationSpeed; } if(moving || rotating) { if(IPhysicalEntity *pPE=GetEntity()->GetPhysics()) pPE->Action(&av); } if((moving && !m_moving) || (rotating && !m_rotating)) GetEntity()->SetWorldTM(Matrix34::Create(GetEntity()->GetScale(), pPostStep->q, pPostStep->pos)); }
//------------------------------------------------------------------------ void CScriptControlledPhysics::OnPostStep(EventPhysPostStep *pPostStep) { pe_action_set_velocity av; const bool moving = m_moving; const bool rotating = m_rotating; const float deltaTime = pPostStep->dt; if (m_moving) { const Vec3 current = pPostStep->pos; const Vec3 target = m_moveTarget; const Vec3 delta = target - current; const float distanceSq = delta.len2(); if (distanceSq <= sqr(0.025f) || (delta.dot(m_lastVelocity) < 0.0f)) { m_speed = 0.0f; m_moving = false; m_lastVelocity.zero(); pPostStep->pos = target; av.v = ZERO; } else { float velocity = m_speed; float acceleration = m_acceleration; Vec3 direction = delta; const float distanceToEnd = direction.NormalizeSafe(); // Accelerate velocity = std::min(velocity + acceleration * deltaTime, m_maxSpeed); // Calculate acceleration and time needed to stop const float accelerationNeededToStop = (-(velocity*velocity) / distanceToEnd) / 2; if (fabsf(accelerationNeededToStop) > 0.0f) { const float timeNeededToStop = sqrtf((distanceToEnd / 0.5f) / fabsf(accelerationNeededToStop)); if (timeNeededToStop < m_stopTime) { acceleration = accelerationNeededToStop; } } // Prevent overshooting if ((velocity * deltaTime) > distanceToEnd) { const float multiplier = distanceToEnd / fabsf(velocity * deltaTime); velocity *= multiplier; acceleration *= multiplier; } m_acceleration = acceleration; m_speed = velocity; m_lastVelocity = direction * velocity; av.v = direction * velocity; } } if (m_rotating) { Quat current=pPostStep->q; Quat target=m_rotationTarget; Quat rotation=target*!current; float angle=cry_acosf(CLAMP(rotation.w, -1.0f, 1.0f))*2.0f; float original=angle; if (angle>gf_PI) angle=angle-gf_PI2; else if (angle<-gf_PI) angle=angle+gf_PI2; if (cry_fabsf(angle)<0.01f) { m_rotationSpeed=0.0f; m_rotating=false; pPostStep->q=m_rotationTarget; av.w=ZERO; } else { float a=m_rotationSpeed/m_rotationStopTime; float d=m_rotationSpeed*m_stopTime-0.5f*a*m_rotationStopTime*m_rotationStopTime; if (cry_fabsf(angle)<d+0.001f) m_rotationAcceleration=(angle-m_rotationSpeed*m_rotationStopTime)/(m_rotationStopTime*m_rotationStopTime); m_rotationSpeed=m_rotationSpeed+sgn(angle)*m_rotationAcceleration*deltaTime; if (cry_fabsf(m_rotationSpeed*deltaTime)>cry_fabsf(angle)) m_rotationSpeed=angle/deltaTime; else if (cry_fabsf(m_rotationSpeed)<0.001f) m_rotationSpeed=sgn(m_rotationSpeed)*0.001f; else if (cry_fabsf(m_rotationSpeed)>=m_rotationMaxSpeed) { m_rotationSpeed=sgn(m_rotationSpeed)*m_rotationMaxSpeed; m_rotationAcceleration=0.0f; } } if(cry_fabsf(angle)>=0.001f) av.w=(rotation.v/cry_sinf(original*0.5f)).normalized(); av.w*=m_rotationSpeed; } if (moving || rotating) { if (IPhysicalEntity *pPE=GetEntity()->GetPhysics()) pPE->Action(&av, 1); } /* if ((moving && !m_moving) || (rotating && !m_rotating)) GetEntity()->SetWorldTM(Matrix34::Create(GetEntity()->GetScale(), pPostStep->q, pPostStep->pos)); */ }
void CMountedGunController::Update(EntityId mountedGunID, float frameTime) { CRY_ASSERT_MESSAGE(m_pControlledPlayer, "Controlled player not initialized"); CItem* pMountedGun = static_cast<CItem*>(gEnv->pGame->GetIGameFramework()->GetIItemSystem()->GetItem(mountedGunID)); bool canUpdateMountedGun = (pMountedGun != NULL) && (pMountedGun->GetStats().mounted); if (canUpdateMountedGun) { IMovementController * pMovementController = m_pControlledPlayer->GetMovementController(); assert(pMovementController); SMovementState info; pMovementController->GetMovementState(info); IEntity* pMountedGunEntity = pMountedGun->GetEntity(); const Matrix34& lastMountedGunWorldTM = pMountedGunEntity->GetWorldTM(); Vec3 desiredAimDirection = info.aimDirection.GetNormalized(); // AI can switch directions too fast, prevent snapping if(!m_pControlledPlayer->IsPlayer()) { const Vec3 currentDir = lastMountedGunWorldTM.GetColumn1(); const float dot = clamp(currentDir.Dot(desiredAimDirection), -1.0f, 1.0f); const float reqAngle = cry_acosf(dot); const float maxRotSpeed = 2.0f; const float maxAngle = frameTime * maxRotSpeed; if(fabs(reqAngle) > maxAngle) { const Vec3 axis = currentDir.Cross(desiredAimDirection); if(axis.GetLengthSquared() > 0.001f) // current dir and new dir are enough different { desiredAimDirection = currentDir.GetRotated(axis.GetNormalized(),sgn(reqAngle)*maxAngle); } } } bool isUserClient = m_pControlledPlayer->IsClient(); IEntity* pMountedGunParentEntity = pMountedGunEntity->GetParent(); IVehicle *pVehicle = NULL; if(pMountedGunParentEntity && m_pControlledPlayer) pVehicle = m_pControlledPlayer->GetLinkedVehicle(); CRecordingSystem* pRecordingSystem = g_pGame->GetRecordingSystem(); //For client update always, for others only when there is notable change if (!pVehicle && (isUserClient || (!desiredAimDirection.IsEquivalent(lastMountedGunWorldTM.GetColumn1(), 0.003f)))) { Quat rotation = Quat::CreateRotationVDir(desiredAimDirection, 0.0f); pMountedGunEntity->SetRotation(rotation); if (isUserClient && pRecordingSystem) { // Only record the gun position if you're using the gun. pRecordingSystem->OnMountedGunRotate(pMountedGunEntity, rotation); } } const Vec3 vInitialAimDirection = GetMountDirection(pMountedGun, pMountedGunParentEntity); assert( vInitialAimDirection.IsUnit() ); //Adjust gunner position and animations UpdateGunnerLocation(pMountedGun, pMountedGunParentEntity, vInitialAimDirection); const float aimrad = Ang3::CreateRadZ(Vec2(vInitialAimDirection),Vec2(-desiredAimDirection)); const float pitchLimit = sin_tpl(DEG2RAD(30.0f)); const float animHeight = fabs_tpl(clamp(desiredAimDirection.z * (float)__fres(pitchLimit), -1.0f, 1.0f)); const float aimUp = (float)__fsel(-desiredAimDirection.z, 0.0f, animHeight); const float aimDown = (float)__fsel(desiredAimDirection.z, 0.0f, animHeight); if (pRecordingSystem) { pRecordingSystem->OnMountedGunUpdate(m_pControlledPlayer, aimrad, aimUp, aimDown); } if(!m_pControlledPlayer->IsThirdPerson()) { UpdateFirstPersonAnimations(pMountedGun, desiredAimDirection); } if(m_pMovementAction) { const float aimUpParam = aimUp; const float aimDownParam = aimDown; const float aimMovementParam = CalculateAnimationTime(aimrad); m_pMovementAction->SetParam(MountedGunCRCs.aimUpParam, aimUpParam); m_pMovementAction->SetParam(MountedGunCRCs.aimDownParam, aimDownParam); m_pMovementAction->SetParam(MountedGunCRCs.aimMovementParam, aimMovementParam); } UpdateIKMounted(pMountedGun); } }
//------------------------------------------------------------------------ void CItem::UpdateMounted(float frameTime) { IRenderAuxGeom* pAuxGeom = gEnv->pRenderer->GetIRenderAuxGeom(); if (!m_ownerId || !m_stats.mounted) return; CActor *pActor = GetOwnerActor(); if (!pActor) return; CheckViewChange(); if (true) { if (IsClient()) { ICharacterInstance *pCharacter = GetEntity()->GetCharacter(eIGS_FirstPerson); if (pCharacter && !m_idleAnimation[eIGS_FirstPerson].empty() && pCharacter->GetISkeletonAnim()->GetNumAnimsInFIFO(0)<1) PlayAction(m_idleAnimation[eIGS_FirstPerson], 0, true); } // need to explicitly update characters at this point // cause the entity system update occered earlier, with the last position for (int i=0; i<eIGS_Last; i++) { if (GetEntity()->GetSlotFlags(i)&ENTITY_SLOT_RENDER) { ICharacterInstance *pCharacter = GetEntity()->GetCharacter(i); if (pCharacter) { Matrix34 mloc = GetEntity()->GetSlotLocalTM(i,false); Matrix34 m34 = GetEntity()->GetWorldTM()*mloc; QuatT renderLocation = QuatT(m34); pCharacter->GetISkeletonPose()->SetForceSkeletonUpdate(9); pCharacter->SkeletonPreProcess(renderLocation, renderLocation, GetISystem()->GetViewCamera(),0x55 ); pCharacter->SetPostProcessParameter(renderLocation, renderLocation, 0, 0.0f, 0x55 ); } } } // f32 fColor[4] = {1,1,0,1}; // f32 g_YLine=60.0f; // gEnv->pRenderer->Draw2dLabel( 1,g_YLine, 1.3f, fColor, false, "Mounted Gun Code" ); //adjust the orientation of the gun based on the aim-direction SMovementState info; IMovementController* pMC = pActor->GetMovementController(); pMC->GetMovementState(info); Vec3 dir = info.aimDirection.GetNormalized(); Matrix34 tm = Matrix33::CreateRotationVDir(dir); Vec3 vGunXAxis=tm.GetColumn0(); if (pActor->GetLinkedVehicle()==0) { if (pMC) { if(!pActor->IsPlayer()) { // prevent snapping direction Vec3 currentDir = GetEntity()->GetWorldRotation().GetColumn1(); float dot = currentDir.Dot(dir); dot = CLAMP(dot,-1,1); float reqAngle = cry_acosf(dot); const float maxRotSpeed = 2.0f; float maxAngle = frameTime * maxRotSpeed; if(fabs(reqAngle) > maxAngle) { Vec3 axis = currentDir.Cross(dir); if(axis.GetLengthSquared()>0.001f) // current dir and new dir are enough different dir = currentDir.GetRotated(axis.GetNormalized(),sgn(reqAngle)*maxAngle); } } //adjust the orientation of the gun based on the aim-direction tm = Matrix33::CreateRotationVDir(dir); Vec3 vWPos=GetEntity()->GetWorldPos(); tm.SetTranslation(vWPos); GetEntity()->SetWorldTM(tm); //set the new orientation of the mounted gun vGunXAxis=tm.GetColumn0(); Vec3 vInitialAimDirection = m_stats.mount_dir; Matrix33 vInitialPlayerOrientation = Matrix33::CreateRotationVDir(vInitialAimDirection); assert( vInitialAimDirection.IsUnit() ); Vec3 newp; if (pActor->IsThirdPerson()) { //third person f32 dist = m_mountparams.body_distance*1.3f; Vec3 oldp = pActor->GetEntity()->GetWorldPos(); newp = GetEntity()->GetWorldPos()-vInitialAimDirection*dist; //mounted gun newp.z = oldp.z; } else { //first person f32 fMoveBack = (1.0f+(dir.z*dir.z*dir.z*dir.z*4.0f))*0.75f; f32 dist = m_mountparams.eye_distance*fMoveBack; Vec3 oldp = pActor->GetEntity()->GetWorldPos(); newp = GetEntity()->GetWorldPos()-dir*dist; //mounted gun //newp.z -= 0.75f; newp.z = oldp.z; } Matrix34 actortm(pActor->GetEntity()->GetWorldTM()); //if (pActor->IsThirdPerson()) actortm=vInitialPlayerOrientation; actortm.SetTranslation(newp); pActor->GetEntity()->SetWorldTM(actortm, ENTITY_XFORM_USER); pActor->GetAnimationGraphState()->SetInput("Action","gunnerMounted"); //f32 g_YLine=80.0f; //gEnv->pRenderer->Draw2dLabel( 1,g_YLine, 1.3f, fColor, false, "Mounted Gun Active for FP and AI" ); if (ICharacterInstance *pCharacter = pActor->GetEntity()->GetCharacter(0)) { ISkeletonAnim *pSkeletonAnim = pCharacter->GetISkeletonAnim(); assert(pSkeletonAnim); uint32 numAnimsLayer = pSkeletonAnim->GetNumAnimsInFIFO(0); for(uint32 i=0; i<numAnimsLayer; i++) { CAnimation &animation = pSkeletonAnim->GetAnimFromFIFO(0, i); if (animation.m_AnimParams.m_nFlags & CA_MANUAL_UPDATE) { f32 aimrad = Ang3::CreateRadZ(Vec2(vInitialAimDirection),Vec2(dir)); animation.m_fAnimTime = clamp_tpl(aimrad/gf_PI,-1.0f,+1.0f)*0.5f+0.5f; //if (pActor->IsThirdPerson()==0) //animation.m_fAnimTime=0.6f; //Ivo & Benito: high advanced future code. don't ask what it is //Benito - Not needed any more ;) //f32 g_YLine=100.0f; //gEnv->pRenderer->Draw2dLabel( 1,g_YLine, 1.3f, fColor, false, "AnimTime: %f MyAimAngle: %f deg:% distance:%f", animation.m_fAnimTime, aimrad, RAD2DEG(aimrad),m_mountparams.body_distance ); } } } m_stats.mount_last_aimdir = dir; } } if (ICharacterInstance* pCharInstance = pActor->GetEntity()->GetCharacter(0)) { if (ISkeletonAnim* pSkeletonAnim = pCharInstance->GetISkeletonAnim()) { OldBlendSpace ap; if (GetAimBlending(ap)) { pSkeletonAnim->SetBlendSpaceOverride(eMotionParamID_TurnSpeed, 0.5f + 0.5f * ap.m_turn, true); } } } UpdateIKMounted(pActor, vGunXAxis*0.1f); RequireUpdate(eIUS_General); } }
//----------------------------------------------------------------------------- void CHomingMissile::UpdateControlledMissile(float frameTime) { bool isServer = gEnv->bServer; bool isClient = gEnv->bClient; CActor *pClientActor=0; if (gEnv->bClient) pClientActor=static_cast<CActor *>(g_pGame->GetIGameFramework()->GetClientActor()); bool isOwner = ((!m_ownerId && isServer) || (isClient && pClientActor && (pClientActor->GetEntityId() == m_ownerId) && pClientActor->IsPlayer())); IRenderer* pRenderer = gEnv->pRenderer; IRenderAuxGeom* pGeom = pRenderer->GetIRenderAuxGeom(); float color[4] = {1,1,1,1}; const static float step = 15.f; float y = 20.f; bool bDebug = g_pGameCVars->i_debug_projectiles > 0; if (isOwner || isServer) { //If there's a target, follow the target if(isServer) { if (m_targetId) { if (m_lockedTimer>0.0f) m_lockedTimer=m_lockedTimer-frameTime; else { // If we are here, there's a target IEntity* pTarget = gEnv->pEntitySystem->GetEntity(m_targetId); if (pTarget) { AABB box; pTarget->GetWorldBounds(box); Vec3 finalDes = box.GetCenter(); SetDestination(finalDes); //SetDestination( box.GetCenter() ); if (bDebug) pRenderer->Draw2dLabel(5.0f, y+=step, 1.5f, color, false, "Target Entity: %s", pTarget->GetName()); } m_lockedTimer+=0.05f; } } else if(m_autoControlled) return; } if (m_controlled && !m_autoControlled && isOwner && !m_targetId) { //Check if the weapon is still selected CWeapon *pWeapon = GetWeapon(); if(!pWeapon || !pWeapon->IsSelected()) return; if (m_controlledTimer>0.0f) m_controlledTimer=m_controlledTimer-frameTime; else if (pClientActor && pClientActor->IsPlayer()) //Follow the crosshair { if (IMovementController *pMC=pClientActor->GetMovementController()) { Vec3 eyePos(ZERO); Vec3 eyeDir(ZERO); IVehicle* pVehicle = pClientActor->GetLinkedVehicle(); if(!pVehicle) { SMovementState state; pMC->GetMovementState(state); eyePos = state.eyePosition; eyeDir = state.eyeDirection; } else { SViewParams viewParams; pVehicle->UpdateView(viewParams, pClientActor->GetEntityId()); eyePos = viewParams.position; eyeDir = viewParams.rotation * Vec3(0,1,0); //eyeDir = (viewParams.targetPos - viewParams.position).GetNormalizedSafe(); } int pierceability=7; if (IPhysicalEntity *pPE=GetEntity()->GetPhysics()) { if (pPE->GetType()==PE_PARTICLE) { pe_params_particle pp; if (pPE->GetParams(&pp)) pierceability=pp.iPierceability; } } static const int objTypes = ent_all; static const int flags = (geom_colltype_ray << rwi_colltype_bit) | rwi_colltype_any | (pierceability & rwi_pierceability_mask) | (geom_colltype14 << rwi_colltype_bit); IPhysicalWorld* pWorld = gEnv->pPhysicalWorld; static IPhysicalEntity* pSkipEnts[10]; int numSkip = CSingle::GetSkipEntities(pWeapon, pSkipEnts, 10); ray_hit hit; int hits = 0; float range=m_maxTargetDistance; hits = pWorld->RayWorldIntersection(eyePos + 1.5f*eyeDir, eyeDir*range, objTypes, flags, &hit, 1, pSkipEnts, numSkip); while (hits) { if (gEnv->p3DEngine->RefineRayHit(&hit, eyeDir*range)) break; eyePos = hit.pt+eyeDir*0.003f; range -= hit.dist+0.003f; hits = pWorld->RayWorldIntersection(eyePos, eyeDir*range, objTypes, flags, &hit, 1, pSkipEnts, numSkip); } DestinationParams params; if(hits) params.pt=hit.pt; else params.pt=(eyePos+m_maxTargetDistance*eyeDir); //Some point in the sky... GetGameObject()->InvokeRMI(SvRequestDestination(), params, eRMI_ToServer); if (bDebug) { pRenderer->Draw2dLabel(5.0f, y+=step, 1.5f, color, false, "PlayerView eye direction: %.3f %.3f %.3f", eyeDir.x, eyeDir.y, eyeDir.z); pRenderer->Draw2dLabel(5.0f, y+=step, 1.5f, color, false, "PlayerView Target: %.3f %.3f %.3f", hit.pt.x, hit.pt.y, hit.pt.z); pRenderer->GetIRenderAuxGeom()->DrawCone(m_destination, Vec3(0,0,-1), 2.5f, 7.f, ColorB(255,0,0,255)); } } m_controlledTimer+=0.0f; } } } //This code is shared by both modes above (auto and controlled) if(!m_destination.IsZero()) { pe_status_dynamics status; if (!GetEntity()->GetPhysics()->GetStatus(&status)) { CryLogAlways("couldn't get physics status!"); return; } pe_status_pos pos; if (!GetEntity()->GetPhysics()->GetStatus(&pos)) { CryLogAlways("couldn't get physics pos!"); return; } float currentSpeed = status.v.len(); if (currentSpeed>0.001f) { Vec3 currentVel = status.v; Vec3 currentPos = pos.pos; Vec3 goalDir(ZERO); assert(!_isnan(currentSpeed)); assert(!_isnan(currentVel.x) && !_isnan(currentVel.y) && !_isnan(currentVel.z)); //Just a security check if((currentPos-m_destination).len2()<(m_detonationRadius*m_detonationRadius)) { Explode(true, true, m_destination, -currentVel.normalized(), currentVel, m_targetId); return; } goalDir = m_destination - currentPos; goalDir.Normalize(); //Turn more slowly... currentVel.Normalize(); if(bDebug) { pRenderer->Draw2dLabel(50,55,2.0f,color,false, " Destination: %.3f, %.3f, %.3f",m_destination.x,m_destination.y,m_destination.z); pRenderer->Draw2dLabel(50,80,2.0f,color,false, " Current Dir: %.3f, %.3f, %.3f",currentVel.x,currentVel.y,currentVel.z); pRenderer->Draw2dLabel(50,105,2.0f,color,false," Goal Dir: %.3f, %.3f, %.3f",goalDir.x,goalDir.y,goalDir.z); } float cosine = currentVel.Dot(goalDir); cosine = CLAMP(cosine,-1.0f,1.0f); float totalAngle = RAD2DEG(cry_acosf(cosine)); assert(totalAngle>=0); if (cosine<0.99) { float maxAngle = m_turnSpeed*frameTime; if (maxAngle>totalAngle) maxAngle=totalAngle; float t=(maxAngle/totalAngle)*m_lazyness; assert(t>=0.0 && t<=1.0); goalDir = Vec3::CreateSlerp(currentVel, goalDir, t); goalDir.Normalize(); } if(bDebug) pRenderer->Draw2dLabel(50,180,2.0f,color,false,"Corrected Dir: %.3f, %.3f, %.3f",goalDir.x,goalDir.y,goalDir.z); pe_action_set_velocity action; action.v = goalDir * currentSpeed; GetEntity()->GetPhysics()->Action(&action); } } }
void CPlayerRotation::TargetAimAssistance(CWeapon* pWeapon, float& followH, float& followV, float& scale, float& _fZoomAmount, const Vec3 playerView[4]) { FUNCTION_PROFILER(GetISystem(), PROFILE_GAME); CRY_ASSERT(m_player.IsClient()); followH = 0.0f; followV = 0.0f; scale = 1.0f; float bestScale = 1.0f; const Vec3 playerFwd = playerView[1]; const Vec3 playerRgt = playerView[0]; const Vec3 playerUp = playerView[2]; const Vec3 playerPos = playerView[3]; Vec3 follow_target_pos(ZERO); float follow_vote_leader = 0.0f; float snap_vote_leader = 0.0f; Vec3 follow_target_dir(ZERO); Vec3 snap_target_dir(ZERO); EntityId follow_target_id = 0; EntityId snap_target_id = 0; CGameRules * pGameRules = g_pGame->GetGameRules(); float distance_follow_threshold_near = max(0.0f, g_pGameCVars->aim_assistMinDistance); float distance_follow_threshold_far = max(20.0f, g_pGameCVars->aim_assistMaxDistance); int playerTeam = pGameRules->GetTeam(m_player.GetEntity()->GetId()); float cloakedPlayerMultiplier = g_pGameCVars->pl_aim_cloaked_multiplier; const bool multipleTeams = pGameRules->GetTeamCount() > 0; const float fFollowFalloffDist = g_pGameCVars->aim_assistFalloffDistance + FLT_EPSILON*g_pGameCVars->aim_assistFalloffDistance; const bool playerIsScoped = m_player.GetActorStats()->isScoped; float minTurnScale, fAimAssistStrength, fMaxDistMult; if(pWeapon) { const float fZoomAmount = pWeapon->GetZoomTransition(); _fZoomAmount = fZoomAmount; const float fStrength = g_pGameCVars->aim_assistStrength; const float fStrengthIronSight = playerIsScoped ? g_pGameCVars->aim_assistStrength_SniperScope : g_pGameCVars->aim_assistStrength_IronSight; const float fDiff = fStrengthIronSight - fStrength; fAimAssistStrength = fStrength + (fZoomAmount * fDiff); const float fMinTurn = g_pGameCVars->aim_assistMinTurnScale; const float fMinTurnIronSight = playerIsScoped ? g_pGameCVars->aim_assistMinTurnScale_SniperScope : g_pGameCVars->aim_assistMinTurnScale_IronSight; const float fMinTurnDiff = fMinTurnIronSight - fMinTurn; minTurnScale = fMinTurn + (fZoomAmount * fMinTurnDiff); const float fMaxAssistDist = g_pGameCVars->aim_assistMaxDistance; const float fMaxAssistDist_Iron = playerIsScoped ? g_pGameCVars->aim_assistMaxDistance_SniperScope : g_pGameCVars->aim_assistMaxDistance_IronSight; const float fMaxAssistDistDiff = (fMaxAssistDist_Iron - fMaxAssistDist) * fZoomAmount; fMaxDistMult = (fMaxAssistDist + fMaxAssistDistDiff) * __fres(fMaxAssistDist); } else { _fZoomAmount = 0.0f; fMaxDistMult = 1.0f; fAimAssistStrength = g_pGameCVars->aim_assistStrength; minTurnScale = g_pGameCVars->aim_assistMinTurnScale; } const float falloffStartDistance = g_pGameCVars->aim_assistSlowFalloffStartDistance; const float falloffPerMeter = 1.0f / (g_pGameCVars->aim_assistSlowDisableDistance - falloffStartDistance); const TAutoaimTargets& aaTargets = g_pGame->GetAutoAimManager().GetAutoAimTargets(); const int targetCount = aaTargets.size(); float fBestTargetDistance = FLT_MAX; #if DBG_AUTO_AIM SAuxGeomRenderFlags oldFlags = gEnv->pRenderer->GetIRenderAuxGeom()->GetRenderFlags(); SAuxGeomRenderFlags newFlags = e_Def3DPublicRenderflags; newFlags.SetAlphaBlendMode(e_AlphaBlended); newFlags.SetDepthTestFlag(e_DepthTestOff); newFlags.SetCullMode(e_CullModeNone); gEnv->pRenderer->GetIRenderAuxGeom()->SetRenderFlags(newFlags); #endif for (int i = 0; i < targetCount; ++i) { const SAutoaimTarget& target = aaTargets[i]; CRY_ASSERT(target.entityId != m_player.GetEntityId()); //Skip friendly ai if(gEnv->bMultiplayer) { if(multipleTeams && (pGameRules->GetTeam(target.entityId) == playerTeam)) { continue; } } else { if (target.HasFlagSet(eAATF_AIHostile) == false) continue; distance_follow_threshold_far = fMaxDistMult * (target.HasFlagSet(eAATF_AIRadarTagged) ? g_pGameCVars->aim_assistMaxDistanceTagged : g_pGameCVars->aim_assistMaxDistance); } Vec3 targetPos = target.primaryAimPosition; Vec3 targetDistVec = (targetPos - playerPos); float distance = targetDistVec.GetLength(); if (distance <= 0.1f) continue; Vec3 dirToTarget = targetDistVec / distance; // fast reject everything behind player, too far away or too near from line of view // sort rest by angle to crosshair and distance from player float alignment = playerFwd * dirToTarget; if (alignment <= 0.0f) continue; if ((distance < distance_follow_threshold_near) || (distance > distance_follow_threshold_far)) continue; const int kAutoaimVisibilityLatency = 2; CPlayerVisTable::SVisibilityParams visParams(target.entityId); visParams.queryParams = eVQP_IgnoreGlass; if (!g_pGame->GetPlayerVisTable()->CanLocalPlayerSee(visParams, kAutoaimVisibilityLatency)) { // Since both player and target entities are ignored, and the ray still intersected something, there's something in the way. // Need to profile this and see if it's faster to do the below checks before doing the linetest. It's fairly expensive but // linetests generally are as well... - Richard continue; } #if DBG_AUTO_AIM const ColorB green(0,255,0,255); const ColorB darkgreen(0,155,0,225); gEnv->pRenderer->GetIRenderAuxGeom()->DrawLine( playerPos, darkgreen, targetPos, green); #endif const float angleDot = dirToTarget.dot(-playerRgt); const float angle = (RAD2DEG(cry_acosf(angleDot)) - 90.f); const float absAngle = fabsf(angle); const float angleDotV = playerUp.dot(dirToTarget); const float angleToTargetV = (RAD2DEG(cry_acosf(angleDotV)) - 90.f); const float absAngleV = fabsf(angleToTargetV); const float slowModifiedDistance = distance * g_pGameCVars->aim_assistSlowDistanceModifier; const float radius_slow_threshold_inner = 0.5f; const float radius_slow_threshold_outer = g_pGameCVars->aim_assistSlowThresholdOuter; const float angle_slow_threshold_inner = RAD2DEG(cry_atanf(radius_slow_threshold_inner / slowModifiedDistance)); const float angle_slow_threshold_outer = RAD2DEG(cry_atanf(radius_slow_threshold_outer / slowModifiedDistance)); float angle_slow_fractionH = clamp((absAngle - angle_slow_threshold_inner) / (angle_slow_threshold_outer - angle_slow_threshold_inner), 0.0f, 1.0f); float angle_slow_fractionV = clamp((absAngleV - angle_slow_threshold_inner) / (angle_slow_threshold_outer - angle_slow_threshold_inner), 0.0f, 1.0f); float angle_slow_fraction = max(angle_slow_fractionH, angle_slow_fractionV); const float distance_follow_fraction = clamp((distance - fFollowFalloffDist) / (distance_follow_threshold_far - fFollowFalloffDist), 0.0f, 1.0f); const float radius_follow_threshold_inner = target.innerRadius; const float radius_follow_threshold_outer = target.outerRadius; const float radius_snap = target.HasFlagSet(eAATF_AIRadarTagged) ? target.snapRadiusTagged * g_pGameCVars->aim_assistSnapRadiusTaggedScale : target.snapRadius * g_pGameCVars->aim_assistSnapRadiusScale; const float angle_follow_threshold_inner = RAD2DEG(cry_atanf(radius_follow_threshold_inner / distance)); const float angle_follow_threshold_outer = RAD2DEG(cry_atanf(radius_follow_threshold_outer / distance)); const float angle_follow_fraction = clamp((absAngle - angle_follow_threshold_inner) / (angle_follow_threshold_outer - angle_follow_threshold_inner), 0.0f, 1.0f); const float angle_follow_fractionV = clamp((absAngleV - angle_follow_threshold_inner) / (angle_follow_threshold_outer - angle_follow_threshold_inner), 0.0f, 1.0f); const float worst_follow_fraction = (float)__fsel(angle_follow_fraction - angle_follow_fractionV, angle_follow_fraction, angle_follow_fractionV); float follow_fraction = ((1.0f - worst_follow_fraction) * (1.0f - distance_follow_fraction)); float follow_vote = follow_fraction; //clamp the lower bound of the distance_slow_modifier so it can't be lower than the angle slow fraction // which prevents close but off-centre targets slowing us too much const float distance_slow_modifier = clamp( 1.0f - ((distance - falloffStartDistance) * falloffPerMeter), angle_slow_fraction, 1.0f); const float fCombinedModifier = angle_slow_fraction * distance_slow_modifier; fBestTargetDistance = (float)__fsel(fCombinedModifier - bestScale, fBestTargetDistance, distance); bestScale = min(fCombinedModifier, bestScale); if (follow_vote > follow_vote_leader) { follow_vote_leader = follow_vote; //m_follow_target_id only gets set after the loop -> this won't get hit when a target is selected // as a follow target for the first time. This doesn't need to be in the loop. if ( m_follow_target_id == target.entityId) { const Vec3 follow_target_dir_local = m_follow_target_dir; Vec3 target_rgt = playerRgt; Vec3 target_up = target_rgt.cross(follow_target_dir_local); target_rgt = follow_target_dir_local.cross(target_up); target_rgt.Normalize(); target_up.Normalize(); float alignH = dirToTarget * -target_rgt; float alignV = dirToTarget.z - follow_target_dir_local.z; float angleH = min(fabsf(alignH * fAimAssistStrength), fabsf(angleDot)); float angleV = min(fabsf(alignV * fAimAssistStrength), fabsf(angleDotV)); followH = follow_fraction * (float)__fsel(angleDot, angleH, -angleH); followV = follow_fraction * (float)__fsel(angleDotV, angleV, -angleV); follow_vote_leader += 0.05f; // anti oscillation between different targets follow_target_pos = targetPos; } follow_target_id = target.entityId; snap_target_id = target.entityId; follow_target_dir = dirToTarget; snap_target_dir = PickBestSnapDirection(playerPos, playerFwd, target); } else if (!follow_target_id && (radius_snap > 0.0f)) { Lineseg lineSegment; lineSegment.start = playerPos; lineSegment.end = playerPos + (playerFwd * (distance + radius_snap)); Sphere sphere; sphere.center = targetPos; sphere.radius = radius_snap; Vec3 intersectionPoint; if (Intersect::Lineseg_SphereFirst(lineSegment, sphere, intersectionPoint)) { float t = 0.0f; const float snap_fraction = 1.0f - (Distance::Point_Lineseg(targetPos, lineSegment, t) * (float)__fres(radius_snap)); if (snap_fraction > snap_vote_leader) { snap_vote_leader = snap_fraction; snap_target_id = target.entityId; snap_target_dir = PickBestSnapDirection(playerPos, playerFwd, target); } } } } #if DBG_AUTO_AIM if ((!follow_target_pos.IsZeroFast()) && (g_pGameCVars->pl_targeting_debug != 0)) { float radius_inner = 0.30f; float radius_outer = 0.33f; ColorB colorInner(255,255,0,0x40); ColorB colorOuter(255,255,0,0x40); DrawDisc(follow_target_pos, follow_target_dir, radius_inner, radius_outer, colorInner, colorOuter); } gEnv->pRenderer->GetIRenderAuxGeom()->SetRenderFlags(oldFlags); #endif m_follow_target_id = follow_target_id; m_follow_target_dir = follow_target_dir; //IMPORTANT: Apply the minimum-distance scaling of the slowdown _after_ calculating the slowdown for the best target // as we want to help the player aim at the nearest target, and modifying the slowdown multiplier prior to this // could result in a different target being selected const float fSlowDownProximityFadeInBand = (g_pGameCVars->aim_assistSlowStopFadeinDistance - g_pGameCVars->aim_assistSlowStartFadeinDistance) + FLT_EPSILON; float fSlowDownProximityScale = (fBestTargetDistance - g_pGameCVars->aim_assistSlowStartFadeinDistance) / fSlowDownProximityFadeInBand; Limit(fSlowDownProximityScale, 0.0f, 1.0f); float fInvBestScale = (1.0f - bestScale) * fSlowDownProximityScale; bestScale = 1.0f - fInvBestScale; scale = minTurnScale + ((1.0f - minTurnScale) * bestScale); UpdateCurrentSnapTarget(snap_target_id, snap_target_dir); }
static void GetTalosInput(CPlayerRotation * pPlayerRotation, const CPlayer& rPlayer, float& x, float& z, const Vec3 playerView[4], float fFrameTime) { //Do I need to reproject the view to actually get the positioning correct? Shouldn't be. const Vec3 playerFwd = playerView[1]; const Vec3 playerRgt = playerView[0]; const Vec3 playerUp = playerView[2]; const Vec3 playerViewPos = playerView[3]; Vec3 playerPos = playerViewPos; IPhysicalEntity * pPhysicalEntity = rPlayer.GetEntity()->GetPhysics(); if(pPhysicalEntity) { pe_status_dynamics dyn_status; pPhysicalEntity->GetStatus(&dyn_status); playerPos = playerViewPos + (dyn_status.v * fFrameTime * 2.0f); } Vec3 follow_target_dir(ZERO); EntityId follow_target_id = 0; static EntityId s_follow_target_id = 0; CGameRules * pGameRules = g_pGame->GetGameRules(); int playerTeam = pGameRules->GetTeam(rPlayer.GetEntity()->GetId()); float cloakedPlayerMultiplier = g_pGameCVars->pl_aim_cloaked_multiplier; const bool multipleTeams = pGameRules->GetTeamCount() > 0; const TAutoaimTargets& aaTargets = g_pGame->GetAutoAimManager().GetAutoAimTargets(); const int targetCount = aaTargets.size(); float fBestTargetDistance = FLT_MAX; for (int i = 0; i < targetCount; ++i) { const SAutoaimTarget& target = aaTargets[i]; if(multipleTeams && (pGameRules->GetTeam(target.entityId) == playerTeam)) { continue; } Vec3 targetPos = target.secondaryAimPosition; IEntity * pEntity = gEnv->pEntitySystem->GetEntity(target.entityId); if(pEntity) { IPhysicalEntity * pPhysicalEntity = pEntity->GetPhysics(); if(pPhysicalEntity) { pe_status_dynamics dyn_status; pPhysicalEntity->GetStatus(&dyn_status); targetPos = targetPos + (dyn_status.v * fFrameTime); } } Vec3 targetDistVec = (targetPos - playerPos); float distance = targetDistVec.GetLength(); if (distance <= 0.01f) continue; Vec3 dirToTarget = targetDistVec / distance; // fast reject everything behind player, too far away or too near from line of view // sort rest by angle to crosshair and distance from player const int kAutoaimVisibilityLatency = 1; if (!g_pGame->GetPlayerVisTable()->CanLocalPlayerSee(target.entityId, kAutoaimVisibilityLatency)) { // Since both player and target entities are ignored, and the ray still intersected something, there's something in the way. // Need to profile this and see if it's faster to do the below checks before doing the linetest. It's fairly expensive but // linetests generally are as well... - Richard continue; } const float angleDot = dirToTarget.dot(-playerRgt); const float angle = (RAD2DEG(cry_acosf(angleDot)) - 90.f); const float absAngle = fabsf(angle); const float angleDotV = playerUp.dot(dirToTarget); const float angleToTargetV = (RAD2DEG(cry_acosf(angleDotV)) - 90.f); const float absAngleV = fabsf(angleToTargetV); if ( s_follow_target_id == target.entityId ) { follow_target_id = target.entityId; follow_target_dir = dirToTarget; break; } else if(distance < fBestTargetDistance) { fBestTargetDistance = distance; follow_target_id = target.entityId; follow_target_dir = dirToTarget; } } if(follow_target_id != 0) { //Player up is the normal of the plane that we are rotating around - (Correct? Or do we want to project both the player direction and the target direction onto the X/Y plane?) Vec3 vProjectedTargetHorz = playerUp.cross(follow_target_dir.cross(playerUp)); Vec3 vProjectedTargetVert = playerRgt.cross(follow_target_dir.cross(playerRgt)); float horzDot = vProjectedTargetHorz.GetNormalized().dot(playerFwd); float vertDot = vProjectedTargetVert.GetNormalized().dot(playerFwd); const float directionDotHorz = follow_target_dir.dot(playerRgt); const float directionDotVert = follow_target_dir.dot(playerUp); const float angle = cry_acosf(horzDot); const float angleToTargetV = cry_acosf(vertDot); const float fHorzFinalAngle = (float)__fsel(directionDotHorz, -angle, angle); const float fVertFinalAngle = (float)__fsel(directionDotVert, angleToTargetV, -angleToTargetV); //CryWatch("Angle to target: %.6f", RAD2DEG(angle)); //CryWatch("Final Angle to target: %.6f", RAD2DEG(fHorzFinalAngle)); x = x + fVertFinalAngle; z = z + fHorzFinalAngle; } s_follow_target_id = follow_target_id; return; }