123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437 |
- /*
- ===========================================================================
- Doom 3 BFG Edition GPL Source Code
- Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company.
- This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code").
- Doom 3 BFG Edition Source Code is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
- Doom 3 BFG Edition Source Code is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
- You should have received a copy of the GNU General Public License
- along with Doom 3 BFG Edition Source Code. If not, see <http://www.gnu.org/licenses/>.
- In addition, the Doom 3 BFG Edition Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 BFG Edition Source Code. If not, please request a copy in writing from id Software at the address below.
- If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
- ===========================================================================
- */
- #include "../idlib/precompiled.h"
- #pragma hdrstop
- #include "Game_local.h"
- /*
- ================================================================================================
- Contains the AimAssist implementation.
- ================================================================================================
- */
- idCVar aa_targetAimAssistEnable ( "aa_targetAimAssistEnable", "0", CVAR_BOOL | CVAR_ARCHIVE, "Enables/Disables the entire Aim Assist system" );
- idCVar aa_targetAdhesionEnable ( "aa_targetAdhesionEnable", "1", CVAR_BOOL, "Enables Target Adhesion" );
- idCVar aa_targetFrictionEnable ( "aa_targetFrictionEnable", "1", CVAR_BOOL, "Enables Target Friction" );
- // Selection
- idCVar aa_targetMaxDistance ( "aa_targetMaxDistance", "3000", CVAR_FLOAT, "The Maximum Distance away for a target to be considered for adhesion, friction and target lock-to" );
- idCVar aa_targetSelectionRadius ( "aa_targetSelectionRadius", "128.0", CVAR_FLOAT, "Radius used to select targets for auto aiming" );
- // Adhesion
- idCVar aa_targetAdhesionRadius ( "aa_targetAdhesionRadius", "96.0", CVAR_FLOAT, "Radius used to apply adhesion amount" );
- idCVar aa_targetAdhesionYawSpeedMax ( "aa_targetAdhesionYawSpeedMax", "0.6", CVAR_FLOAT, "Max Yaw Adhesion Speed" );
- idCVar aa_targetAdhesionPitchSpeedMax ( "aa_targetAdhesionPitchSpeedMax", "0.6", CVAR_FLOAT, "Max Pitch Adhesion Speed" );
- idCVar aa_targetAdhesionContributionPctMax ( "aa_targetAdhesionContributionPctMax", "0.25", CVAR_FLOAT, "Max Adhesion Contribution Percentage - Range 0.0 - 1.0" );
- idCVar aa_targetAdhesionPlayerSpeedThreshold ( "aa_targetAdhesionPlayerSpeedThreshold", "10.0", CVAR_FLOAT, "Speed Threshold that determines how fast the player needs to move before adhesion is allowed to kick in" );
- // Friction
- idCVar aa_targetFrictionMaxDistance ( "aa_targetFrictionMaxDistance", "1024.0", CVAR_FLOAT, "Minimum Distance Friction takes effect" );
- idCVar aa_targetFrictionOptimalDistance ( "aa_targetFrictionOptimalDistance", "768.0", CVAR_FLOAT, "Optimal Distance for Friction to take an effect" );
- idCVar aa_targetFrictionRadius ( "aa_targetFrictionRadius", "96.0", CVAR_FLOAT, "Friction Collision Sphere Radius" );
- idCVar aa_targetFrictionOptimalRadius ( "aa_targetFrictionOptimalRadius", "192.0", CVAR_FLOAT, "Friction Collision Sphere Radius when at Optimal Distance" );
- idCVar aa_targetFrictionMultiplierMin ( "aa_targetFrictionMultiplierMin", "1.0", CVAR_FLOAT, "Minimum Friction Scalar" );
- idCVar aa_targetFrictionMultiplierMax ( "aa_targetFrictionMultiplierMax", "0.4", CVAR_FLOAT, "Maximum Friction Scalar" );
- /*
- ========================
- idAimAssist::Init
- ========================
- */
- void idAimAssist::Init( idPlayer *player_ ) {
- player = player_;
- angleCorrection = ang_zero;
- frictionScalar = 1.0f;
- lastTargetPos = vec3_zero;
- }
- /*
- ========================
- idAimAssist::Update
- ========================
- */
- void idAimAssist::Update() {
- angleCorrection = ang_zero;
- UpdateNewAimAssist();
- }
- /*
- ========================
- idAimAssist::UpdateNewAimAssist
- ========================
- */
- void idAimAssist::UpdateNewAimAssist() {
- angleCorrection = ang_zero;
- frictionScalar = 1.0f;
- idEntity* lastTarget = targetEntity;
- targetEntity = NULL;
- // is aim assisting allowed? If not then just bail out
- if ( !aa_targetAimAssistEnable.GetBool() ) {
- return;
- }
- bool forceLastTarget = false;
- idVec3 targetPos;
- idEntity * entity = NULL;
- if ( forceLastTarget ) {
- entity = lastTarget;
- targetPos = lastTargetPos;
- } else {
- entity = FindAimAssistTarget( targetPos );
- }
- if ( entity != NULL ) {
- UpdateFriction( entity, targetPos );
- // by default we don't allow adhesion when we are standing still
- const float playerMovementSpeedThreshold = Square( aa_targetAdhesionPlayerSpeedThreshold.GetFloat() );
- float playerSpeed = player->GetPhysics()->GetLinearVelocity().LengthSqr();
- // only allow adhesion on actors (ai) or players. Disallow adhesion on any static world entities such as explosive barrels
- if ( playerSpeed > playerMovementSpeedThreshold ) {
- UpdateAdhesion( entity, targetPos );
- }
- targetEntity = entity;
- }
- lastTargetPos = targetPos;
- }
- /*
- ========================
- idAimAssist::FindAimAssistTarget
- ========================
- */
- idEntity* idAimAssist::FindAimAssistTarget( idVec3& targetPos ) {
- if ( player == NULL ) {
- return NULL;
- }
- //TO DO: Make this faster
- //TO DO: Defer Traces
- idEntity * optimalTarget = NULL;
- float currentBestScore = -idMath::INFINITY;
- targetPos = vec3_zero;
- idVec3 cameraPos;
- idMat3 cameraAxis;
- player->GetViewPos( cameraPos, cameraAxis );
- float maxDistanceSquared = Square( aa_targetMaxDistance.GetFloat() );
- idVec3 dirToTarget;
- float distanceToTargetSquared;
- idVec3 primaryTargetPos;
- idVec3 secondaryTargetPos;
-
- for ( idEntity * entity = gameLocal.aimAssistEntities.Next(); entity != NULL; entity = entity->aimAssistNode.Next() ) {
- if ( !entity->IsActive() ) {
- continue;
- }
- if ( entity->IsHidden() ) {
- continue;
- }
- if ( entity->IsType( idActor::Type ) ) {
- idActor * actor = static_cast<idActor *>( entity );
- if ( actor->team == player->team ) {
- // In DM, LMS, and Tourney, all players are on the same team
- if ( gameLocal.gameType == GAME_CTF || gameLocal.gameType == GAME_TDM || gameLocal.gameType == GAME_SP ) {
- continue;
- }
- }
- }
- if ( entity->IsType( idAI::Type ) ) {
- idAI * aiEntity = static_cast<idAI *>( entity );
- if ( aiEntity->ReactionTo( player ) == ATTACK_IGNORE ) {
- continue;
- }
- }
- // check whether we have a valid target position for this entity - skip it if we don't
- if ( !ComputeTargetPos( entity, primaryTargetPos, secondaryTargetPos ) ) {
- continue;
- }
- // is it close enough to us
- dirToTarget = primaryTargetPos-cameraPos;
- distanceToTargetSquared = dirToTarget.LengthSqr();
- if ( distanceToTargetSquared > maxDistanceSquared ) {
- continue;
- }
- // Compute a score in the range of 0..1 for how much are looking towards the target.
- idVec3 forward = cameraAxis[ 0 ];
- forward.Normalize();
- dirToTarget.Normalize();
- float ViewDirDotTargetDir = idMath::ClampFloat( -1.0f, 1.0f, forward * dirToTarget ); // compute the dot and clamp to account for floating point error
- // throw out anything thats outside of weapon's global FOV.
- if ( ViewDirDotTargetDir < 0.0f ) {
- continue;
- }
- // to be consistent we always use the primaryTargetPos to compute the score for this entity
- float computedScore = ComputeEntityAimAssistScore( primaryTargetPos, cameraPos, cameraAxis );
- // check if the current score beats our current best score and we have line of sight to it.
- if ( computedScore > currentBestScore ) {
- // determine if the current target is in our line of sight
- trace_t tr;
- gameLocal.clip.TracePoint( tr, cameraPos, primaryTargetPos, MASK_MONSTERSOLID, player );
-
- // did our trace fail?
- if ( ( ( tr.fraction < 1.0f ) && ( tr.c.entityNum != entity->entityNumber ) ) || ( tr.fraction >= 1.0f ) ) {
- // if the collision test failed for the primary position -- check the secondary position
- trace_t tr2;
- gameLocal.clip.TracePoint( tr2, cameraPos, secondaryTargetPos, MASK_MONSTERSOLID, player );
- if ( ( ( tr2.fraction < 1.0f ) && ( tr2.c.entityNum != entity->entityNumber ) ) || ( tr2.fraction >= 1.0f ) ) {
- // if the secondary position is also not visible then give up
- continue;
- }
- // we can see the secondary target position so we should consider this entity but use
- // the secondary position as the target position
- targetPos = secondaryTargetPos;
- } else {
- targetPos = primaryTargetPos;
- }
- // if we got here then this is our new best score
- optimalTarget = entity;
- currentBestScore = computedScore;
- }
- }
- return optimalTarget;
- }
- /*
- ========================
- idAimAssist::ComputeEntityAimAssistScore
- ========================
- */
- float idAimAssist::ComputeEntityAimAssistScore( const idVec3& targetPos, const idVec3& cameraPos, const idMat3& cameraAxis ) {
- float score = 0.0f;
-
- idVec3 dirToTarget = targetPos - cameraPos;
- float distanceToTarget = dirToTarget.Length();
- // Compute a score in the range of 0..1 for how much are looking towards the target.
- idVec3 forward = cameraAxis[0];
- forward.Normalize();
- dirToTarget.Normalize();
- float ViewDirDotTargetDir = idMath::ClampFloat( 0.0f, 1.0f, forward * dirToTarget ); // compute the dot and clamp to account for floating point error
- // the more we look at the target the higher our score
- score = ViewDirDotTargetDir;
- // weigh the score from the view angle higher than the distance score
- static float aimWeight = 0.8f;
- score *= aimWeight;
- // Add a score of 0..1 for how close the target is to the player
- if ( distanceToTarget < aa_targetMaxDistance.GetFloat() ) {
- float distanceScore = 1.0f - ( distanceToTarget / aa_targetMaxDistance.GetFloat() );
- float distanceWeight = 1.0f - aimWeight;
- score += ( distanceScore * distanceWeight );
- }
- return score * 1000.0f;
- }
- /*
- ========================
- idAimAssist::UpdateAdhesion
- ========================
- */
- void idAimAssist::UpdateAdhesion( idEntity* pTarget, const idVec3& targetPos ) {
- if ( !aa_targetAdhesionEnable.GetBool() ) {
- return;
- }
- if ( !pTarget ) {
- return;
- }
-
- float contributionPctMax = aa_targetAdhesionContributionPctMax.GetFloat();
-
- idVec3 cameraPos;
- idMat3 cameraAxis;
- player->GetViewPos(cameraPos, cameraAxis);
- idAngles cameraViewAngles = cameraAxis.ToAngles();
- cameraViewAngles.Normalize180();
- idVec3 cameraViewPos = cameraPos;
- idVec3 dirToTarget = targetPos - cameraViewPos;
- float distanceToTarget = dirToTarget.Length();
- idAngles aimAngles = dirToTarget.ToAngles();
- aimAngles.Normalize180();
- // find the delta
- aimAngles -= cameraViewAngles;
- // clamp velocities to some max values
- aimAngles.yaw = idMath::ClampFloat( -aa_targetAdhesionYawSpeedMax.GetFloat(), aa_targetAdhesionYawSpeedMax.GetFloat(), aimAngles.yaw );
- aimAngles.pitch = idMath::ClampFloat( -aa_targetAdhesionPitchSpeedMax.GetFloat(), aa_targetAdhesionPitchSpeedMax.GetFloat(), aimAngles.pitch );
-
- idVec3 forward = cameraAxis[0];
- forward.Normalize();
- dirToTarget.Normalize();
- float ViewDirDotTargetDir = idMath::ClampFloat( 0.0f, 1.0f, forward * dirToTarget ); // compute the dot and clamp to account for floating point error
- float aimLength = ViewDirDotTargetDir * distanceToTarget;
- idVec3 aimPoint = cameraPos + ( forward * aimLength );
- float delta = idMath::Sqrt( Square( distanceToTarget ) - Square( aimLength ) );
- float contribution = idMath::ClampFloat( 0.0f, contributionPctMax, 1.0f - ( delta / aa_targetAdhesionRadius.GetFloat() ) );
- angleCorrection.yaw = aimAngles.yaw * contribution;
- angleCorrection.pitch = aimAngles.pitch * contribution;
- }
- /*
- ========================
- idAimAssist::ComputeFrictionRadius
- ========================
- */
- float idAimAssist::ComputeFrictionRadius( float distanceToTarget ) {
- if ( ( distanceToTarget <= idMath::FLT_SMALLEST_NON_DENORMAL ) || distanceToTarget > aa_targetFrictionMaxDistance.GetFloat() ) {
- return aa_targetFrictionRadius.GetFloat();
- }
- float distanceContributionScalar = ( aa_targetFrictionOptimalDistance.GetFloat() > 0.0f ) ? ( distanceToTarget / aa_targetFrictionOptimalDistance.GetFloat() ) : 0.0f;
- if ( distanceToTarget > aa_targetFrictionOptimalDistance.GetFloat() ) {
- const float range = idMath::ClampFloat( 0.0f, aa_targetFrictionMaxDistance.GetFloat(), aa_targetFrictionMaxDistance.GetFloat() - aa_targetFrictionOptimalDistance.GetFloat() );
- if ( range > idMath::FLT_SMALLEST_NON_DENORMAL ) {
- distanceContributionScalar = 1.0f - ( ( distanceToTarget - aa_targetFrictionOptimalDistance.GetFloat() ) / range );
- }
- }
- return Lerp( aa_targetFrictionRadius.GetFloat(), aa_targetFrictionOptimalRadius.GetFloat(), distanceContributionScalar );
- }
- /*
- ========================
- idAimAssist::UpdateFriction
- ========================
- */
- void idAimAssist::UpdateFriction( idEntity* pTarget, const idVec3& targetPos ) {
- if ( !aa_targetFrictionEnable.GetBool() ) {
- return;
- }
- if ( pTarget == NULL ) {
- return;
- }
-
- idVec3 cameraPos;
- idMat3 cameraAxis;
- player->GetViewPos(cameraPos, cameraAxis);
- idVec3 dirToTarget = targetPos - cameraPos;
- float distanceToTarget = dirToTarget.Length();
- idVec3 forward = cameraAxis[0];
- forward.Normalize();
- dirToTarget.Normalize();
- float ViewDirDotTargetDir = idMath::ClampFloat( 0.0f, 1.0f, forward * dirToTarget ); // compute the dot and clamp to account for floating point error
- float aimLength = ViewDirDotTargetDir * distanceToTarget;
- idVec3 aimPoint = cameraPos + ( forward * aimLength );
- float delta = idMath::Sqrt( Square( distanceToTarget ) - Square( aimLength ) );
- const float radius = ComputeFrictionRadius( distanceToTarget );
- if ( delta < radius ) {
- float alpha = 1.0f - ( delta / radius );
- frictionScalar = Lerp( aa_targetFrictionMultiplierMin.GetFloat(), aa_targetFrictionMultiplierMax.GetFloat(), alpha );
- }
- }
- /*
- ========================
- idAimAssist::ComputeTargetPos
- ========================
- */
- bool idAimAssist::ComputeTargetPos( idEntity* entity, idVec3& primaryTargetPos, idVec3& secondaryTargetPos ) {
- primaryTargetPos = vec3_zero;
- secondaryTargetPos = vec3_zero;
- if ( entity == NULL ) {
- return false;
- }
- // The target point on actors can now be either the head or the torso
- idActor * actor = NULL;
- if ( entity->IsType( idActor::Type ) ) {
- actor = ( idActor *) entity;
- }
- if ( actor != NULL ) {
- // Actor AimPoint
- idVec3 torsoPos;
- idVec3 headPos = actor->GetEyePosition();
- if ( actor->GetHeadEntity() != NULL ) {
- torsoPos = actor->GetHeadEntity()->GetPhysics()->GetOrigin();
- } else {
- const float offsetScale = 0.9f;
- torsoPos = actor->GetPhysics()->GetOrigin() + ( actor->EyeOffset() * offsetScale );
- }
- primaryTargetPos = torsoPos;
- secondaryTargetPos = headPos;
- return true;
- } else if ( entity->GetPhysics()->GetClipModel() != NULL ) {
- const idBounds& box = entity->GetPhysics()->GetClipModel()->GetAbsBounds();
- primaryTargetPos = box.GetCenter();
- secondaryTargetPos = box.GetCenter();
- return true;
- }
- return false;
- }
|