2022-12-10 16:47:02 +01:00
using BepInEx ;
2023-01-09 17:48:25 +01:00
using BepInEx.Bootstrap ;
2022-12-10 16:47:02 +01:00
using BepInEx.Configuration ;
using Comfort.Common ;
using EFT ;
using System ;
using System.Collections ;
using UnityEngine ;
2024-04-26 14:03:42 +02:00
// DLL dependencies needed to update the Uniform Aim Mod for newer versions of the game
//%tarkovdir%\BepInEx\core\BepInEx.dll
//%tarkovdir%\EscapeFromTarkov_Data\Managed\Assembly-CSharp.dll
//%tarkovdir%\EscapeFromTarkov_Data\Managed\Aki.Reflection.dll
//%tarkovdir%\EscapeFromTarkov_Data\Managed\Comfort.dll
//%tarkovdir%\EscapeFromTarkov_Data\Managed\UnityEngine.dll
//%tarkovdir%\EscapeFromTarkov_Data\Managed\UnityEngine.CoreModule.dll
2023-02-28 15:47:02 +01:00
2022-12-10 16:47:02 +01:00
namespace notGreg.UniformAim
{
2024-04-26 14:03:42 +02:00
[BepInPlugin("com.notGreg.UniformAim", "notGreg's Uniform Aim for Tarkov", "3.8.0")]
2023-01-09 17:48:25 +01:00
[BepInDependency("RealismMod", BepInDependency.DependencyFlags.SoftDependency)]
2022-12-10 16:47:02 +01:00
public class Plugin : BaseUnityPlugin
{
ConfigEntry < int > configExponent ;
2024-01-02 17:14:30 +01:00
ConfigEntry < int > configSens ;
2023-04-01 16:44:32 +02:00
ConfigEntry < bool > enableDebug ;
2022-12-10 16:47:02 +01:00
2023-01-09 17:48:25 +01:00
public static bool isRealismModPresent = Chainloader . PluginInfos . ContainsKey ( "RealismMod" ) ;
2022-12-10 16:47:02 +01:00
void Awake ( )
{
2023-02-28 15:47:02 +01:00
configExponent = Config . Bind ( "General" , "Coefficient" , 133 , new ConfigDescription ( "" , new AcceptableValueRange < int > ( 10 , 500 ) ) ) ;
2024-04-26 14:03:42 +02:00
//configSens = Config.Bind("General", "Sensitivity", 1.0f, new ConfigDescription("", new AcceptableValueRange<float>(0.01f, 2.0f))); // an old float-based sensitivity for future reference
2024-01-02 17:14:30 +01:00
configSens = Config . Bind ( "General" , "Sensitivity" , 100 , new ConfigDescription ( "" , new AcceptableValueRange < int > ( 10 , 200 ) ) ) ;
2022-12-10 16:47:02 +01:00
2023-04-01 16:44:32 +02:00
enableDebug = Config . Bind ( "Debug" , "Enable debug logging" , false ) ;
2023-01-09 17:48:25 +01:00
if ( ! isRealismModPresent )
{
new get_AimingSensitivityPatch ( ) . Enable ( ) ;
}
2023-02-28 15:47:02 +01:00
else
{
2023-04-01 16:44:32 +02:00
if ( enableDebug . Value ) Logger . LogInfo ( "RealismMod detected! Abandoning aimingSens patch...\nMake sure to use the compatibility plugin!" ) ;
2023-02-28 15:47:02 +01:00
}
2022-12-10 16:47:02 +01:00
}
Player mainPlayer ;
int inGameFOV ;
float inGameAimedSens ;
2023-01-09 17:48:25 +01:00
public static float aimingSens = 1.0f ;
2022-12-10 16:47:02 +01:00
//this function can be replaced by FixedUpdate() at 50Hz (adjustable via Time.fixedDeltaTime)
//FixedUpdate() proved to be slightly more reliable in the past, however it had other unintended effects on the game.
void FixedUpdate ( )
{
//check if the world instance exists
if ( Singleton < AbstractGame > . Instance = = null )
{
return ;
}
//grab the game status of the existing instance
GameStatus currentGameStatus = Singleton < AbstractGame > . Instance . Status ;
//check if the game has started and if the player exists. If the player is aiming, update the scoped sensitivity (executed every frame while aiming)
if ( currentGameStatus = = GameStatus . Started & & mainPlayer ! = null )
{
if ( mainPlayer . HandsController . IsAiming )
{
2023-02-28 15:47:02 +01:00
aimingSens = calculateSensitivity ( ) ;
2022-12-10 16:47:02 +01:00
}
return ;
}
2023-04-01 16:44:32 +02:00
if ( enableDebug . Value ) Logger . LogInfo ( "Switch on GameStatus" ) ;
2022-12-10 16:47:02 +01:00
switch ( currentGameStatus )
{
case GameStatus . Started :
{
mainPlayer = getLocalPlayer ( ) ;
2023-04-01 16:44:32 +02:00
if ( enableDebug . Value ) Logger . LogInfo ( $"Subscribing to onAimingChanged event" ) ;
2022-12-10 16:47:02 +01:00
subscribeOnAimingChanged ( ) ;
2023-04-01 16:44:32 +02:00
if ( enableDebug . Value ) Logger . LogInfo ( "TryGetCameras coroutines" ) ;
2022-12-10 16:47:02 +01:00
StartCoroutine ( tryGetMainCamera ( ) ) ;
StartCoroutine ( tryGetScopeCamera ( ) ) ;
break ;
}
case GameStatus . SoftStopping :
case GameStatus . Stopped :
{
mainPlayer = null ;
break ;
}
default :
{
break ;
}
}
2023-01-09 17:48:25 +01:00
2023-02-28 15:47:02 +01:00
2022-12-10 16:47:02 +01:00
}
//this function grabs the Field of View from the settings. This is the function that most often needs patching after update.
2024-04-26 14:03:42 +02:00
//Appropriate class can usually be found by searching for the "ClearSettings" function.
2022-12-10 16:47:02 +01:00
int getInGameFOV ( )
{
2024-04-26 14:03:42 +02:00
int fov = Singleton < SharedGameSettingsClass > . Instance . Game . Settings . FieldOfView ;
2022-12-10 16:47:02 +01:00
return fov ;
}
//this function grabs the Aiming Sensitivity from the settings. This is the function that most often needs patching after update.
2024-04-26 14:03:42 +02:00
//Appropriate class can usually be found by searching for the "ClearSettings" function.
2022-12-10 16:47:02 +01:00
float getInGameAimSens ( )
{
2024-04-26 14:03:42 +02:00
float sens = Singleton < SharedGameSettingsClass > . Instance . Control . Settings . MouseAimingSensitivity ;
2023-04-01 16:44:32 +02:00
if ( enableDebug . Value ) Logger . LogInfo ( $"In-game AimSens: {sens}" ) ;
2022-12-10 16:47:02 +01:00
return sens ;
}
Player getLocalPlayer ( )
{
2023-04-01 16:44:32 +02:00
if ( enableDebug . Value ) Logger . LogInfo ( "Setting local player..." ) ;
2024-01-02 17:14:30 +01:00
return Singleton < GameWorld > . Instance . RegisteredPlayers . Find ( p = > p . IsYourPlayer ) as Player ;
2022-12-10 16:47:02 +01:00
}
WaitForSecondsRealtime myDelay = new WaitForSecondsRealtime ( 1f ) ;
Camera mainCamera ;
Camera scopeCamera ;
//this coroutine attempts to find the FPS Camera in the scene.
IEnumerator tryGetMainCamera ( )
{
string cameraName = "FPS Camera" ;
if ( GameObject . Find ( cameraName ) ! = null )
{
mainCamera = GameObject . Find ( cameraName ) . GetComponent < Camera > ( ) ;
2023-04-01 16:44:32 +02:00
if ( enableDebug . Value ) Logger . LogInfo ( $"{mainCamera.name} found!" ) ;
2022-12-10 16:47:02 +01:00
}
else
{
2023-04-01 16:44:32 +02:00
if ( enableDebug . Value ) Logger . LogMessage ( $"Camera \" { cameraName } \ " not found, rescheduling..." ) ;
2022-12-10 16:47:02 +01:00
yield return myDelay ;
StartCoroutine ( tryGetMainCamera ( ) ) ;
yield break ;
}
yield return null ;
}
2023-04-01 16:44:32 +02:00
//this coroutine attempts to find existing baseOpticCamera in the scene.
2022-12-10 16:47:02 +01:00
IEnumerator tryGetScopeCamera ( )
{
string cameraName = "BaseOpticCamera(Clone)" ;
if ( GameObject . Find ( cameraName ) ! = null )
{
scopeCamera = GameObject . Find ( cameraName ) . GetComponent < Camera > ( ) ;
2023-04-01 16:44:32 +02:00
if ( enableDebug . Value ) Logger . LogInfo ( $"{scopeCamera.name} found!" ) ;
2022-12-10 16:47:02 +01:00
}
yield break ;
}
2023-04-01 16:44:32 +02:00
//figure out whether the player is using a magnified optic or not. Return the camera of the sight.
float determineCurrentAimedFOV ( )
2022-12-10 16:47:02 +01:00
{
2023-04-01 16:44:32 +02:00
if ( enableDebug . Value )
2023-01-09 17:48:25 +01:00
{
2023-04-01 16:44:32 +02:00
Logger . LogInfo ( $"Current scope: {mainPlayer.ProceduralWeaponAnimation.CurrentAimingMod.Item.LocalizedName()} isOptic? {mainPlayer.ProceduralWeaponAnimation.CurrentScope.IsOptic}" ) ;
2023-01-09 17:48:25 +01:00
}
2023-04-01 16:44:32 +02:00
if ( mainPlayer . ProceduralWeaponAnimation . CurrentScope . IsOptic )
2022-12-10 16:47:02 +01:00
{
2023-04-01 16:44:32 +02:00
return scopeCamera . fieldOfView ;
2022-12-10 16:47:02 +01:00
}
2023-04-01 16:44:32 +02:00
return mainCamera . fieldOfView ;
2022-12-10 16:47:02 +01:00
}
float calculateSensitivity ( )
{
2023-04-01 16:44:32 +02:00
if ( enableDebug . Value ) Logger . LogInfo ( "calculateSensitivity()" ) ;
2022-12-10 16:47:02 +01:00
//grab vertical hipfire field of view (FOV set by the user in the setting), then convert it to horizontal FOV
//convert degrees to radians
float hipFOV = Mathf . Deg2Rad * Camera . VerticalToHorizontalFieldOfView ( inGameFOV , mainCamera . aspect ) ;
//grab current field of view while aiming, then convert it to horizontal FOV
//convert degrees to radians
2023-04-01 16:44:32 +02:00
float aimedFOV = Mathf . Deg2Rad * Camera . VerticalToHorizontalFieldOfView ( determineCurrentAimedFOV ( ) , mainCamera . aspect ) ;
2022-12-10 16:47:02 +01:00
//exponent applied to the ratio of aimedFOV to hipFOV, causes sights to become relatively faster or slower as zoom increases
float exponent = 100f / configExponent . Value ;
2023-03-25 11:28:04 +01:00
2022-12-10 16:47:02 +01:00
float tanRatio = ( float ) ( Mathf . Tan ( aimedFOV / 2 ) / Mathf . Tan ( hipFOV / 2 ) ) ;
float sensitivity = ( float ) Math . Pow ( tanRatio , exponent ) * inGameAimedSens ;
2023-04-01 16:44:32 +02:00
if ( enableDebug . Value ) Logger . LogInfo ( $"Sensitivity: {sensitivity}" ) ;
2024-01-02 17:14:30 +01:00
return sensitivity * ( configSens . Value / 100.0f ) ;
2022-12-10 16:47:02 +01:00
}
void subscribeOnAimingChanged ( )
{
//HandsChangedEvent triggers whenever player changes weapons
//without it the patch would cease to work as expected when weapons were changed
mainPlayer . HandsChangedEvent + = ( handsArgs ) = >
{
//onAimingChanged triggers whenever the player starts or stops aiming.
mainPlayer . HandsController . OnAimingChanged + = ( aimArgs ) = >
{
2023-04-01 16:44:32 +02:00
if ( enableDebug . Value ) Logger . LogInfo ( $"Scope: {mainPlayer.ProceduralWeaponAnimation.CurrentAimingMod.Item.LocalizedName()} isOptic? {mainPlayer.ProceduralWeaponAnimation.CurrentScope.IsOptic}" ) ;
2022-12-10 16:47:02 +01:00
inGameFOV = getInGameFOV ( ) ;
inGameAimedSens = getInGameAimSens ( ) ;
StartCoroutine ( tryGetScopeCamera ( ) ) ;
} ;
} ;
}
}
}