Decompiled source of AlwaysFireWhileSprinting v1.0.3

BepInEx/plugins/AlwaysFireWhileSprinting.dll

Decompiled 3 weeks 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(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("Sparroh")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.3.0")]
[assembly: AssemblyInformationalVersion("1.0.3")]
[assembly: AssemblyProduct("AlwaysFireWhileSprinting")]
[assembly: AssemblyTitle("AlwaysFireWhileSprinting")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.3.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;
		}
	}
}
public static class ConfigManager
{
	private const float DebounceSeconds = 0.25f;

	private static ConfigFile config;

	private static ManualLogSource logger;

	private static FileSystemWatcher configWatcher;

	private static volatile bool pendingConstraintRefresh;

	private static volatile bool reloadPending;

	private static float lastReloadTime;

	public static ConfigEntry<bool> EnableCanFireWhileSprinting { get; private set; }

	public static ConfigEntry<bool> EnableCanFireWhileSliding { get; private set; }

	public static ConfigEntry<bool> EnableSprintToFireFix { get; private set; }

	public static void Initialize(ConfigFile configFile, ManualLogSource log)
	{
		config = configFile;
		logger = log;
		EnableCanFireWhileSprinting = config.Bind<bool>("General", "Can Fire While Sprinting", true, "Allows firing weapons while sprinting.");
		EnableCanFireWhileSliding = config.Bind<bool>("General", "Can Fire While Sliding", true, "Allows firing weapons while sliding.");
		EnableSprintToFireFix = config.Bind<bool>("General", "Sprint To Fire Fix", false, "Enables the Sprint-to-Fire fix that allows immediate firing while sprinting and proper sprint resume behavior.");
		EnableCanFireWhileSprinting.SettingChanged += OnSettingChanged;
		EnableCanFireWhileSliding.SettingChanged += OnSettingChanged;
		EnableSprintToFireFix.SettingChanged += OnSettingChanged;
		try
		{
			SetupFileWatcher();
		}
		catch (Exception ex)
		{
			logger.LogError((object)("Error setting up config file watcher: " + ex.Message));
		}
	}

	public static void Tick()
	{
		if (!reloadPending || Time.unscaledTime - lastReloadTime < 0.25f)
		{
			return;
		}
		reloadPending = false;
		lastReloadTime = Time.unscaledTime;
		try
		{
			config.Reload();
			pendingConstraintRefresh = true;
			logger.LogInfo((object)"Config reloaded from disk.");
		}
		catch (Exception ex)
		{
			logger.LogError((object)("Error reloading config: " + ex.Message));
		}
	}

	public static bool ConsumePendingRefresh()
	{
		if (!pendingConstraintRefresh)
		{
			return false;
		}
		pendingConstraintRefresh = false;
		return true;
	}

	public static void Dispose()
	{
		if (EnableCanFireWhileSprinting != null)
		{
			EnableCanFireWhileSprinting.SettingChanged -= OnSettingChanged;
		}
		if (EnableCanFireWhileSliding != null)
		{
			EnableCanFireWhileSliding.SettingChanged -= OnSettingChanged;
		}
		if (EnableSprintToFireFix != null)
		{
			EnableSprintToFireFix.SettingChanged -= OnSettingChanged;
		}
		if (configWatcher != null)
		{
			configWatcher.EnableRaisingEvents = false;
			configWatcher.Changed -= OnConfigFileChanged;
			configWatcher.Created -= OnConfigFileChanged;
			configWatcher.Renamed -= OnConfigFileChanged;
			configWatcher.Dispose();
			configWatcher = null;
		}
	}

	private static 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 static void OnConfigFileChanged(object sender, FileSystemEventArgs e)
	{
		reloadPending = true;
	}

	private static void OnSettingChanged(object sender, EventArgs e)
	{
		pendingConstraintRefresh = true;
	}
}
public static class FireConstraintsPatches
{
	private struct OriginalFireConstraints
	{
		public ActionFireMode CanFireWhileSprinting;

		public ActionFireMode CanFireWhileSliding;
	}

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

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

	[HarmonyPatch(typeof(Gun), "Setup", new Type[]
	{
		typeof(Player),
		typeof(PlayerAnimation),
		typeof(IGear)
	})]
	[HarmonyPrefix]
	private 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);
				}
			}
			SparrohPlugin.Logger.LogInfo((object)$"Re-applied fire constraints to {array.Length} gun(s).");
		}
		catch (Exception ex)
		{
			SparrohPlugin.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)
		{
			SparrohPlugin.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)(ConfigManager.EnableCanFireWhileSprinting.Value ? 1 : ((int)value.CanFireWhileSprinting));
		gunData.fireConstraints.canFireWhileSliding = (ActionFireMode)(ConfigManager.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)(ConfigManager.EnableCanFireWhileSprinting.Value ? 1 : ((int)value2.CanFireWhileSprinting));
			object value4 = (object)(ActionFireMode)(ConfigManager.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);
			}
		}
	}
}
[BepInPlugin("sparroh.alwaysfirewhilesprinting", "AlwaysFireWhileSprinting", "1.0.3")]
[MycoMod(/*Could not decode attribute arguments.*/)]
public class SparrohPlugin : BaseUnityPlugin
{
	public const string PluginGUID = "sparroh.alwaysfirewhilesprinting";

	public const string PluginName = "AlwaysFireWhileSprinting";

	public const string PluginVersion = "1.0.3";

	internal static ManualLogSource Logger;

	private Harmony harmony;

	private void Awake()
	{
		//IL_0021: Unknown result type (might be due to invalid IL or missing references)
		//IL_002b: Expected O, but got Unknown
		Logger = ((BaseUnityPlugin)this).Logger;
		ConfigManager.Initialize(((BaseUnityPlugin)this).Config, Logger);
		harmony = new Harmony("sparroh.alwaysfirewhilesprinting");
		try
		{
			harmony.PatchAll(typeof(FireConstraintsPatches));
			harmony.PatchAll(typeof(SprintToFireFixPatches));
			Logger.LogInfo((object)("Harmony patches applied (SprintToFireFix currently " + (ConfigManager.EnableSprintToFireFix.Value ? "enabled" : "disabled") + ")."));
		}
		catch (Exception ex)
		{
			Logger.LogError((object)("Error applying patches: " + ex.Message));
		}
		Logger.LogInfo((object)"AlwaysFireWhileSprinting v1.0.3 loaded successfully.");
	}

	private void Update()
	{
		ConfigManager.Tick();
		if (ConfigManager.ConsumePendingRefresh())
		{
			FireConstraintsPatches.ApplyFireConstraintsToAllGuns();
		}
	}

	private void OnDestroy()
	{
		ConfigManager.Dispose();
		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 readonly PropertyInfo wantsToFireProperty = AccessTools.Property(typeof(Gun), "WantsToFire");

	private static readonly FieldInfo wantsToSprintField = AccessTools.Field(typeof(Player), "wantsToSprint");

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

	private static bool CanOverrideFireGates(Gun gun, bool isFireInputHeld)
	{
		if (!isFireInputHeld || gun.Reloading)
		{
			return false;
		}
		try
		{
			bool flag = canFireWithoutAmmoProperty != null && (bool)canFireWithoutAmmoProperty.GetValue(gun);
			if (gun.RemainingAmmo >= 1f || flag)
			{
				return true;
			}
		}
		catch (Exception ex)
		{
			SparrohPlugin.Logger.LogError((object)("Error checking fire override eligibility: " + ex.Message));
		}
		return false;
	}

	[HarmonyPatch(typeof(Gun), "CanFireDuringAnimationState")]
	[HarmonyPrefix]
	private static bool CanFireDuringAnimationStatePrefix(Gun __instance, ref bool __result)
	{
		if (!IsEnabled)
		{
			return true;
		}
		try
		{
			bool isFireInputHeld = (bool)isFireInputHeldField.GetValue(__instance);
			if (CanOverrideFireGates(__instance, isFireInputHeld))
			{
				__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
		{
			bool isFireInputHeld = (bool)isFireInputHeldField.GetValue(__instance);
			if (CanOverrideFireGates(__instance, isFireInputHeld))
			{
				__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 && wantsToSprintField != null)
					{
						wantsToSprintField.SetValue(val, true);
					}
				}
			}
			sprintToFireData.PreviousFireInputHeld = flag;
			if (CanOverrideFireGates(__instance, flag) && wantsToFireProperty != null)
			{
				wantsToFireProperty.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
		{
			if (__instance.Reloading)
			{
				return true;
			}
			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)
			{
				return true;
			}
			bool flag2 = canFireWithoutAmmoProperty != null && (bool)canFireWithoutAmmoProperty.GetValue(__instance);
			if (__instance.RemainingAmmo < 1f && !flag2)
			{
				return true;
			}
			FieldInfo field = value2.GetType().GetField("chargeData");
			if (field == null)
			{
				return true;
			}
			object value3 = field.GetValue(value2);
			if (value3 == null)
			{
				return true;
			}
			PropertyInfo property = value3.GetType().GetProperty("CanFire");
			if (property == null)
			{
				return true;
			}
			if (!(bool)property.GetValue(value3))
			{
				return true;
			}
			FieldInfo field2 = value2.GetType().GetField("fireConstraints");
			if (field2 == null)
			{
				return true;
			}
			object value4 = field2.GetValue(value2);
			if (value4 == null)
			{
				return true;
			}
			FieldInfo field3 = value4.GetType().GetField("canFireWhileSprinting");
			if (field3 == null)
			{
				return true;
			}
			if ((int)field3.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.3";
	}
}
namespace System.Runtime.CompilerServices
{
	[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
	internal sealed class IgnoresAccessChecksToAttribute : Attribute
	{
		public IgnoresAccessChecksToAttribute(string assemblyName)
		{
		}
	}
}