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)
{
}
}
}