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 ;
2023-02-28 15:47:02 +01:00
//Assembly-CSharp.dll
//Aki.Reflection.dll
//BepInEx.dll
//Comfort.dll
//UnityEngine.dll
//UnityEngine.CoreModule.dll
2022-12-10 16:47:02 +01:00
namespace notGreg.UniformAim
{
2023-03-25 11:28:04 +01:00
[BepInPlugin("com.notGreg.UniformAim", "notGreg's Uniform Aim for Tarkov", "3.5.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 ;
ConfigEntry < float > configSens ;
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 ) ) ) ;
2022-12-10 16:47:02 +01:00
configSens = Config . Bind ( "General" , "Sensitivity" , 1.0f , new ConfigDescription ( "" , new AcceptableValueRange < float > ( 0.01f , 2.0f ) ) ) ;
2023-01-09 17:48:25 +01:00
if ( ! isRealismModPresent )
{
new get_AimingSensitivityPatch ( ) . Enable ( ) ;
}
2023-02-28 15:47:02 +01:00
else
{
Logger . LogInfo ( "RealismMod detected! Abandoning aimingSens patch...\nMake sure to use the compatibility plugin!" ) ;
}
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
//Logger.LogInfo("Checking world instance");
if ( Singleton < AbstractGame > . Instance = = null )
{
return ;
}
//grab the game status of the existing instance
//Logger.LogInfo("Checking game status");
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)
//Logger.LogInfo("Checking GameStatus and isAiming");
2023-01-09 17:48:25 +01:00
//Logger.LogInfo($"GameStatus: {currentGameStatus}");
2022-12-10 16:47:02 +01:00
if ( currentGameStatus = = GameStatus . Started & & mainPlayer ! = null )
{
//Logger.LogInfo($"isAiming? {mainPlayer.HandsController.IsAiming}");
if ( mainPlayer . HandsController . IsAiming )
{
2023-02-28 15:47:02 +01:00
aimingSens = calculateSensitivity ( ) ;
2022-12-10 16:47:02 +01:00
}
return ;
}
//Logger.LogInfo("Switch on GameStatus");
switch ( currentGameStatus )
{
case GameStatus . Started :
{
mainPlayer = getLocalPlayer ( ) ;
//Logger.LogInfo($"Subscribing to onAimingChanged event");
subscribeOnAimingChanged ( ) ;
//Logger.LogInfo("TryGetCameras coroutines");
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.
//Appropriate class can usually be found by searching for the ClearSettings function.
2023-02-28 15:47:02 +01:00
//SharedGameSettingsClass settingsLibrary = SharedGameSettingsClass.Instance; // Futureproofing for 3.5.1 and onwards
2022-12-10 16:47:02 +01:00
int getInGameFOV ( )
{
//int fov = Singleton<GClass1642>.Instance.Game.Settings.FieldOfView; //SPT-AKI 3.2.3
2023-01-09 17:48:25 +01:00
//int fov = Singleton<GClass1653>.Instance.Game.Settings.FieldOfView; //SPT-AKI 3.3.0
2023-02-28 15:47:02 +01:00
//int fov = Singleton<GClass1659>.Instance.Game.Settings.FieldOfView; //SPT-AKI 3.4.1
2023-03-10 15:54:59 +00:00
//int fov = Singleton<GClass1776>.Instance.Game.Settings.FieldOfView; //SPT-AKI 3.5.0
2023-03-25 11:28:04 +01:00
int fov = Singleton < SharedGameSettingsClass > . Instance . Game . Settings . FieldOfView ; //SPT-AKI 3.5.3
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.
//Appropriate class can usually be found by searching for the ClearSettings function.
float getInGameAimSens ( )
{
//float sens = Singleton<GClass1642>.Instance.Control.Settings.MouseAimingSensitivity; //SPT-AKI 3.2.*
2023-01-09 17:48:25 +01:00
//float sens = Singleton<GClass1653>.Instance.Control.Settings.MouseAimingSensitivity; //SPT-AKI 3.3.0
2023-02-28 15:47:02 +01:00
//float sens = Singleton<GClass1659>.Instance.Control.Settings.MouseAimingSensitivity; //SPT-AKI 3.4.1
2023-03-10 15:54:59 +00:00
//float sens = Singleton<GClass1776>.Instance.Control.Settings.MouseAimingSensitivity; //SPT-AKI 3.5.0
2023-03-25 11:28:04 +01:00
float sens = Singleton < SharedGameSettingsClass > . Instance . Control . Settings . MouseAimingSensitivity ; //SPT-AKI 3.5.3
2023-02-28 15:47:02 +01:00
//float sens = settingsLibrary.Control.Settings.MouseAimingSensitivity;
//Logger.LogInfo($"In-game AimSens: {sens}");
2022-12-10 16:47:02 +01:00
return sens ;
}
Player getLocalPlayer ( )
{
2023-01-09 17:48:25 +01:00
//Logger.LogInfo("Setting local player...");
2022-12-10 16:47:02 +01:00
return Singleton < GameWorld > . Instance . RegisteredPlayers . Find ( p = > p . IsYourPlayer ) ;
}
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 > ( ) ;
//Logger.LogInfo($"{mainCamera.name} found!");
}
else
{
//Logger.LogMessage($"Camera \"{cameraName}\" not found, rescheduling...");
yield return myDelay ;
StartCoroutine ( tryGetMainCamera ( ) ) ;
yield break ;
}
yield return null ;
}
//this coroutine attempts to find existing baseOpticCamera in the scene. The state of this camera is used to determine whether the player is using a magnified optic or not.
IEnumerator tryGetScopeCamera ( )
{
string cameraName = "BaseOpticCamera(Clone)" ;
if ( GameObject . Find ( cameraName ) ! = null )
{
scopeCamera = GameObject . Find ( cameraName ) . GetComponent < Camera > ( ) ;
//Logger.LogInfo($"{scopeCamera.name} found!");
}
yield break ;
}
//figure out whether the player is using a magnified optic or not. Return the FOV of the sight.
Camera determineCurrentAimedFOV ( )
{
2023-01-09 17:48:25 +01:00
//Logger.LogInfo("Get current aiming mod");
//Logger.LogInfo($"MainPlayer {mainPlayer.name}");
//Logger.LogInfo($"ProcWeapAnim {mainPlayer.ProceduralWeaponAnimation}");
//Logger.LogInfo($"CurrAimMod {mainPlayer.ProceduralWeaponAnimation.CurrentAimingMod}");
if ( mainPlayer . ProceduralWeaponAnimation . CurrentAimingMod = = null )
{
return mainCamera ;
}
var currentAimingMod = mainPlayer . ProceduralWeaponAnimation . CurrentAimingMod ;
2022-12-10 16:47:02 +01:00
2023-01-09 17:48:25 +01:00
//get the name of the currently active scope
//string scopeName = currentAimingMod.Item.Template.Name;
string scopeName = currentAimingMod . Item . Name ;
2022-12-10 16:47:02 +01:00
//Logger.LogInfo($"Scope name: {scopeName}");
//scopeMode determines the scope being used (e.g. main magnified optic at index 0, backup RDS at index 1)
//scopeIndex determines the mode of the scope being used (e.g. x1 magnification at index 0, x4 magnification at index 1)
//there are exceptions to this rule thanks to BSG's inconsistency, some of them are patched below
2023-01-09 17:48:25 +01:00
var scopeIndex = currentAimingMod . SelectedScopeIndex ;
var scopeMode = currentAimingMod . SelectedScopeMode ;
2022-12-10 16:47:02 +01:00
//Logger.LogInfo("Index: " + scopeIndex + "\nMode: " + scopeMode);
//patches for specific scopes, matches item name
switch ( scopeName )
{
case "tactical_mp155_kalashnikov_ultima_camera" :
{
//Logger.LogInfo("MP-155 Thermal");
return mainCamera ;
}
//SAM-SWAT's Leupold D-Evo optic patch. The modes on this scope are reversed. Causes detection based on baseOpticCamera to fail as the magnified optic camera is always active
case "scope_leupold_d_evo" :
{
if ( scopeMode = = 0 )
{
//Logger.LogInfo($"Leupold D-EVO BUIS FOV: {mainCamera.fieldOfView}");
return mainCamera ;
}
else
{
//Logger.LogInfo($"Leupold D-EVO Scope FOV: {scopeCamera.fieldOfView}");
return scopeCamera ;
}
}
case "scope_base_trijicon_acog_ta11_3,5x35" :
{
if ( scopeIndex = = 1 )
{
2023-01-09 17:48:25 +01:00
//Logger.LogInfo($"G36 Rail sight");
2022-12-10 16:47:02 +01:00
return mainCamera ;
}
else
{
return scopeCamera ;
}
}
default :
{
if ( scopeCamera = = null | | scopeCamera . isActiveAndEnabled = = false )
{
//Logger.LogInfo($"Non-magnified: {scopeName} FOV: {mainCamera.fieldOfView}");
return mainCamera ;
}
else
{
//Logger.LogInfo($"Magnified: {scopeName} FOV: {scopeCamera.fieldOfView}");
return scopeCamera ;
}
}
}
}
float calculateSensitivity ( )
{
2023-01-09 17:48:25 +01:00
//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-01-09 17:48:25 +01:00
float aimedFOV = Mathf . Deg2Rad * Camera . VerticalToHorizontalFieldOfView ( determineCurrentAimedFOV ( ) . fieldOfView , 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 ;
//Logger.LogInfo($"Sensitivity: {sensitivity}");
return sensitivity * configSens . Value ;
}
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 ) = >
{
inGameFOV = getInGameFOV ( ) ;
inGameAimedSens = getInGameAimSens ( ) ;
StartCoroutine ( tryGetScopeCamera ( ) ) ;
} ;
} ;
}
}
}