Decompiled source of AlwaysFireWhileSprinting v1.0.1

BepInEx/plugins/AlwaysFireWhileSprinting.dll

Decompiled 2 days ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Pigeon.Movement;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: AssemblyCompany("AlwaysFireWhileSprinting")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("AlwaysFireWhileSprinting")]
[assembly: AssemblyTitle("AlwaysFireWhileSprinting")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.0.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
[BepInPlugin("sparroh.alwaysfirewhilesprinting", "AlwaysFireWhileSprinting", "1.0.1")]
[MycoMod(/*Could not decode attribute arguments.*/)]
public class SparrohPlugin : BaseUnityPlugin
{
	private struct OriginalFireConstraints
	{
		public ActionFireMode CanFireWhileSprinting;

		public ActionFireMode CanFireWhileSliding;
	}

	public const string PluginGUID = "sparroh.alwaysfirewhilesprinting";

	public const string PluginName = "AlwaysFireWhileSprinting";

	public const string PluginVersion = "1.0.1";

	internal static ManualLogSource Logger;

	internal static ConfigEntry<bool> enableCanFireWhileSprinting;

	internal static ConfigEntry<bool> enableCanFireWhileSliding;

	internal static ConfigEntry<bool> enableSprintToFireFix;

	private static readonly FieldInfo gunDataField = AccessTools.Field(typeof(Gun), "gunData");

	private static readonly Dictionary<int, OriginalFireConstraints> originalConstraints = new Dictionary<int, OriginalFireConstraints>();

	private Harmony harmony;

	private FileSystemWatcher configWatcher;

	private static volatile bool pendingConstraintRefresh;

	private void Awake()
	{
		//IL_00e0: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ea: Expected O, but got Unknown
		//IL_0157: Unknown result type (might be due to invalid IL or missing references)
		//IL_015d: Expected O, but got Unknown
		Logger = ((BaseUnityPlugin)this).Logger;
		enableCanFireWhileSprinting = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Can Fire While Sprinting", true, "Allows firing weapons while sprinting.");
		enableCanFireWhileSliding = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Can Fire While Sliding", true, "Allows firing weapons while sliding.");
		enableSprintToFireFix = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Sprint To Fire Fix", true, "Enables the Sprint-to-Fire fix that allows immediate firing while sprinting and proper sprint resume behavior.");
		enableCanFireWhileSprinting.SettingChanged += OnFireConstraintSettingChanged;
		enableCanFireWhileSliding.SettingChanged += OnFireConstraintSettingChanged;
		try
		{
			SetupFileWatcher();
		}
		catch (Exception ex)
		{
			Logger.LogError((object)("Error setting up config file watcher: " + ex.Message));
		}
		harmony = new Harmony("sparroh.alwaysfirewhilesprinting");
		try
		{
			MethodInfo methodInfo = AccessTools.Method(typeof(Gun), "Setup", new Type[3]
			{
				typeof(Player),
				typeof(PlayerAnimation),
				typeof(IGear)
			}, (Type[])null);
			if (methodInfo == null)
			{
				Logger.LogError((object)"Could not find Gun.Setup method for patching.");
			}
			else
			{
				HarmonyMethod val = new HarmonyMethod(typeof(SparrohPlugin), "ModifyWeaponPrefix", (Type[])null);
				harmony.Patch((MethodBase)methodInfo, val, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			}
		}
		catch (Exception ex2)
		{
			Logger.LogError((object)("Error patching Gun.Setup: " + ex2.Message));
		}
		try
		{
			harmony.PatchAll(typeof(SprintToFireFixPatches));
			Logger.LogInfo((object)("SprintToFireFix patches applied (currently " + (enableSprintToFireFix.Value ? "enabled" : "disabled") + ")"));
		}
		catch (Exception ex3)
		{
			Logger.LogError((object)("Error applying SprintToFireFix patches: " + ex3.Message));
		}
		Logger.LogInfo((object)"AlwaysFireWhileSprinting v1.0.1 loaded successfully.");
	}

	private void Update()
	{
		if (pendingConstraintRefresh)
		{
			pendingConstraintRefresh = false;
			ApplyFireConstraintsToAllGuns();
		}
	}

	private void SetupFileWatcher()
	{
		configWatcher = new FileSystemWatcher(Paths.ConfigPath, "sparroh.alwaysfirewhilesprinting.cfg");
		configWatcher.NotifyFilter = NotifyFilters.FileName | NotifyFilters.Size | NotifyFilters.LastWrite;
		configWatcher.Changed += OnConfigFileChanged;
		configWatcher.Created += OnConfigFileChanged;
		configWatcher.Renamed += OnConfigFileChanged;
		configWatcher.EnableRaisingEvents = true;
	}

	private void OnConfigFileChanged(object sender, FileSystemEventArgs e)
	{
		try
		{
			((BaseUnityPlugin)this).Config.Reload();
			pendingConstraintRefresh = true;
			Logger.LogInfo((object)"Config reloaded from disk.");
		}
		catch (Exception ex)
		{
			Logger.LogError((object)("Error reloading config: " + ex.Message));
		}
	}

	private static void OnFireConstraintSettingChanged(object sender, EventArgs e)
	{
		pendingConstraintRefresh = true;
	}

	public static void ModifyWeaponPrefix(Gun __instance, IGear prefab)
	{
		Gun val = (Gun)(object)((prefab is Gun) ? prefab : null);
		if (val != null)
		{
			ApplyFireConstraints(val);
		}
	}

	internal static void ApplyFireConstraintsToAllGuns()
	{
		try
		{
			Gun[] array = Object.FindObjectsOfType<Gun>();
			foreach (Gun val in array)
			{
				if ((Object)(object)val != (Object)null)
				{
					ApplyFireConstraints(val);
				}
			}
			Logger.LogInfo((object)$"Re-applied fire constraints to {array.Length} gun(s).");
		}
		catch (Exception ex)
		{
			Logger.LogError((object)("Error re-applying fire constraints: " + ex.Message));
		}
	}

	internal static void ApplyFireConstraints(Gun gun)
	{
		if ((Object)(object)gun == (Object)null)
		{
			return;
		}
		try
		{
			object obj = gunDataField?.GetValue(gun);
			if (obj != null)
			{
				ApplyFireConstraintsToGunDataObject(gun, obj);
			}
			else
			{
				ApplyFireConstraintsToGunData(gun, ref gun.GunData);
			}
		}
		catch (Exception ex)
		{
			Logger.LogError((object)("Error applying fire constraints: " + ex.Message));
		}
	}

	private static void ApplyFireConstraintsToGunData(Gun gun, ref GunData gunData)
	{
		//IL_0026: Unknown result type (might be due to invalid IL or missing references)
		//IL_002b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0038: Unknown result type (might be due to invalid IL or missing references)
		//IL_003d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0063: Unknown result type (might be due to invalid IL or missing references)
		//IL_006b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0083: Unknown result type (might be due to invalid IL or missing references)
		//IL_008b: Unknown result type (might be due to invalid IL or missing references)
		int instanceID = ((Object)gun).GetInstanceID();
		if (!originalConstraints.TryGetValue(instanceID, out var value))
		{
			value = new OriginalFireConstraints
			{
				CanFireWhileSprinting = gunData.fireConstraints.canFireWhileSprinting,
				CanFireWhileSliding = gunData.fireConstraints.canFireWhileSliding
			};
			originalConstraints[instanceID] = value;
		}
		gunData.fireConstraints.canFireWhileSprinting = (ActionFireMode)(enableCanFireWhileSprinting.Value ? 1 : ((int)value.CanFireWhileSprinting));
		gunData.fireConstraints.canFireWhileSliding = (ActionFireMode)(enableCanFireWhileSliding.Value ? 1 : ((int)value.CanFireWhileSliding));
	}

	private static void ApplyFireConstraintsToGunDataObject(Gun gun, object gunDataObj)
	{
		//IL_0080: Unknown result type (might be due to invalid IL or missing references)
		//IL_0085: Unknown result type (might be due to invalid IL or missing references)
		//IL_0093: Unknown result type (might be due to invalid IL or missing references)
		//IL_0098: Unknown result type (might be due to invalid IL or missing references)
		//IL_00bd: Unknown result type (might be due to invalid IL or missing references)
		//IL_00da: Unknown result type (might be due to invalid IL or missing references)
		FieldInfo field = gunDataObj.GetType().GetField("fireConstraints");
		if (field == null)
		{
			return;
		}
		object value = field.GetValue(gunDataObj);
		if (value == null)
		{
			return;
		}
		Type type = value.GetType();
		FieldInfo field2 = type.GetField("canFireWhileSprinting");
		FieldInfo field3 = type.GetField("canFireWhileSliding");
		if (!(field2 == null) && !(field3 == null))
		{
			int instanceID = ((Object)gun).GetInstanceID();
			if (!originalConstraints.TryGetValue(instanceID, out var value2))
			{
				value2 = new OriginalFireConstraints
				{
					CanFireWhileSprinting = (ActionFireMode)field2.GetValue(value),
					CanFireWhileSliding = (ActionFireMode)field3.GetValue(value)
				};
				originalConstraints[instanceID] = value2;
			}
			object value3 = (object)(ActionFireMode)(enableCanFireWhileSprinting.Value ? 1 : ((int)value2.CanFireWhileSprinting));
			object value4 = (object)(ActionFireMode)(enableCanFireWhileSliding.Value ? 1 : ((int)value2.CanFireWhileSliding));
			field2.SetValue(value, value3);
			field3.SetValue(value, value4);
			field.SetValue(gunDataObj, value);
			if (gunDataField != null && gunDataField.FieldType.IsValueType)
			{
				gunDataField.SetValue(gun, gunDataObj);
			}
		}
	}

	private void OnDestroy()
	{
		if (enableCanFireWhileSprinting != null)
		{
			enableCanFireWhileSprinting.SettingChanged -= OnFireConstraintSettingChanged;
		}
		if (enableCanFireWhileSliding != null)
		{
			enableCanFireWhileSliding.SettingChanged -= OnFireConstraintSettingChanged;
		}
		if (configWatcher != null)
		{
			configWatcher.EnableRaisingEvents = false;
			configWatcher.Changed -= OnConfigFileChanged;
			configWatcher.Created -= OnConfigFileChanged;
			configWatcher.Renamed -= OnConfigFileChanged;
			configWatcher.Dispose();
			configWatcher = null;
		}
		Harmony obj = harmony;
		if (obj != null)
		{
			obj.UnpatchSelf();
		}
	}
}
public class SprintToFireData : MonoBehaviour
{
	public bool SprintingLockedBySprintToFire { get; set; }

	public bool PreviousFireInputHeld { get; set; }
}
public static class SprintToFireFixPatches
{
	private static readonly FieldInfo playerField = AccessTools.Field(typeof(Gun), "player");

	private static readonly FieldInfo gunDataField = AccessTools.Field(typeof(Gun), "gunData");

	private static readonly FieldInfo isFireInputHeldField = AccessTools.Field(typeof(Gun), "isFireInputHeld");

	private static readonly MethodInfo tryFireMethod = AccessTools.Method(typeof(Gun), "TryFire", (Type[])null, (Type[])null);

	private static readonly PropertyInfo canFireWithoutAmmoProperty = AccessTools.Property(typeof(Gun), "CanFireWithoutAmmo");

	private static bool IsEnabled
	{
		get
		{
			if (SparrohPlugin.enableSprintToFireFix != null)
			{
				return SparrohPlugin.enableSprintToFireFix.Value;
			}
			return false;
		}
	}

	[HarmonyPatch(typeof(Gun), "CanFireDuringAnimationState")]
	[HarmonyPrefix]
	private static bool CanFireDuringAnimationStatePrefix(Gun __instance, ref bool __result)
	{
		if (!IsEnabled)
		{
			return true;
		}
		try
		{
			if ((bool)isFireInputHeldField.GetValue(__instance))
			{
				__result = true;
				return false;
			}
		}
		catch (Exception ex)
		{
			SparrohPlugin.Logger.LogError((object)("Error in CanFireDuringAnimationState patch: " + ex.Message));
		}
		return true;
	}

	[HarmonyPatch(typeof(Gun), "MinWalkingWeightToFire")]
	[HarmonyPrefix]
	private static bool MinWalkingWeightToFirePrefix(Gun __instance, ref float __result)
	{
		if (!IsEnabled)
		{
			return true;
		}
		try
		{
			if ((bool)isFireInputHeldField.GetValue(__instance))
			{
				__result = 0f;
				return false;
			}
		}
		catch (Exception ex)
		{
			SparrohPlugin.Logger.LogError((object)("Error in MinWalkingWeightToFire patch: " + ex.Message));
		}
		return true;
	}

	[HarmonyPatch(typeof(Gun), "Update")]
	[HarmonyPostfix]
	private static void UpdatePostfix(Gun __instance)
	{
		if (!IsEnabled)
		{
			return;
		}
		try
		{
			bool flag = (bool)isFireInputHeldField.GetValue(__instance);
			SprintToFireData sprintToFireData = ((Component)__instance).gameObject.GetComponent<SprintToFireData>();
			if ((Object)(object)sprintToFireData == (Object)null)
			{
				sprintToFireData = ((Component)__instance).gameObject.AddComponent<SprintToFireData>();
			}
			if (sprintToFireData.PreviousFireInputHeld && !flag && sprintToFireData.SprintingLockedBySprintToFire)
			{
				object? value = playerField.GetValue(__instance);
				Player val = (Player)((value is Player) ? value : null);
				if ((Object)(object)val != (Object)null)
				{
					val.SprintLocks = 0;
					sprintToFireData.SprintingLockedBySprintToFire = false;
					if (val.AutoSprint)
					{
						FieldInfo field = typeof(Player).GetField("wantsToSprint", BindingFlags.Instance | BindingFlags.NonPublic);
						if (field != null)
						{
							field.SetValue(val, true);
						}
					}
				}
			}
			sprintToFireData.PreviousFireInputHeld = flag;
			if (flag)
			{
				AccessTools.Property(typeof(Gun), "WantsToFire").SetValue(__instance, true);
			}
		}
		catch (Exception ex)
		{
			SparrohPlugin.Logger.LogError((object)("Error in Update postfix: " + ex.Message));
		}
	}

	[HarmonyPatch(typeof(Gun), "HandleFiring")]
	[HarmonyPrefix]
	private static bool HandleFiringPrefix(Gun __instance)
	{
		if (!IsEnabled)
		{
			return true;
		}
		try
		{
			object? value = playerField.GetValue(__instance);
			Player val = (Player)((value is Player) ? value : null);
			if ((Object)(object)val == (Object)null)
			{
				return true;
			}
			object value2 = gunDataField.GetValue(__instance);
			if (value2 == null)
			{
				return true;
			}
			bool flag = (bool)isFireInputHeldField.GetValue(__instance);
			if (val.IsSprinting && flag)
			{
				object value3 = value2.GetType().GetField("chargeData").GetValue(value2);
				bool num = (bool)value3.GetType().GetProperty("CanFire").GetValue(value3);
				bool flag2 = (bool)canFireWithoutAmmoProperty.GetValue(__instance);
				if (num && ((double)__instance.RemainingAmmo >= 1.0 || flag2))
				{
					object value4 = value2.GetType().GetField("fireConstraints").GetValue(value2);
					if ((int)value4.GetType().GetField("canFireWhileSprinting").GetValue(value4) != 1)
					{
						val.SprintLocks = 1;
						SprintToFireData component = ((Component)__instance).gameObject.GetComponent<SprintToFireData>();
						if ((Object)(object)component != (Object)null)
						{
							component.SprintingLockedBySprintToFire = true;
						}
					}
					tryFireMethod.Invoke(__instance, null);
					return false;
				}
			}
		}
		catch (Exception ex)
		{
			SparrohPlugin.Logger.LogError((object)("Error in SprintToFireFix patch: " + ex.Message));
			SparrohPlugin.Logger.LogError((object)("Stack trace: " + ex.StackTrace));
		}
		return true;
	}
}
namespace AlwaysFireWhileSprinting
{
	public static class MyPluginInfo
	{
		public const string PLUGIN_GUID = "AlwaysFireWhileSprinting";

		public const string PLUGIN_NAME = "AlwaysFireWhileSprinting";

		public const string PLUGIN_VERSION = "1.0.0";
	}
}
namespace System.Runtime.CompilerServices
{
	[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
	internal sealed class IgnoresAccessChecksToAttribute : Attribute
	{
		public IgnoresAccessChecksToAttribute(string assemblyName)
		{
		}
	}
}