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.Math; using Pigeon.Movement; using Unity.Netcode; using UnityEngine; using UnityEngine.InputSystem; using UnityEngine.InputSystem.Interactions; [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("SalvoMacro")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0")] [assembly: AssemblyProduct("SalvoMacro")] [assembly: AssemblyTitle("SalvoMacro")] [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.salvomacro", "SalvoMacro", "1.2.0")] [MycoMod(/*Could not decode attribute arguments.*/)] public class SparrohPlugin : BaseUnityPlugin { public enum ActivationMode { None, Toggle, Always } public const string PluginGUID = "sparroh.salvomacro"; public const string PluginName = "SalvoMacro"; public const string PluginVersion = "1.2.0"; internal static ManualLogSource Logger; public static ConfigEntry salvoMode; public static ConfigEntry useZeroLockRelease; public static ConfigEntry suppressSalvoModelAlways; private FileSystemWatcher configWatcher; private volatile bool configReloadPending; private int lastConfigChangeTick; private const int ConfigReloadDebounceMs = 250; internal static SparrohPlugin Instance { get; private set; } private void Awake() { //IL_00a4: Unknown result type (might be due to invalid IL or missing references) Instance = this; Logger = ((BaseUnityPlugin)this).Logger; salvoMode = ((BaseUnityPlugin)this).Config.Bind("General", "SalvoActivationMode", ActivationMode.Toggle, "None: default manual firing. Toggle: Slot3 toggles auto-fire on/off. Always: auto-fire whenever charged."); useZeroLockRelease = ((BaseUnityPlugin)this).Config.Bind("General", "UseZeroLockRelease", true, "When true (recommended), auto-fire uses vanilla zero-lock release (crosshair point + spread). When false, instantly fills target locks via FindSalvoTarget before firing."); suppressSalvoModelAlways = ((BaseUnityPlugin)this).Config.Bind("General", "SuppressSalvoModelAlways", false, "Always hide the 3D salvo launcher model to save screen space (including manual aim). Auto-fire never shows the model regardless."); salvoMode.SettingChanged += delegate { WingsuitPatches.ResetToggle(); }; try { WingsuitPatches.InitializeAccess(); new Harmony("sparroh.salvomacro").PatchAll(typeof(WingsuitPatches)); Logger.LogInfo((object)"SalvoMacro v1.2.0 loaded"); } catch (Exception ex) { Logger.LogError((object)("Failed to patch methods: " + ex)); } SetupConfigWatcher(); } private void Update() { if (!configReloadPending || Environment.TickCount - lastConfigChangeTick < 250) { return; } configReloadPending = false; try { ((BaseUnityPlugin)this).Config.Reload(); WingsuitPatches.ResetToggle(); Logger.LogInfo((object)"Config reloaded."); } catch (Exception ex) { Logger.LogError((object)("Failed to reload config: " + ex.Message)); } } private void SetupConfigWatcher() { try { string configFilePath = ((BaseUnityPlugin)this).Config.ConfigFilePath; string directoryName = Path.GetDirectoryName(configFilePath); string fileName = Path.GetFileName(configFilePath); if (string.IsNullOrEmpty(directoryName) || string.IsNullOrEmpty(fileName)) { Logger.LogWarning((object)"Could not set up config hot-reload: invalid config path."); return; } configWatcher = new FileSystemWatcher(directoryName, fileName) { NotifyFilter = (NotifyFilters.FileName | NotifyFilters.Size | NotifyFilters.LastWrite), EnableRaisingEvents = true }; configWatcher.Changed += OnConfigFileChanged; configWatcher.Created += OnConfigFileChanged; configWatcher.Renamed += OnConfigFileRenamed; } catch (Exception ex) { Logger.LogError((object)("Failed to set up config file watcher: " + ex.Message)); } } private void OnConfigFileChanged(object sender, FileSystemEventArgs e) { lastConfigChangeTick = Environment.TickCount; configReloadPending = true; } private void OnConfigFileRenamed(object sender, RenamedEventArgs e) { string fileName = Path.GetFileName(((BaseUnityPlugin)this).Config.ConfigFilePath); if (string.Equals(e.Name, fileName, StringComparison.OrdinalIgnoreCase) || string.Equals(e.OldName, fileName, StringComparison.OrdinalIgnoreCase)) { lastConfigChangeTick = Environment.TickCount; configReloadPending = true; } } private void OnDestroy() { if (configWatcher != null) { configWatcher.EnableRaisingEvents = false; configWatcher.Changed -= OnConfigFileChanged; configWatcher.Created -= OnConfigFileChanged; configWatcher.Renamed -= OnConfigFileRenamed; configWatcher.Dispose(); configWatcher = null; } } } [HarmonyPatch(typeof(Wingsuit))] public static class WingsuitPatches { public static bool salvoAutoEnabled; private static FieldInfo isSalvoActiveField; private static FieldInfo salvoLockOrFireTimeField; private static FieldInfo salvoModelField; private static FieldInfo salvoAnimationTimeField; private static PropertyInfo maxSalvoLocksProperty; private static MethodInfo addExtraHealingRocketMethod; private static MethodInfo findSalvoTargetMethod; private static bool accessReady; public static void InitializeAccess() { isSalvoActiveField = AccessTools.Field(typeof(Wingsuit), "isSalvoActive"); salvoLockOrFireTimeField = AccessTools.Field(typeof(Wingsuit), "salvoLockOrFireTime"); salvoModelField = AccessTools.Field(typeof(Wingsuit), "salvoModel"); salvoAnimationTimeField = AccessTools.Field(typeof(Wingsuit), "salvoAnimationTime"); maxSalvoLocksProperty = AccessTools.Property(typeof(Wingsuit), "MaxSalvoLocks"); addExtraHealingRocketMethod = AccessTools.Method(typeof(Wingsuit), "AddExtraHealingRocket", (Type[])null, (Type[])null); findSalvoTargetMethod = AccessTools.Method(typeof(Wingsuit), "FindSalvoTarget", (Type[])null, (Type[])null); accessReady = isSalvoActiveField != null && salvoLockOrFireTimeField != null && maxSalvoLocksProperty != null; if (!accessReady) { SparrohPlugin.Logger.LogError((object)"Failed to resolve one or more Wingsuit members for SalvoMacro."); } } public static void ResetToggle() { salvoAutoEnabled = false; } private static bool IsAutoFireActive() { return SparrohPlugin.salvoMode.Value switch { SparrohPlugin.ActivationMode.Always => true, SparrohPlugin.ActivationMode.Toggle => salvoAutoEnabled, _ => false, }; } [HarmonyPatch("OnSalvoPressed")] [HarmonyPrefix] private static bool OnSalvoPressedPrefix(CallbackContext context) { if (SparrohPlugin.salvoMode.Value != SparrohPlugin.ActivationMode.Toggle) { return true; } if (((CallbackContext)(ref context)).interaction is TapInteraction) { return true; } salvoAutoEnabled = !salvoAutoEnabled; SparrohPlugin.Logger.LogDebug((object)("Salvo auto-fire " + (salvoAutoEnabled ? "enabled" : "disabled"))); return false; } [HarmonyPatch("FixedUpdate")] [HarmonyPostfix] private static void FixedUpdatePostfix(Wingsuit __instance) { if (!accessReady || !((NetworkBehaviour)__instance).IsOwner || !IsAutoFireActive()) { return; } try { TryAutoFire(__instance); } catch (Exception ex) { SparrohPlugin.Logger.LogError((object)("Error in salvo auto-fire: " + ex)); } } [HarmonyPatch("Update")] [HarmonyPostfix] private static void UpdatePostfix(Wingsuit __instance) { if (!SparrohPlugin.suppressSalvoModelAlways.Value || !((NetworkBehaviour)__instance).IsOwner) { return; } try { SuppressSalvoModel(__instance); } catch (Exception ex) { SparrohPlugin.Logger.LogError((object)("Error suppressing salvo model: " + ex.Message)); } } [HarmonyPatch("OnSalvoPressed")] [HarmonyPostfix] private static void OnSalvoPressedPostfix(Wingsuit __instance) { if (!SparrohPlugin.suppressSalvoModelAlways.Value || !((NetworkBehaviour)__instance).IsOwner) { return; } try { SuppressSalvoModel(__instance); } catch (Exception ex) { SparrohPlugin.Logger.LogError((object)("Error suppressing salvo model on press: " + ex.Message)); } } private static void SuppressSalvoModel(Wingsuit wingsuit) { if (!(salvoModelField == null)) { object? value = salvoModelField.GetValue(wingsuit); Transform val = (Transform)((value is Transform) ? value : null); if ((Object)(object)val != (Object)null && ((Component)val).gameObject.activeSelf) { ((Component)val).gameObject.SetActive(false); } if (salvoAnimationTimeField != null) { salvoAnimationTimeField.SetValue(wingsuit, 0f); } } } private static void TryAutoFire(Wingsuit wingsuit) { //IL_00be: Unknown result type (might be due to invalid IL or missing references) if ((bool)isSalvoActiveField.GetValue(wingsuit)) { return; } List salvoLocks = wingsuit.SalvoLocks; List salvoLockPositions = wingsuit.SalvoLockPositions; if (salvoLocks == null || salvoLockPositions == null || salvoLocks.Count > 0) { return; } Cooldown rocketSalvoCooldown = wingsuit.RocketSalvoCooldown; if (rocketSalvoCooldown == null || !((CooldownData)(ref rocketSalvoCooldown.data)).IsCharged) { return; } int num = (int)maxSalvoLocksProperty.GetValue(wingsuit); if (num > 0) { ref WingsuitData data = ref wingsuit.Data; salvoLocks.Clear(); salvoLockPositions.Clear(); salvoLockOrFireTimeField.SetValue(wingsuit, -99f); if (SparrohPlugin.useZeroLockRelease.Value) { BuildZeroLockRelease(wingsuit, salvoLocks, salvoLockPositions, num, ref data); } else { BuildInstantTargetLocks(wingsuit, salvoLocks, num); } if (salvoLocks.Count == 0) { BuildZeroLockRelease(wingsuit, salvoLocks, salvoLockPositions, num, ref data); } if (UpgradeFlagsExtensions.IsEnabled(wingsuit.UpgradeFlags, (WingsuitUpgradeFlags)16) && addExtraHealingRocketMethod != null) { addExtraHealingRocketMethod.Invoke(wingsuit, null); } ((CooldownData)(ref rocketSalvoCooldown.data)).UseCharge(); if (data.fuelAddedOnSalvoFire > 0f) { float num2 = Mathf.LerpUnclamped(1f, 0.13f, Mathf.InverseLerp(2f, 20f, (float)rocketSalvoCooldown.MaxCharges)); wingsuit.AddCharge(data.fuelAddedOnSalvoFire * num2); } } } private static void BuildZeroLockRelease(Wingsuit wingsuit, List locks, List lockPositions, int maxLocks, ref WingsuitData data) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Unknown result type (might be due to invalid IL or missing references) //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_0056: 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_006d: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Unknown result type (might be due to invalid IL or missing references) RaycastHit val2 = default(RaycastHit); Vector3 val = ((!IBullet.RaycastForBullet(PlayerLook.Position, PlayerLook.Forward, data.maxSalvoLockDistance, 10241, 0f, ref val2)) ? (PlayerLook.Position + PlayerLook.Forward * data.maxSalvoLockDistance) : ((RaycastHit)(ref val2)).point); for (int i = 0; i < maxLocks; i++) { locks.Add(null); lockPositions.Add(val + ((Random)(ref data.salvoSpread.spreadRandom)).InsideUnitSphere() * 4f); } } private static void BuildInstantTargetLocks(Wingsuit wingsuit, List locks, int maxLocks) { //IL_0055: 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) if (findSalvoTargetMethod == null) { return; } for (int i = 0; i < maxLocks; i++) { if (!(bool)findSalvoTargetMethod.Invoke(wingsuit, null)) { break; } salvoLockOrFireTimeField.SetValue(wingsuit, -99f); } if (locks.Count > 0 && locks.Count < maxLocks && !UpgradeFlagsExtensions.IsEnabled(wingsuit.UpgradeFlags, (WingsuitUpgradeFlags)8)) { List salvoLockPositions = wingsuit.SalvoLockPositions; int index = locks.Count - 1; for (int j = locks.Count; j < maxLocks; j++) { locks.Add(locks[index]); salvoLockPositions.Add(salvoLockPositions[index]); } } } } namespace SalvoMacro { public static class MyPluginInfo { public const string PLUGIN_GUID = "SalvoMacro"; public const string PLUGIN_NAME = "SalvoMacro"; 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) { } } }