float GetQuatAbsAngle(const Quat& q) { //float fwd = q.GetColumn1().y; float fwd = q.GetFwdY(); fwd = clamp_tpl(fwd, -1.0f, 1.0f); return acos_tpl(fwd); }
inline float AngleBetweenVectors( const Vec3 &v1,const Vec3 &v2 ) { float a = acos_tpl(v1.Dot(v2)); Vec3 r = v1.Cross(v2); if (r.z < 0) a = -a; return a; }
//------------------------------------------------------------------------ bool CGunTurret::GetTargetAngles(const Vec3 &targetPos, float &z, float &x) const { // turret rotation reachability check Vec3 weaponPos = GetWeaponPos(); Vec3 targetDir = targetPos - weaponPos; if(targetDir.len2() < minLenSqr) return false; // local dir to target Vec3 targetDirLocal = GetEntity()->GetWorldTM().GetInverted().TransformVector(targetDir); // angle around z Vec3 targetDirZ(targetDirLocal.x, targetDirLocal.y, 0.f); if(targetDirZ.len2() < minLenSqr) z = 0.f; else { targetDirZ.Normalize(); float cosZ = max(-1.f, min(1.f, FORWARD_DIRECTION * targetDirZ)); z = acos_tpl(cosZ) * -sgnnz(targetDirZ.x); if(z<0.0f) z+=gf_PI2; } // angle around x Vec3 targetDirX(targetDirLocal); Vec3 targetDirBase(targetDirLocal.x, targetDirLocal.y, 0); if(targetDirBase.len2() < minLenSqr) x = 0.0f; else { targetDirX.Normalize(); targetDirBase.Normalize(); float cosX = max(-1.f, min(1.f, targetDirBase * targetDirX)); x = acos_tpl(cosX) * sgnnz(targetDirX.z); } return true; }
bool operator() (const SSpectacularKillAnimation& killAnim) const { const SSpectacularKillCVars& skCVars = g_pGameCVars->g_spectacularKill; const CActor* pOwner = m_spectacularKill.m_pOwner; // 0. the anim shouldn't be redundant if (((gEnv->pTimer->GetFrameStartTime().GetSeconds() - s_lastKillInfo.timeStamp) <= skCVars.minTimeBetweenSameKills) && (killAnim.killerAnimation.compare(s_lastKillInfo.killerAnim))) { SK_DEBUG_LOG("GetValidAnim - %s is not valid: This animation was last played %.1f ago, a minimum time of %.1f is required", killAnim.killerAnimation.c_str(), (gEnv->pTimer->GetFrameStartTime().GetSeconds() - s_lastKillInfo.timeStamp), skCVars.minTimeBetweenSameKills); return true; } // 1. the killer needs to be within a certain distance from the target IEntity* pTargetEntity = m_pTarget->GetEntity(); IEntity* pKillerEntity = pOwner->GetEntity(); const QuatT& killerTransform = pOwner->GetAnimatedCharacter()->GetAnimLocation(); const QuatT& targetTransform = m_pTarget->GetAnimatedCharacter()->GetAnimLocation(); const Vec3& vKillerPos = killerTransform.t; const Vec3& vTargetPos = targetTransform.t; Vec2 vKillerToTarget = Vec2(vTargetPos) - Vec2(vKillerPos); float distance = vKillerToTarget.GetLength(); const float optimalDistance = killAnim.optimalDist; if ((optimalDistance > 0.0f) && (fabs_tpl(distance - optimalDistance) > skCVars.maxDistanceError)) { #ifndef _RELEASE if (g_pGameCVars->g_spectacularKill.debug > 1) { // visually shows why it failed IPersistantDebug* pPersistantDebug = m_spectacularKill.BeginPersistantDebug(); const float fConeHeight = killAnim.optimalDist + skCVars.maxDistanceError; pPersistantDebug->AddPlanarDisc(vTargetPos, killAnim.optimalDist - skCVars.maxDistanceError, killAnim.optimalDist + skCVars.maxDistanceError, Col_Coral, 6.0f); pPersistantDebug->AddLine(vKillerPos, vKillerPos + Vec3(0.f, 0.f, 5.0f), Col_Red, 6.f); } SK_DEBUG_LOG("GetValidAnim - %s is not valid: Distance between actors should be %.2f, is %.2f (max error is %f)", killAnim.killerAnimation.c_str(), optimalDistance, distance, skCVars.maxDistanceError); #endif return true; } // 2. The killer needs to be facing the target within cosLookToConeHalfAngleRadians angle Vec2 vKillerDir(killerTransform.GetColumn1()); // In decoupled catchup mode we need the animated character's orientation vKillerDir.Normalize(); if (vKillerToTarget.GetNormalizedSafe().Dot(vKillerDir) <= skCVars.minKillerToTargetDotProduct) { SK_DEBUG_LOG("GetValidAnim - %s is not valid: Killer is not looking within %.2f degrees towards the target", killAnim.killerAnimation.c_str(), RAD2DEG(acos_tpl(skCVars.minKillerToTargetDotProduct) * 2.0f)); return true; } // 3. If specified, the killer needs to be within a certain angle range from a given reference orientation from the target // e.g. Specifying referenceAngle 180 means using the back of the target as the center of the angle range // (imagine it as a cone) where the killer has to be for the kill to be valid if (killAnim.targetToKillerAngle >= 0.f) { const float referenceAngle = killAnim.targetToKillerAngle; // Find the reference vector which will be the center of the allowed angle range Vec2 vTargetDir(targetTransform.GetColumn1()); vTargetDir.Normalize(); // 2D rotation Vec2 vReferenceDir((vTargetDir.x * cosf(referenceAngle)) - (vTargetDir.y * sinf(referenceAngle)), (vTargetDir.y * cosf(referenceAngle)) + (vTargetDir.x * sinf(referenceAngle))); if (vKillerToTarget.GetNormalizedSafe().Dot(-vReferenceDir) <= killAnim.targetToKillerMinDot) { #ifndef _RELEASE if (g_pGameCVars->g_spectacularKill.debug > 1) { // visually shows why it failed IPersistantDebug* pPersistantDebug = m_spectacularKill.BeginPersistantDebug(); const float fConeHeight = killAnim.optimalDist + skCVars.maxDistanceError; pPersistantDebug->AddCone(vTargetPos + (vReferenceDir * fConeHeight), -vReferenceDir, killAnim.targetToKillerMinDot * fConeHeight * 2.0f, fConeHeight, Col_Coral, 6.f); pPersistantDebug->AddLine(vKillerPos, vKillerPos + Vec3(0.f, 0.f, 5.0f), Col_Red, 6.f); } float targetToKillerDot = vTargetDir.GetNormalizedSafe().Dot(-vKillerToTarget); SK_DEBUG_LOG("GetValidAnim - %s is not valid: Killer is not within a %.2f degrees cone centered on the target's %.2f degrees. Killer is at %.2f angles respect the target", killAnim.killerAnimation.c_str(), RAD2DEG(acos_tpl(killAnim.targetToKillerMinDot) * 2.0f), RAD2DEG(killAnim.targetToKillerAngle), RAD2DEG(acos_tpl(targetToKillerDot))); #endif return true; } } SK_DEBUG_LOG("GetValidAnim - %s is valid", killAnim.killerAnimation.c_str()); return false; }
//------------------------------------------------------------------------ void CGameRulesMPDamageHandling::SvOnExplosion(const ExplosionInfo &explosionInfo, const CGameRules::TExplosionAffectedEntities& affectedEntities) { // SinglePlayer.lua 1721 IActorSystem* pActorSystem = g_pGame->GetIGameFramework()->GetIActorSystem(); float totalDamage = 0.f; // calculate damage for each entity CGameRules::TExplosionAffectedEntities::const_iterator it = affectedEntities.begin(); for (; it != affectedEntities.end(); ++it) { bool success = false; IEntity* entity = it->first; float obstruction = 1.0f - it->second; bool incone=true; if (explosionInfo.angle > 0 && explosionInfo.angle < 2*3.1415f) { Vec3 explosionEntityPos = entity->GetWorldPos(); Vec3 entitypos = explosionEntityPos; float ha = explosionInfo.angle * 0.5f; Vec3 edir = entitypos - explosionInfo.pos; edir.normalize(); float dot = 1; if (edir != Vec3(ZERO)) { dot = edir.dot(explosionInfo.dir); } float angle = abs(acos_tpl(dot)); if (angle > ha) { incone = false; } } if (incone && gEnv->bServer) { float damage = explosionInfo.damage; damage = floor_tpl(0.5f + CalcExplosionDamage(entity, explosionInfo, obstruction)); bool dead = false; HitInfo explHit; Vec3 dir = entity->GetWorldPos() - explosionInfo.pos; explHit.pos = explosionInfo.pos; explHit.radius = explosionInfo.radius; explHit.partId = -1; dir.Normalize(); explHit.targetId = entity->GetId(); explHit.weaponId = explosionInfo.weaponId; explHit.shooterId = explosionInfo.shooterId; explHit.projectileId = explosionInfo.projectileId; explHit.projectileClassId = explosionInfo.projectileClassId; uint16 weaponClass = ~uint16(0); const IEntity* pWeaponEntity = gEnv->pEntitySystem->GetEntity(explosionInfo.weaponId); if (pWeaponEntity) { g_pGame->GetIGameFramework()->GetNetworkSafeClassId(weaponClass, pWeaponEntity->GetClass()->GetName()); } explHit.weaponClassId = weaponClass; explHit.material = 0; explHit.damage = damage; explHit.type = explosionInfo.type; explHit.hitViaProxy = explosionInfo.explosionViaProxy; explHit.dir = dir; explHit.normal = -dir; //set normal to reverse of direction to avoid backface cull of damage if (explHit.damage > 0.0f || explosionInfo.damage == 0.f) { CActor* pActor = static_cast<CActor*>(pActorSystem->GetActor(entity->GetId())); if (pActor) { const float damageMultiplier = pActor->GetBodyExplosionDamageMultiplier(explHit); explHit.damage *= damageMultiplier; // Protect players who are currently shielded if(pActor->IsPlayer() && static_cast<CPlayer*>(pActor)->ShouldFilterOutExplosion(explHit)) { explHit.damage = 0.0f; } } else { CInteractiveObjectEx* pInteractiveObject = static_cast<CInteractiveObjectEx*>(g_pGame->GetIGameFramework()->QueryGameObjectExtension(entity->GetId(), "InteractiveObjectEx")); if(pInteractiveObject) { pInteractiveObject->OnExploded(explHit.pos); } } if(!explosionInfo.explosionViaProxy) { if(pActor) { if(!pActor->IsFriendlyEntity(explHit.shooterId)) { totalDamage += damage; } } else { IVehicle* pVehicle = g_pGame->GetIGameFramework()->GetIVehicleSystem()->GetVehicle(entity->GetId()); if(pVehicle) { IActor* pDriver = pVehicle->GetDriver(); int seatNum = 0; int numSeats = pVehicle->GetSeatCount(); while(!pDriver && seatNum < numSeats) { IVehicleSeat* pSeat = pVehicle->GetSeatById(seatNum++); EntityId passengerId = pSeat ? pSeat->GetPassenger(true) : 0; pDriver = pActorSystem->GetActor(passengerId); } if(pDriver) { CActor* pDriverActor = static_cast<CActor*>(pDriver); if(!pDriverActor->IsFriendlyEntity(explHit.shooterId)) { totalDamage += damage; } } } } } m_pGameRules->ServerHit(explHit); } } } if (totalDamage > 0.f) { IGameRulesPlayerStatsModule* pPlayStatsMod = m_pGameRules->GetPlayerStatsModule(); if(pPlayStatsMod) { pPlayStatsMod->ProcessSuccessfulExplosion(explosionInfo.shooterId, totalDamage, explosionInfo.projectileClassId); } } }
void CHUD::AutoSnap() { const float fRadius = 25.0f; static Vec2 s_vCursor = Vec2(0,0); if(fabsf(m_fAutosnapCursorControllerX)>0.1 || fabsf(m_fAutosnapCursorControllerY)>0.1) { s_vCursor.x = m_fAutosnapCursorControllerX * 30.0f; s_vCursor.y = m_fAutosnapCursorControllerY * 30.0f; } else { s_vCursor.x = m_fAutosnapCursorRelativeX; s_vCursor.y = m_fAutosnapCursorRelativeY; } if(m_bOnCircle && s_vCursor.GetLength() < fRadius*0.5f) { m_fAutosnapCursorRelativeX = 0; m_fAutosnapCursorRelativeY = 0; m_bOnCircle = false; } if(s_vCursor.GetLength() > fRadius) { s_vCursor.NormalizeSafe(); m_fAutosnapCursorRelativeX = s_vCursor.x*fRadius; m_fAutosnapCursorRelativeY = s_vCursor.y*fRadius; m_bOnCircle = true; } const char* autosnapItem = "Center"; if(m_bOnCircle) { Vec2 vCursor = s_vCursor; vCursor.NormalizeSafe(); float fAngle; if(vCursor.y < 0) { fAngle = RAD2DEG(acos_tpl(vCursor.x)); } else { fAngle = RAD2DEG(gf_PI2-acos_tpl(vCursor.x)); } char szAngle[32]; sprintf(szAngle,"%f",-fAngle+90.0f); /* ColorB col(255,255,255,255); int iW=m_pRenderer->GetWidth(); int iH=m_pRenderer->GetHeight(); m_pRenderer->Set2DMode(true,iW,iH); m_pRenderer->GetIRenderAuxGeom()->DrawLine(Vec3(iW/2,iH/2,0),col,Vec3(iW/2+vCursor.x*100,iH/2+vCursor.y*100,0),col,5); m_pRenderer->Set2DMode(false,0,0); */ m_animQuickMenu.CheckedSetVariable("Root.QuickMenu.Circle.Indicator._rotation",szAngle); if(fAngle >= 342 || fAngle < 52) { autosnapItem = "Strength"; } else if(fAngle >= 52 && fAngle < 128) { autosnapItem = "Speed"; } else if(fAngle >= 128 && fAngle < 205) { autosnapItem = "Defense"; } else if(fAngle >= 205 && fAngle < 260) { autosnapItem = "Weapon"; } else if(fAngle >= 260 && fAngle < 342) { autosnapItem = "Cloak"; } } m_animQuickMenu.CheckedInvoke("Root.QuickMenu.setAutosnapItem", autosnapItem); }
void CHUD::UpdateMissionObjectiveIcon(EntityId objective, int friendly, FlashOnScreenIcon iconType, bool forceNoOffset, Vec3 rotationTarget) { IEntity *pObjectiveEntity = GetISystem()->GetIEntitySystem()->GetEntity(objective); if(!pObjectiveEntity) return; AABB box; pObjectiveEntity->GetWorldBounds(box); Vec3 vWorldPos = Vec3(0,0,0); SEntitySlotInfo info; int slotCount = pObjectiveEntity->GetSlotCount(); for(int i=0; i<slotCount; ++i) { if (pObjectiveEntity->GetSlotInfo(i, info)) { if (info.pCharacter) { int16 id = info.pCharacter->GetISkeletonPose()->GetJointIDByName("objectiveicon"); if (id >= 0) { //vPos = pCharacter->GetISkeleton()->GetHelperPos(helper); vWorldPos = info.pCharacter->GetISkeletonPose()->GetAbsJointByID(id).t; if (!vWorldPos.IsZero()) { vWorldPos = pObjectiveEntity->GetSlotWorldTM(i).TransformPoint(vWorldPos); break; } } } } } if(vWorldPos == Vec3(0,0,0)) vWorldPos = pObjectiveEntity->GetWorldPos(); if(!forceNoOffset) vWorldPos.z += 2.0f; Vec3 vEntityScreenSpace; m_pRenderer->ProjectToScreen( vWorldPos.x, vWorldPos.y, vWorldPos.z, &vEntityScreenSpace.x, &vEntityScreenSpace.y, &vEntityScreenSpace.z); Vec3 vEntityTargetSpace; bool useTarget = false; if(!rotationTarget.IsZero()) { m_pRenderer->ProjectToScreen( rotationTarget.x, rotationTarget.y, rotationTarget.z, &vEntityTargetSpace.x, &vEntityTargetSpace.y, &vEntityTargetSpace.z); useTarget = true; } CActor *pActor = (CActor*)(gEnv->pGame->GetIGameFramework()->GetClientActor()); bool bBack = false; if (IMovementController *pMV = pActor->GetMovementController()) { SMovementState state; pMV->GetMovementState(state); Vec3 vLook = state.eyeDirection; Vec3 vDir = vWorldPos - pActor->GetEntity()->GetWorldPos(); float fDot = vLook.Dot(vDir); if(fDot<0.0f) bBack = true; } bool bLeft(false), bRight(false), bTop(false), bBottom(false); if(vEntityScreenSpace.z > 1.0f && bBack) { Vec2 vCenter(50.0f, 50.0f); Vec2 vTarget(-vEntityScreenSpace.x, -vEntityScreenSpace.y); Vec2 vScreenDir = vTarget - vCenter; vScreenDir = vCenter + (100.0f * vScreenDir.NormalizeSafe()); vEntityScreenSpace.y = vScreenDir.y; vEntityScreenSpace.x = vScreenDir.x; useTarget = false; } if(vEntityScreenSpace.x < 2.0f) { bLeft = true; vEntityScreenSpace.x = 2.0f; useTarget = false; } if(vEntityScreenSpace.x > 98.0f) { bRight = true; vEntityScreenSpace.x = 98.0f; useTarget = false; } if(vEntityScreenSpace.y < 2.01f) { bTop = true; vEntityScreenSpace.y = 2.0f; useTarget = false; } if(vEntityScreenSpace.y > 97.99f) { bBottom = true; vEntityScreenSpace.y = 98.0f; useTarget = false; } float fScaleX = 0.0f; float fScaleY = 0.0f; float fHalfUselessSize = 0.0f; GetProjectionScale(&m_animMissionObjective,&fScaleX,&fScaleY,&fHalfUselessSize); if(bLeft && bTop) { iconType = eOS_TopLeft; } else if(bLeft && bBottom) { iconType = eOS_BottomLeft; } else if(bRight && bTop) { iconType = eOS_TopRight; } else if(bRight && bBottom) { iconType = eOS_BottomRight; } else if(bLeft) { iconType = eOS_Left; } else if(bRight) { iconType = eOS_Right; } else if(bTop) { iconType = eOS_Top; } else if(bBottom) { iconType = eOS_Bottom; } float rotation = 0.0f; if(useTarget) { float diffX = (vEntityScreenSpace.x*fScaleX+fHalfUselessSize+16.0f) - (vEntityTargetSpace.x*fScaleX+fHalfUselessSize+16.0f); float diffY = (vEntityScreenSpace.y*fScaleY) - (vEntityTargetSpace.y*fScaleY); Vec2 dir(diffX, diffY); dir.NormalizeSafe(); float fAngle; if(dir.y < 0) { fAngle = RAD2DEG(acos_tpl(dir.x)); } else { fAngle = RAD2DEG(gf_PI2-acos_tpl(dir.x)); } rotation = fAngle - 90.0f; } int iMinDist = g_pGameCVars->hud_onScreenNearDistance; int iMaxDist = g_pGameCVars->hud_onScreenFarDistance; float fMinSize = g_pGameCVars->hud_onScreenNearSize; float fMaxSize = g_pGameCVars->hud_onScreenFarSize; CActor *pPlayerActor = static_cast<CActor *>(gEnv->pGame->GetIGameFramework()->GetClientActor()); float fDist = (vWorldPos-pPlayerActor->GetEntity()->GetWorldPos()).len(); float fSize = 1.0; if(fDist<=iMinDist) { fSize = fMinSize; } else if(fDist>=iMaxDist) { fSize = fMaxSize; } else if(iMaxDist>iMinDist) { float fA = ((float)iMaxDist - fDist); float fB = (float)(iMaxDist - iMinDist); float fC = (fMinSize - fMaxSize); fSize = ((fA / fB) * fC) + fMaxSize; } float centerX = 50.0; float centerY = 50.0; m_missionObjectiveNumEntries += FillUpMOArray(&m_missionObjectiveValues, objective, vEntityScreenSpace.x*fScaleX+fHalfUselessSize+16.0f, vEntityScreenSpace.y*fScaleY, iconType, friendly, (int)fDist, fSize*fSize, -rotation); bool nearCenter = (pow(centerX-vEntityScreenSpace.x+1.5f,2.0f)+pow(centerY-vEntityScreenSpace.y+2.5f,2.0f))<16.0f; if(nearCenter && (gEnv->bMultiplayer || m_pHUDScopes->IsBinocularsShown())) { m_objectiveNearCenter = objective; } }
//------------------------------------------------------------------ void CLam::UpdateTPLaser(float frameTime, CItem* parent) { FUNCTION_PROFILER(GetISystem(), PROFILE_GAME); const int frameId = gEnv->pRenderer->GetFrameID(); if (s_lastUpdateFrameId != frameId) { // Check how many LAMs to update this frame. float dt = frameTime; // + s_laserUpdateTimeError; const int n = s_lasers.size(); int nActive = 0; for (int i = 0; i < n; ++i) { if (!s_lasers[i]->IsLaserActivated() && !s_lasers[i]->IsLightActivated()) continue; nActive++; } float updatedPerSecond = (nActive / LASER_UPDATE_TIME) + s_laserUpdateTimeError; int updateCount = (int)floorf(updatedPerSecond * dt); if(dt==0.0f) s_laserUpdateTimeError = 0.0f; else s_laserUpdateTimeError = updatedPerSecond - updateCount/dt; s_curLaser %= n; for (int i = 0, j = 0; i < n && j < updateCount ; ++i) { s_curLaser = (s_curLaser + 1) % n; if (!s_lasers[s_curLaser]->IsLaserActivated() && !s_lasers[s_curLaser]->IsLightActivated()) continue; s_lasers[s_curLaser]->SetAllowUpdate(); ++j; } s_lastUpdateFrameId = frameId; } IEntity* pRootEnt = GetEntity(); if (!pRootEnt) return; IEntity *pLaserEntity = m_pEntitySystem->GetEntity(m_pLaserEntityId); // if(!pLaserEntity) // return; const CCamera& camera = gEnv->pRenderer->GetCamera(); Vec3 lamPos = pRootEnt->GetWorldPos(); //pLaserEntity->GetParent()->GetWorldPos(); Vec3 dir = pRootEnt->GetWorldRotation().GetColumn1(); //pLaserEntity->GetParent()->GetWorldRotation().GetColumn1(); bool charNotVisible = false; float dsg1Scale = 1.0f; //If character not visible, laser is not correctly updated if(parent) { if(CActor* pOwner = parent->GetOwnerActor()) { ICharacterInstance* pCharacter = pOwner->GetEntity()->GetCharacter(0); if(pCharacter && !pCharacter->IsCharacterVisible()) charNotVisible = true; } if(parent->GetEntity()->GetClass()==CItem::sDSG1Class) dsg1Scale = 3.0f; } // if (!pLaserEntity->GetParent()) // return; Vec3 hitPos(0,0,0); float laserLength = 0.0f; // HACK??: Use player movement controller locations, or else the laser // pops all over the place when character out of the screen. CActor *pActor = parent->GetOwnerActor(); if (pActor && (!pActor->IsPlayer() || charNotVisible)) { if (IMovementController* pMC = pActor->GetMovementController()) { SMovementState state; pMC->GetMovementState(state); if(!charNotVisible) lamPos = state.weaponPosition; else { float oldZPos = lamPos.z; lamPos = state.weaponPosition; if(m_lastZPos>0.0f) lamPos.z = m_lastZPos; //Stabilize somehow z position (even if not accurate) else lamPos.z = oldZPos; } const float angleMin = DEG2RAD(3.0f); const float angleMax = DEG2RAD(7.0f); const float thr = cosf(angleMax); float dot = dir.Dot(state.aimDirection); if (dot > thr) { float a = acos_tpl(dot); float u = 1.0f - clamp((a - angleMin) / (angleMax - angleMin), 0.0f, 1.0f); dir = dir + u * (state.aimDirection - dir); dir.Normalize(); } } } if(!charNotVisible) m_lastZPos = lamPos.z; lamPos += (dir*0.10f); if (m_allowUpdate) { m_allowUpdate = false; IPhysicalEntity* pSkipEntity = NULL; if(parent->GetOwner()) pSkipEntity = parent->GetOwner()->GetPhysics(); const float range = m_lamparams.laser_range[eIGS_ThirdPerson]*dsg1Scale; // Use the same flags as the AI system uses for visbility. const int objects = ent_terrain|ent_static|ent_rigid|ent_sleeping_rigid|ent_independent; //ent_living; const int flags = (geom_colltype_ray << rwi_colltype_bit) | rwi_colltype_any | (10 & rwi_pierceability_mask) | (geom_colltype14 << rwi_colltype_bit); ray_hit hit; if (gEnv->pPhysicalWorld->RayWorldIntersection(lamPos, dir*range, objects, flags, &hit, 1, &pSkipEntity, pSkipEntity ? 1 : 0)) { laserLength = hit.dist; m_lastLaserHitPt = hit.pt; m_lastLaserHitSolid = true; } else { m_lastLaserHitSolid = false; m_lastLaserHitPt = lamPos + dir * range; laserLength = range + 0.1f; } // Hit near plane if (dir.Dot(camera.GetViewdir()) < 0.0f) { Plane nearPlane; nearPlane.SetPlane(camera.GetViewdir(), camera.GetPosition()); nearPlane.d -= camera.GetNearPlane()+0.15f; Ray ray(lamPos, dir); Vec3 out; m_lastLaserHitViewPlane = false; if (Intersect::Ray_Plane(ray, nearPlane, out)) { float dist = Distance::Point_Point(lamPos, out); if (dist < laserLength) { laserLength = dist; m_lastLaserHitPt = out; m_lastLaserHitSolid = true; m_lastLaserHitViewPlane = true; } } } hitPos = m_lastLaserHitPt; } else { laserLength = Distance::Point_Point(m_lastLaserHitPt, lamPos); hitPos = lamPos + dir * laserLength; } if (m_smoothLaserLength < 0.0f) m_smoothLaserLength = laserLength; else { if (laserLength < m_smoothLaserLength) m_smoothLaserLength = laserLength; else m_smoothLaserLength += (laserLength - m_smoothLaserLength) * min(1.0f, 10.0f * frameTime); } float laserAIRange = 0.0f; if (m_laserActivated && pLaserEntity) { // Orient the laser towards the point point. Matrix34 parentTMInv; parentTMInv = pRootEnt->GetWorldTM().GetInverted(); Vec3 localDir = parentTMInv.TransformPoint(hitPos); float finalLaserLen = localDir.NormalizeSafe(); Matrix33 rot; rot.SetIdentity(); rot.SetRotationVDir(localDir); pLaserEntity->SetLocalTM(rot); laserAIRange = finalLaserLen; const float assetLength = 2.0f; finalLaserLen = CLAMP(finalLaserLen,0.01f,m_lamparams.laser_max_len*dsg1Scale); float scale = finalLaserLen / assetLength; // Scale the laser based on the distance. if (m_laserEffectSlot >= 0) { Matrix33 scl; scl.SetIdentity(); scl.SetScale(Vec3(1,scale,1)); pLaserEntity->SetSlotLocalTM(m_laserEffectSlot, scl); } if (m_dotEffectSlot >= 0) { if (m_lastLaserHitSolid) { Matrix34 mt = Matrix34::CreateTranslationMat(Vec3(0,finalLaserLen,0)); if(m_lastLaserHitViewPlane) mt.Scale(Vec3(0.2f,0.2f,0.2f)); pLaserEntity->SetSlotLocalTM(m_dotEffectSlot, mt); } else { Matrix34 scaleMatrix; scaleMatrix.SetIdentity(); scaleMatrix.SetScale(Vec3(0.001f,0.001f,0.001f)); pLaserEntity->SetSlotLocalTM(m_dotEffectSlot, scaleMatrix); } } } float lightAIRange = 0.0f; if (m_lightActivated) { float range = clamp(m_smoothLaserLength, 0.5f, m_lamparams.light_range[eIGS_ThirdPerson]); lightAIRange = range * 1.5f; if (m_lightID[eIGS_ThirdPerson] && m_smoothLaserLength > 0.0f) { CItem* pLightEffect = this; if (IItem *pOwnerItem = m_pItemSystem->GetItem(GetParentId())) pLightEffect = (CItem *)pOwnerItem; pLightEffect->SetLightRadius(range, m_lightID[eIGS_ThirdPerson]); } } if (laserAIRange > 0.0001f || lightAIRange > 0.0001f) UpdateAILightAndLaser(lamPos, dir, lightAIRange, m_lamparams.light_fov[eIGS_ThirdPerson], laserAIRange); }
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_tpl(currentDir.Dot(desiredAimDirection), -1.0f, 1.0f); const float reqAngle = acos_tpl(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_tpl(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 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(acos_tpl(angleDot)) - 90.f); const float absAngle = fabsf(angle); const float angleDotV = playerUp.dot(dirToTarget); const float angleToTargetV = (RAD2DEG(acos_tpl(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(atan_tpl(radius_slow_threshold_inner / slowModifiedDistance)); const float angle_slow_threshold_outer = RAD2DEG(atan_tpl(radius_slow_threshold_outer / slowModifiedDistance)); float angle_slow_fractionH = clamp_tpl((absAngle - angle_slow_threshold_inner) / (angle_slow_threshold_outer - angle_slow_threshold_inner), 0.0f, 1.0f); float angle_slow_fractionV = clamp_tpl((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_tpl((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(atan_tpl(radius_follow_threshold_inner / distance)); const float angle_follow_threshold_outer = RAD2DEG(atan_tpl(radius_follow_threshold_outer / distance)); const float angle_follow_fraction = clamp_tpl((absAngle - angle_follow_threshold_inner) / (angle_follow_threshold_outer - angle_follow_threshold_inner), 0.0f, 1.0f); const float angle_follow_fractionV = clamp_tpl((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_tpl( 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 * pPhysicalEntity2 = pEntity->GetPhysics(); if(pPhysicalEntity2) { pe_status_dynamics dyn_status; pPhysicalEntity2->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(acos_tpl(angleDot)) - 90.f); const float absAngle = fabsf(angle); const float angleDotV = playerUp.dot(dirToTarget); const float angleToTargetV = (RAD2DEG(acos_tpl(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 = acos_tpl(horzDot); const float angleToTargetV = acos_tpl(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; }
//------------------------------------------------------------------------ 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=acos_tpl(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 (fabs_tpl(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 (fabs_tpl(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 (fabs_tpl(m_rotationSpeed*deltaTime)>fabs_tpl(angle)) m_rotationSpeed=angle/deltaTime; else if (fabs_tpl(m_rotationSpeed)<0.001f) m_rotationSpeed=sgn(m_rotationSpeed)*0.001f; else if (fabs_tpl(m_rotationSpeed)>=m_rotationMaxSpeed) { m_rotationSpeed=sgn(m_rotationSpeed)*m_rotationMaxSpeed; m_rotationAcceleration=0.0f; } } if(fabs_tpl(angle)>=0.001f) av.w=(rotation.v/sin_tpl(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 CNetPlayerInput::UpdateMoveRequest() { CMovementRequest moveRequest; SMovementState moveState; m_pPlayer->GetMovementController()->GetMovementState(moveState); Quat worldRot = m_pPlayer->GetBaseQuat(); // m_pPlayer->GetEntity()->GetWorldRotation(); Vec3 deltaMovement = worldRot.GetInverted().GetNormalized() * m_curInput.deltaMovement; // absolutely ensure length is correct deltaMovement = deltaMovement.GetNormalizedSafe(ZERO) * m_curInput.deltaMovement.GetLength(); moveRequest.AddDeltaMovement( deltaMovement ); if( IsDemoPlayback() ) { Vec3 localVDir(m_pPlayer->GetViewQuatFinal().GetInverted() * m_curInput.lookDirection); Ang3 deltaAngles(asinf(localVDir.z),0,atan2_tpl(-localVDir.x,localVDir.y)); moveRequest.AddDeltaRotation(deltaAngles*gEnv->pTimer->GetFrameTime()); } const float fNetAimLerpFactor = g_pGameCVars->pl_netAimLerpFactor; //Vector slerp produces artifacts here, using a per-component lerp instead Vec3 vCurrentRight = m_lookDir.cross(Vec3Constants<float>::fVec3_OneZ); Vec3 vCurrentProjected = -(vCurrentRight.cross(Vec3Constants<float>::fVec3_OneZ)); vCurrentRight.Normalize(); vCurrentProjected.Normalize(); Vec3 vNewRight = m_curInput.lookDirection.cross(Vec3Constants<float>::fVec3_OneZ); Vec3 vNewProjected = -(vNewRight.cross(Vec3Constants<float>::fVec3_OneZ)); vNewProjected.Normalize(); float fRotZDirDot = vNewProjected.dot(vCurrentRight); float fRotZDot = vNewProjected.dot(vCurrentProjected); float fRotZ = acos_tpl(fRotZDot); fRotZ = AngleWrap_PI(fRotZ); float fRotZFinal = -fsgnf(fRotZDirDot) * fRotZ * fNetAimLerpFactor; float fCurrentAngle = acos_tpl(Vec3Constants<float>::fVec3_OneZ.dot(m_lookDir)); float fNewAngle = acos_tpl(Vec3Constants<float>::fVec3_OneZ.dot(m_curInput.lookDirection)); float fRotXFinal = (fNewAngle - fCurrentAngle) * -fNetAimLerpFactor; //Rotate around X first, as we have already generated the right vector Vec3 vNewLookDir = m_lookDir.GetRotated(vCurrentRight, fRotXFinal); m_lookDir = vNewLookDir.GetRotated(Vec3Constants<float>::fVec3_OneZ, fRotZFinal); Vec3 distantTarget = moveState.eyePosition + 1000.0f * m_lookDir; Vec3 lookTarget = distantTarget; if (m_curInput.usinglookik) moveRequest.SetLookTarget( lookTarget ); else moveRequest.ClearLookTarget(); if (m_curInput.aiming) moveRequest.SetAimTarget( lookTarget ); else moveRequest.ClearAimTarget(); if (m_curInput.deltaMovement.GetLengthSquared() > sqr(0.02f)) // 0.2f is almost stopped moveRequest.SetBodyTarget( distantTarget ); else moveRequest.ClearBodyTarget(); moveRequest.SetAllowStrafing(m_curInput.allowStrafing); moveRequest.SetPseudoSpeed(CalculatePseudoSpeed()); moveRequest.SetStance( (EStance)m_curInput.stance ); m_pPlayer->GetMovementController()->RequestMovement(moveRequest); if (m_curInput.sprint) m_pPlayer->m_actions |= ACTION_SPRINT; else m_pPlayer->m_actions &= ~ACTION_SPRINT; #if 0 if (m_pPlayer->m_netSetPosition) { SPredictedCharacterStates charStates; charStates.states[0].position = m_pPlayer->GetEntity()->GetPos(); charStates.states[0].orientation = m_pPlayer->GetEntity()->GetRotation(); charStates.states[0].deltatime = 0.0f; charStates.states[1].position = m_pPlayer->m_netCurrentLocation; charStates.states[1].orientation = m_pPlayer->GetEntity()->GetRotation(); charStates.states[1].deltatime = gEnv->pTimer->GetFrameTime(); charStates.states[2].position = m_pPlayer->m_netDesiredLocation; charStates.states[2].orientation = m_pPlayer->GetEntity()->GetRotation(); charStates.states[2].deltatime = m_pPlayer->m_netLerpTime; charStates.nStates = 3; moveRequest.SetPrediction(charStates); } #endif //0 #if !defined(_RELEASE) // debug.. if (g_pGameCVars->g_debugNetPlayerInput & 2) { IPersistantDebug * pPD = gEnv->pGame->GetIGameFramework()->GetIPersistantDebug(); pPD->Begin( string("update_player_input_") + m_pPlayer->GetEntity()->GetName(), true ); Vec3 wp = m_pPlayer->GetEntity()->GetWorldPos(); wp.z += 2.0f; pPD->AddSphere( moveRequest.GetLookTarget(), 0.5f, ColorF(1,0,1,0.3f), 1.0f ); // pPD->AddSphere( moveRequest.GetMoveTarget(), 0.5f, ColorF(1,1,0,0.3f), 1.0f ); pPD->AddDirection( m_pPlayer->GetEntity()->GetWorldPos() + Vec3(0,0,2), 1, m_curInput.deltaMovement, ColorF(1,0,0,0.3f), 1.0f ); } #endif //m_curInput.deltaMovement.zero(); }
////////////////////////////////////////////////////////////////////////// // NOTE: This function must be thread-safe. Before adding stuff contact MarcoC. void CVehicleMovementStdBoat::ProcessAI(const float deltaTime) { FUNCTION_PROFILER( GetISystem(), PROFILE_GAME ); float dt = max( deltaTime, 0.005f); SVehiclePhysicsStatus* physStatus = &m_physStatus[k_physicsThread]; // It is copyed from CVehicleMoventTank::ProcessAI m_movementAction.brake = false; m_movementAction.rotateYaw = 0.0f; m_movementAction.power = 0.0f; float inputSpeed = 0.0f; { if (m_aiRequest.HasDesiredSpeed()) inputSpeed = m_aiRequest.GetDesiredSpeed(); Limit(inputSpeed, -(m_maxSpeed*m_factorMaxSpeed), m_maxSpeed*m_factorMaxSpeed); } Vec3 vMove(ZERO); { if (m_aiRequest.HasMoveTarget()) vMove = ( m_aiRequest.GetMoveTarget() - m_pEntity->GetWorldPos() ).GetNormalizedSafe(); } //start calculation if ( fabsf( inputSpeed ) < 0.0001f ) { m_movementAction.brake = true; if ( physStatus->v.GetLength() > 1.0f ) { m_movementAction.power =-1.0f; } } else { Matrix33 entRotMatInvert( physStatus->q ); entRotMatInvert.Invert(); float currentAngleSpeed = RAD2DEG(-physStatus->w.z); const static float maxSteer = RAD2DEG(gf_PI/4.f); // fix maxsteer, shouldn't change Vec3 vVel = physStatus->v; Vec3 vVelR = entRotMatInvert * vVel; float currentSpeed =vVel.GetLength(); vVelR.NormalizeSafe(); if ( vVelR.Dot( FORWARD_DIRECTION ) < 0 ) currentSpeed *= -1.0f; // calculate pedal static float accScale = 0.5f; m_movementAction.power = (inputSpeed - currentSpeed) * accScale; Limit( m_movementAction.power, -1.0f, 1.0f); // calculate angles Vec3 vMoveR = entRotMatInvert * vMove; Vec3 vFwd = FORWARD_DIRECTION; vMoveR.z =0.0f; vMoveR.NormalizeSafe(); if ( inputSpeed < 0.0f ) // when going back { vFwd *= -1.0f; vMoveR *= -1.0f; currentAngleSpeed *=-1.0f; } float cosAngle = vFwd.Dot(vMoveR); float angle = RAD2DEG(acos_tpl(cosAngle)); if ( vMoveR.Dot( Vec3( 1.0f,0.0f,0.0f ) )< 0 ) angle = -angle; //int step =0; m_movementAction.rotateYaw = angle * 1.75f/ maxSteer; // implementation 1. if there is enough angle speed, we don't need to steer more if ( fabsf(currentAngleSpeed) > fabsf(angle) && angle*currentAngleSpeed > 0.0f ) { m_movementAction.rotateYaw = m_movementAction.rotateYaw*0.995f; //step =1; } // implementation 2. if we can guess we reach the distination angle soon, start counter steer. float predictDelta = inputSpeed < 0.0f ? 0.1f : 0.1f; float dict = angle + predictDelta * ( angle - m_prevAngle) / dt ; if ( dict*currentAngleSpeed<0.0f ) { if ( fabsf( angle ) < 2.0f ) { m_movementAction.rotateYaw = angle* 1.75f/ maxSteer;// ; step =3; } else { m_movementAction.rotateYaw = currentAngleSpeed < 0.0f ? 1.0f : -1.0f;// step =2; } } if ( fabsf( angle ) > 20.0f && currentSpeed > 3.0f ) { m_movementAction.power = 0.1f ; //step =4; } m_prevAngle = angle; //CryLog("steering %4.2f %4.2f %4.2f %4.2f %4.2f %4.2f %d", deltaTime,inputSpeed - currentSpeed,angle,currentAngleSpeed, m_movementAction.rotateYaw,currentAngleSpeed-m_prevAngle,step); } }
//---------------------------------------------------------------------------- void CHomingMissile::UpdateCruiseMissile(float frameTime) { 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 (m_targetId) { IEntity* pTarget = gEnv->pEntitySystem->GetEntity(m_targetId); if (pTarget) { AABB box; pTarget->GetWorldBounds(box); SetDestination( box.GetCenter() ); //if (bDebug) //pRenderer->Draw2dLabel(5.0f, y+=step, 1.5f, color, false, "Target Entity: %s", pTarget->GetName()); } } else { // update destination pos from weapon static IItemSystem* pItemSystem = g_pGame->GetIGameFramework()->GetIItemSystem(); IItem* pItem = pItemSystem->GetItem(m_weaponId); if (pItem && pItem->GetIWeapon()) { const Vec3& dest = pItem->GetIWeapon()->GetDestination(); SetDestination( dest ); //if (bDebug) //pRenderer->Draw2dLabel(5.0f, y+=step, 1.5f, color, false, "Weapon Destination: (%.1f %.1f %.1f)", dest.x, dest.y, dest.z); } } pe_status_dynamics status; if (!GetEntity()->GetPhysics()->GetStatus(&status)) return; float currentSpeed = status.v.len(); Vec3 currentPos = GetEntity()->GetWorldPos(); Vec3 goalDir(ZERO); if (!m_destination.IsZero()) { if((currentPos-m_destination).len2()<(m_detonationRadius*m_detonationRadius)) { Explode(true, true, m_destination, -status.v.normalized(), status.v, m_targetId); return; } if (bDebug) pGeom->DrawCone(m_destination, Vec3(0,0,-1), 2.5f, 7.f, ColorB(255,0,0,255)); float heightDiff = (m_cruiseAltitude-m_alignAltitude) - currentPos.z; if (!m_isCruising && heightDiff * sgn(status.v.z) > 0.f) { // if heading towards align altitude (but not yet reached) accelerate to max speed if (bDebug) pRenderer->Draw2dLabel(5.0f, y+=step, 1.5f, color, false, "[HomingMissile] accelerating (%.1f / %.1f)", currentSpeed, m_maxSpeed); } else if (!m_isCruising && heightDiff * sgnnz(status.v.z) < 0.f && (status.v.z<0 || status.v.z>0.25f)) { // align to cruise if (currentSpeed != 0) { goalDir = status.v; goalDir.z = 0; goalDir.normalize(); } if (bDebug) pRenderer->Draw2dLabel(5.0f, y+=step, 1.5f, color, false, "[HomingMissile] aligning"); } else { if (bDebug) pRenderer->Draw2dLabel(5.0f, y+=step, 1.5f, color, false, "[HomingMissile] cruising..."); // cruise m_isCruising = true; if (!m_destination.IsZero()) { float groundDistSq = m_destination.GetSquaredDistance2D(currentPos); float distSq = m_destination.GetSquaredDistance(currentPos); float descendDistSq = sqr(m_descendDistance); if (m_isDescending || groundDistSq <= descendDistSq) { if (bDebug) pRenderer->Draw2dLabel(5.0f, y+=step, 1.5f, color, false, "[HomingMissile] descending!"); if (distSq != 0) goalDir = (m_destination - currentPos).normalized(); else goalDir.zero(); m_isDescending = true; } else { Vec3 airPos = m_destination; airPos.z = currentPos.z; goalDir = airPos - currentPos; if (goalDir.len2() != 0) goalDir.Normalize(); } } } } float desiredSpeed = currentSpeed; if (currentSpeed < m_maxSpeed-0.1f) { desiredSpeed = min(m_maxSpeed, desiredSpeed + m_accel*frameTime); } Vec3 currentDir = status.v.GetNormalizedSafe(FORWARD_DIRECTION); Vec3 dir = currentDir; if (!goalDir.IsZero()) { float cosine = max(min(currentDir.Dot(goalDir), 0.999f), -0.999f); float goalAngle = RAD2DEG(acos_tpl(cosine)); float maxAngle = m_turnSpeed * frameTime; if (bDebug) { pGeom->DrawCone( currentPos, goalDir, 0.4f, 12.f, ColorB(255,0,0,255) ); pRenderer->Draw2dLabel(5.0f, y+=step, 1.5f, color, false, "[HomingMissile] goalAngle: %.2f", goalAngle); } if (goalAngle > maxAngle+0.05f) dir = (Vec3::CreateSlerp(currentDir, goalDir, maxAngle/goalAngle)).normalize(); else //if (goalAngle < 0.005f) dir = goalDir; } pe_action_set_velocity action; action.v = dir * desiredSpeed; GetEntity()->GetPhysics()->Action(&action); if (bDebug) { pGeom->DrawCone( currentPos, dir, 0.4f, 12.f, ColorB(128,128,0,255) ); pRenderer->Draw2dLabel(5.0f, y+=step, 1.5f, color, false, "[HomingMissile] currentSpeed: %.1f (max: %.1f)", currentSpeed, m_maxSpeed); } }
////////////////////////////////////////////////////////////////////////// // NOTE: This function must be thread-safe. Before adding stuff contact MarcoC. void CVehicleMovementTank::ProcessAI(const float deltaTime) { FUNCTION_PROFILER( GetISystem(), PROFILE_GAME ); float dt = max( deltaTime, 0.005f); m_movementAction.brake = false; m_movementAction.rotateYaw = 0.0f; m_movementAction.power = 0.0f; float inputSpeed = 0.0f; { if (m_aiRequest.HasDesiredSpeed()) inputSpeed = m_aiRequest.GetDesiredSpeed(); Limit(inputSpeed, -m_maxSpeed, m_maxSpeed); } Vec3 vMove(ZERO); { if (m_aiRequest.HasMoveTarget()) vMove = ( m_aiRequest.GetMoveTarget() - m_PhysPos.pos ).GetNormalizedSafe(); } //start calculation if ( fabsf( inputSpeed ) < 0.0001f || m_tireBlownTimer > 1.5f ) { // only the case to use a hand break m_movementAction.brake = true; } else { Matrix33 entRotMatInvert( m_PhysPos.q ); entRotMatInvert.Invert(); float currentAngleSpeed = RAD2DEG(-m_PhysDyn.w.z); const static float maxSteer = RAD2DEG(gf_PI/4.f); // fix maxsteer, shouldn't change Vec3 vVel = m_PhysDyn.v; Vec3 vVelR = entRotMatInvert * vVel; float currentSpeed =vVel.GetLength(); vVelR.NormalizeSafe(); if ( vVelR.Dot( FORWARD_DIRECTION ) < 0 ) currentSpeed *= -1.0f; // calculate pedal static float accScale = 0.5f; m_movementAction.power = (inputSpeed - currentSpeed) * accScale; Limit( m_movementAction.power, -1.0f, 1.0f); // calculate angles Vec3 vMoveR = entRotMatInvert * vMove; Vec3 vFwd = FORWARD_DIRECTION; vMoveR.z =0.0f; vMoveR.NormalizeSafe(); if ( inputSpeed < 0.0 ) // when going back { vFwd *= -1.0f; vMoveR *= -1.0f; currentAngleSpeed *=-1.0f; } float cosAngle = vFwd.Dot(vMoveR); float angle = RAD2DEG( acos_tpl(cosAngle)); if ( vMoveR.Dot( Vec3( 1.0f,0.0f,0.0f ) )< 0 ) angle = -angle; // int step =0; m_movementAction.rotateYaw = angle * 1.75f/ maxSteer; // implementation 1. if there is enough angle speed, we don't need to steer more if ( fabsf(currentAngleSpeed) > fabsf(angle) && angle*currentAngleSpeed > 0.0f ) { m_movementAction.rotateYaw = m_currSteer*0.995f; // step =1; } // implementation 2. if we can guess we reach the distination angle soon, start counter steer. float predictDelta = inputSpeed < 0.0f ? 0.1f : 0.07f; float dict = angle + predictDelta * ( angle - m_prevAngle) / dt ; if ( dict*currentAngleSpeed<0.0f ) { if ( fabsf( angle ) < 2.0f ) { m_movementAction.rotateYaw = angle* 1.75f/ maxSteer; // step =3; } else { m_movementAction.rotateYaw = currentAngleSpeed < 0.0f ? 1.0f : -1.0f; // step =2; } } // implementation 3. tank can rotate at a point if ( fabs( angle )> 20.0f ) { m_movementAction.power *=0.1f; // step =4; } // char buf[1024]; // sprintf(buf, "steering %4.2f %4.2f %4.2f %d\n", deltaTime,currentAngleSpeed, angle - m_prevAngle, step); // OutputDebugString( buf ); m_prevAngle = angle; } }
void CPlayerRotation::GetStanceAngleLimits(float &minAngle,float &maxAngle) { EStance stance = m_player.GetStance(); switch(stance) { default: case STANCE_CROUCH: case STANCE_STAND: minAngle = -80.0f; maxAngle = 80.0f; break; case STANCE_PRONE: minAngle = -35.0f; maxAngle = 45.0f; break; } //Limit camera rotation on ladders(to prevent clipping) if(m_player.m_stats.isOnLadder) { minAngle = -40.0f; maxAngle = 80.0f; } if(m_stats.grabbedHeavyEntity!=0) { minAngle = -35.0f; //Limit angle to prevent clipping, throw objects at feet, etc... } // SNH: additional restriction based on weapon type if prone. if(m_player.GetStance() == STANCE_PRONE && g_pGameCVars->g_proneAimAngleRestrict_Enable != 0) { float dist = 0.0f; CItem *pItem = (CItem *)(m_player.GetCurrentItem()); if(pItem) dist = pItem->GetParams().raise_distance; SMovementState movestate; m_player.m_pMovementController->GetMovementState(movestate); // try a cylinder intersection test IPhysicalEntity *pIgnore[2]; pIgnore[0] = m_player.GetEntity()->GetPhysics(); pIgnore[1] = pItem ? pItem->GetEntity()->GetPhysics() : NULL; primitives::cylinder cyl; cyl.r = 0.05f; cyl.axis = movestate.aimDirection; cyl.hh = dist; cyl.center = movestate.weaponPosition + movestate.aimDirection*cyl.hh; //gEnv->pRenderer->GetIRenderAuxGeom()->DrawCylinder(cyl.center, cyl.axis, cyl.r, cyl.hh, ColorF(0.4f,1.0f,0.6f, 0.2f)); float n = 0.0f; geom_contact *contacts; intersection_params params; WriteLockCond lockContacts; params.bStopAtFirstTri = false; params.bSweepTest = false; params.bNoBorder = true; params.bNoAreaContacts = true; n = gEnv->pPhysicalWorld->PrimitiveWorldIntersection(primitives::cylinder::type, &cyl, Vec3(0,0,2), ent_static|ent_terrain, &contacts, 0, geom_colltype_player, ¶ms, 0, 0, pIgnore, pIgnore[1]?2:1), lockContacts; int ret = (int)n; geom_contact *currentc = contacts; for(int i=0; i<ret; i++) { geom_contact *contact = currentc; if(contact && (fabs_tpl(contact->n.z)>0.2f)) { Vec3 dir = contact->pt - movestate.weaponPosition; dir.NormalizeSafe(); Vec3 horiz = dir; horiz.z = 0.0f; horiz.NormalizeSafe(); float cosangle = dir.Dot(horiz); Limit(cosangle, -1.0f, 1.0f); float newMin = acos_tpl(cosangle); newMin = -newMin * 180.0f / gf_PI; //float col[] = {1,1,1,1}; //gEnv->pRenderer->Draw2dLabel(100,100, 1.0f, col, false, "minangle: %.2f", newMin); //gEnv->pRenderer->GetIRenderAuxGeom()->DrawSphere(contact->pt, 0.03f, ColorF(1,0,0,1)); minAngle = MAX(newMin, minAngle); } ++currentc; } } minAngle *= gf_PI/180.0f; maxAngle *= gf_PI/180.0f; }
bool CDialogActorContext::DoLocalPlayerChecks(const float dt) { // don't check this every frame, but only every .2 secs m_checkPlayerTimeOut-=dt; if (m_checkPlayerTimeOut <= 0.0f) { do // a dummy loop to use break { float awareDistance; float awareDistanceSq; float awareAngle; m_pSession->GetPlayerAwarenessValues(awareDistance, awareAngle); awareDistanceSq=awareDistance*awareDistance; m_checkPlayerTimeOut = PLAYER_CHECKTIME; const float spotAngleCos = cos_tpl(DEG2RAD(awareAngle)); const CDialogSession::TActorContextMap& contextMap = m_pSession->GetAllContexts(); if (contextMap.size() == 1 && contextMap.begin()->first == m_actorID) { m_bIsAware = true; break; } // early out, when we don't have to do any checks if (awareDistance <= 0.0f && awareAngle <= 0.0f) { m_bIsAware = true; break; } IEntity* pThisEntity = m_pSession->GetActorEntity(m_actorID); if (!pThisEntity) { assert (false); m_bIsAware = true; break; } IMovementController* pMC = (m_pIActor != NULL) ? m_pIActor->GetMovementController() : NULL; if (!pMC) { assert (false); m_bIsAware = true; break; } SMovementState moveState; pMC->GetMovementState(moveState); Vec3 viewPos = moveState.eyePosition; Vec3 viewDir = moveState.eyeDirection; viewDir.z = 0.0f; viewDir.NormalizeSafe(); // check the player's position // check the player's view direction AABB groupBounds; groupBounds.Reset(); CDialogSession::TActorContextMap::const_iterator iter = contextMap.begin(); CDialogScript::SActorSet lookingAt = 0; while (iter != contextMap.end()) { if (iter->first != m_actorID) { IEntity* pActorEntity = m_pSession->GetActorEntity(iter->first); if (pActorEntity) { Vec3 vEntityPos = pActorEntity->GetWorldPos(); AABB worldBounds; pActorEntity->GetWorldBounds(worldBounds); groupBounds.Add(worldBounds); // calc if we look at it somehow Vec3 vEntityDir = vEntityPos - viewPos; vEntityDir.z = 0.0f; vEntityDir.NormalizeSafe(); if (viewDir.IsUnit() && vEntityDir.IsUnit()) { const float dot = clamp_tpl(viewDir.Dot(vEntityDir),-1.0f,+1.0f); // clamping should not be needed if (spotAngleCos <= dot) lookingAt.SetActor(iter->first); DiaLOG::Log(DiaLOG::eDebugC, "Angle to actor %d is %f deg", iter->first, RAD2DEG(acos_tpl(dot))); } } } ++iter; } const float distanceSq = pThisEntity->GetWorldPos().GetSquaredDistance(groupBounds.GetCenter()); CCamera& camera=gEnv->pSystem->GetViewCamera(); const bool bIsInAABB = camera.IsAABBVisible_F(groupBounds); const bool bIsInRange = distanceSq <= awareDistanceSq; const bool bIsLooking = contextMap.empty() || lookingAt.NumActors() > 0; m_bIsAwareLooking = awareAngle <= 0.0f || (bIsInAABB || bIsLooking); m_bIsAwareInRange = awareDistance <= 0.0f || bIsInRange; m_bIsAware = m_bIsAwareLooking && m_bIsAwareInRange; DiaLOG::Log(DiaLOG::eDebugB, "[DIALOG] LPC: %s awDist=%f awAng=%f AABBVis=%d IsLooking=%d InRange=%d [Distance=%f LookingActors=%d] Final=%saware", m_pSession->GetDebugName(), awareDistance, awareAngle, bIsInAABB, bIsLooking, bIsInRange, sqrt_tpl(distanceSq), lookingAt.NumActors(), m_bIsAware ? "" : "not "); } while (false); } if (m_bIsAware) { m_playerAwareTimeOut = m_pSession->GetPlayerAwarenessGraceTime(); } else { m_playerAwareTimeOut-= dt; if (m_playerAwareTimeOut <= 0) { return false; } } return true; }