Decompiled source of BulletWhiz v1.0.2

Mods/BulletWhiz.dll

Decompiled 2 days ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Resources;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using AudioImportLib;
using BoneLib;
using BoneLib.BoneMenu;
using BulletWhiz;
using HarmonyLib;
using Il2CppSLZ.Marrow;
using MelonLoader;
using MelonLoader.Preferences;
using MelonLoader.Utils;
using Microsoft.CodeAnalysis;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: MelonInfo(typeof(Core), "BulletWhiz", "1.0.2", "Lunobe", null)]
[assembly: MelonGame("Stress Level Zero", "BONELAB")]
[assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")]
[assembly: AssemblyCompany("BulletWhiz")]
[assembly: AssemblyConfiguration("Debug")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("BulletWhiz")]
[assembly: AssemblyTitle("BulletWhiz")]
[assembly: NeutralResourcesLanguage("en-US")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.0.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.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;
		}
	}
}
namespace BulletWhiz
{
	public class Core : MelonMod
	{
		[HarmonyPatch(typeof(Gun))]
		public static class GunPatches
		{
			[HarmonyPatch("OnFire")]
			[HarmonyPostfix]
			public static void OnFirePostfix(Gun __instance)
			{
				//IL_0073: Unknown result type (might be due to invalid IL or missing references)
				//IL_0078: Unknown result type (might be due to invalid IL or missing references)
				//IL_007a: Unknown result type (might be due to invalid IL or missing references)
				//IL_007f: Unknown result type (might be due to invalid IL or missing references)
				//IL_00b4: Unknown result type (might be due to invalid IL or missing references)
				//IL_00ca: Unknown result type (might be due to invalid IL or missing references)
				//IL_00f5: Unknown result type (might be due to invalid IL or missing references)
				//IL_00f6: Unknown result type (might be due to invalid IL or missing references)
				try
				{
					if ((Object)(object)__instance == (Object)null)
					{
						LogDebug("[PATCH] Gun.OnFire called with null instance");
						return;
					}
					LogDebug("[PATCH] Gun.OnFire triggered: " + ((Object)__instance).name);
					bool flag = IsLocalPlayerGun(__instance);
					Transform firePointTransform = __instance.firePointTransform;
					if ((Object)(object)firePointTransform == (Object)null)
					{
						LogDebug("[PATCH] " + ((Object)__instance).name + " has no firePointTransform");
						return;
					}
					Vector3 position = firePointTransform.position;
					Vector3 forward = firePointTransform.forward;
					LogDebug($"[PATCH] {((Object)__instance).name} | Muzzle: {position} | Dir: {forward} | IsLocal: {flag}");
					OnGunFired(__instance, position, forward, flag);
				}
				catch (Exception value)
				{
					LogError($"[PATCH] Gun.OnFire error: {value}");
				}
			}
		}

		public static Core Instance;

		public static List<AudioClip> WhizSounds = new List<AudioClip>();

		private static string FolderPath;

		private static MelonPreferences_Category _prefCategory;

		public static MelonPreferences_Entry<bool> PrefEnabled;

		public static MelonPreferences_Entry<float> PrefVolume;

		public static MelonPreferences_Entry<float> PrefMinDistance;

		public static MelonPreferences_Entry<float> PrefPassDistance;

		public static MelonPreferences_Entry<float> PrefSpeedThreshold;

		public static MelonPreferences_Entry<bool> PrefDebugLogs;

		private static FloatElement _volumeElement;

		private static FloatElement _minDistanceElement;

		private static FloatElement _passDistanceElement;

		private static FloatElement _speedThresholdElement;

		public static bool IsEnabled => PrefEnabled?.Value ?? false;

		public static bool DebugMode => PrefDebugLogs?.Value ?? false;

		public override void OnInitializeMelon()
		{
			Instance = this;
			Log("=== BulletWhiz Initialization Started ===");
			try
			{
				SetupPreferences();
				Log("Preferences initialized");
			}
			catch (Exception value)
			{
				LogError($"Failed to setup preferences: {value}");
			}
			try
			{
				SetupBoneMenu();
				Log("BoneMenu initialized");
			}
			catch (Exception value2)
			{
				LogError($"Failed to setup BoneMenu: {value2}");
			}
			try
			{
				FolderPath = Path.Combine(MelonEnvironment.UserDataDirectory, "BulletWhiz");
				Log("Audio folder path: " + FolderPath);
				if (!Directory.Exists(FolderPath))
				{
					Directory.CreateDirectory(FolderPath);
					Log("Created audio folder");
				}
				LoadAudioFiles();
			}
			catch (Exception value3)
			{
				LogError($"Failed to setup audio folder: {value3}");
			}
			Log($"=== BulletWhiz Ready | Enabled: {IsEnabled} | Sounds: {WhizSounds.Count} ===");
		}

		private void SetupPreferences()
		{
			_prefCategory = MelonPreferences.CreateCategory("BulletWhiz");
			PrefEnabled = _prefCategory.CreateEntry<bool>("Enabled", true, (string)null, (string)null, false, false, (ValueValidator)null, (string)null);
			PrefVolume = _prefCategory.CreateEntry<float>("Volume", 50f, (string)null, (string)null, false, false, (ValueValidator)null, (string)null);
			PrefMinDistance = _prefCategory.CreateEntry<float>("MinDistance", 100f, (string)null, (string)null, false, false, (ValueValidator)null, (string)null);
			PrefPassDistance = _prefCategory.CreateEntry<float>("PassDistance", 5f, (string)null, (string)null, false, false, (ValueValidator)null, (string)null);
			PrefSpeedThreshold = _prefCategory.CreateEntry<float>("SpeedThreshold", 250f, (string)null, (string)null, false, false, (ValueValidator)null, (string)null);
			PrefDebugLogs = _prefCategory.CreateEntry<bool>("DebugLogs", false, (string)null, (string)null, false, false, (ValueValidator)null, (string)null);
			LogDebug($"Prefs loaded - Enabled:{PrefEnabled.Value} Vol:{PrefVolume.Value} MinFiredDist:{PrefMinDistance.Value} PassDist:{PrefPassDistance.Value} Speed:{PrefSpeedThreshold.Value} Debug:{PrefDebugLogs.Value}");
		}

		private void SetupBoneMenu()
		{
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			//IL_008a: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fe: Unknown result type (might be due to invalid IL or missing references)
			//IL_0138: Unknown result type (might be due to invalid IL or missing references)
			Page val = Page.Root.CreatePage("Bullet Whiz", Color.yellow, 0, true);
			LogDebug("Created BoneMenu page");
			val.CreateBool("Enabled", Color.green, PrefEnabled.Value, (Action<bool>)delegate(bool value)
			{
				SafeSetPref("Enabled", delegate
				{
					PrefEnabled.Value = value;
				});
			});
			_volumeElement = val.CreateFloat("Volume", Color.white, PrefVolume.Value, 1f, 0f, 100f, (Action<float>)delegate(float value)
			{
				SafeSetPref("Volume", delegate
				{
					PrefVolume.Value = Mathf.Max(0f, value);
				});
			});
			_minDistanceElement = val.CreateFloat("Min Firing Distance", Color.white, PrefMinDistance.Value, 10f, 0f, 2000f, (Action<float>)delegate(float value)
			{
				SafeSetPref("MinDistance", delegate
				{
					PrefMinDistance.Value = Mathf.Max(0f, value);
				});
			});
			_passDistanceElement = val.CreateFloat("Pass Distance", Color.white, PrefPassDistance.Value, 0.5f, 0.1f, 20f, (Action<float>)delegate(float value)
			{
				SafeSetPref("PassDistance", delegate
				{
					PrefPassDistance.Value = Mathf.Max(0.1f, value);
				});
			});
			_speedThresholdElement = val.CreateFloat("Speed Threshold", Color.white, PrefSpeedThreshold.Value, 1f, 1f, 500f, (Action<float>)delegate(float value)
			{
				SafeSetPref("SpeedThreshold", delegate
				{
					PrefSpeedThreshold.Value = Mathf.Max(1f, value);
				});
			});
			val.CreateFunction("Reset to Defaults", Color.red, (Action)delegate
			{
				ResetToDefaults();
			});
			LogDebug("All BoneMenu elements created");
		}

		private void SafeSetPref(string name, Action action)
		{
			try
			{
				action();
				_prefCategory.SaveToFile(true);
				LogDebug("Pref changed: " + name);
			}
			catch (Exception value)
			{
				LogError($"Failed to save pref '{name}': {value}");
			}
		}

		private void ResetToDefaults()
		{
			try
			{
				PrefVolume.Value = 50f;
				PrefMinDistance.Value = 100f;
				PrefPassDistance.Value = 5f;
				PrefSpeedThreshold.Value = 250f;
				_prefCategory.SaveToFile(true);
				if (_volumeElement != null)
				{
					_volumeElement.Value = 50f;
				}
				if (_minDistanceElement != null)
				{
					_minDistanceElement.Value = 100f;
				}
				if (_passDistanceElement != null)
				{
					_passDistanceElement.Value = 5f;
				}
				if (_speedThresholdElement != null)
				{
					_speedThresholdElement.Value = 250f;
				}
				Log("Settings reset to defaults");
			}
			catch (Exception value)
			{
				LogError($"Failed to reset settings: {value}");
			}
		}

		private void LoadAudioFiles()
		{
			if (!Directory.Exists(FolderPath))
			{
				LogWarning("Audio folder does not exist: " + FolderPath);
				return;
			}
			string[] files = Directory.GetFiles(FolderPath);
			Log($"Found {files.Length} files in audio folder");
			string[] array = files;
			foreach (string text in array)
			{
				string fileName = Path.GetFileName(text);
				try
				{
					LogDebug("Loading audio: " + fileName);
					AudioClip val = API.LoadAudioClip(text, true);
					if ((Object)(object)val != (Object)null)
					{
						WhizSounds.Add(val);
						Log($"Loaded audio: {fileName} (duration: {val.length:F2}s)");
					}
					else
					{
						LogWarning("AudioImportLib returned null for: " + fileName);
					}
				}
				catch (Exception value)
				{
					LogError($"Failed to load audio '{fileName}': {value}");
				}
			}
			Log($"Total audio clips loaded: {WhizSounds.Count}");
		}

		public static void OnGunFired(Gun gun, Vector3 muzzlePos, Vector3 direction, bool isLocalPlayer)
		{
			//IL_00c4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ca: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cb: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d1: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d2: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d3: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d8: Unknown result type (might be due to invalid IL or missing references)
			//IL_01da: Unknown result type (might be due to invalid IL or missing references)
			//IL_01dc: Unknown result type (might be due to invalid IL or missing references)
			//IL_020f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0210: Unknown result type (might be due to invalid IL or missing references)
			//IL_0213: Unknown result type (might be due to invalid IL or missing references)
			//IL_0218: Unknown result type (might be due to invalid IL or missing references)
			//IL_021d: Unknown result type (might be due to invalid IL or missing references)
			//IL_021f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0221: Unknown result type (might be due to invalid IL or missing references)
			//IL_03d4: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				string text = ((gun != null) ? ((Object)gun).name : null) ?? "Unknown";
				if (!IsEnabled)
				{
					LogDebug("[FIRE] " + text + " - Mod disabled, skipping");
					return;
				}
				if (WhizSounds.Count == 0)
				{
					LogDebug("[FIRE] " + text + " - No audio clips loaded, skipping");
					return;
				}
				if (isLocalPlayer)
				{
					LogDebug("[FIRE] " + text + " - LocalOwned, skipping");
					return;
				}
				Transform head = Player.Head;
				if ((Object)(object)head == (Object)null)
				{
					LogDebug("[FIRE] " + text + " - Player.Head is null, skipping");
					return;
				}
				Vector3 position = head.position;
				float num = Vector3.Distance(muzzlePos, position);
				float value = PrefMinDistance.Value;
				LogDebug($"[FIRE] {text} | RemoteOwned | MuzzleDist: {num:F1}m | MinFiringDist: {value:F1}m");
				if (num < value)
				{
					LogDebug($"[FIRE] {text} - Fired too close ({num:F1}m < {value:F1}m), skipping");
					return;
				}
				Vector3 val = position - muzzlePos;
				float num2 = Vector3.Dot(val, direction);
				if (num2 < 0f)
				{
					LogDebug("[FIRE] " + text + " - Shooting away from player, skipping");
					return;
				}
				Vector3 val2 = muzzlePos + direction * num2;
				float num3 = Vector3.Distance(val2, position);
				float value2 = PrefPassDistance.Value;
				LogDebug($"[FIRE] {text} | ClosestApproach: {num3:F2}m | MaxPassDist: {value2:F1}m");
				if (num3 > value2)
				{
					LogDebug($"[FIRE] {text} - Trajectory too far from head ({num3:F2}m > {value2:F1}m), skipping");
				}
				else
				{
					Log($"[WHIZ!] {text} | PassDist: {num3:F2}m (<= {value2:F1}m) | FiredFrom: {num:F1}m (>= {value:F1}m)");
					PlayWhizSound(val2);
				}
			}
			catch (Exception value3)
			{
				LogError($"OnGunFired error: {value3}");
			}
		}

		public static void PlayWhizSound(Vector3 position)
		{
			//IL_00d1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e6: Unknown result type (might be due to invalid IL or missing references)
			if (WhizSounds.Count == 0)
			{
				LogWarning("PlayWhizSound called but no clips loaded");
				return;
			}
			try
			{
				int num = Random.Range(0, WhizSounds.Count);
				AudioClip val = WhizSounds[num];
				float num2 = Mathf.Max(0f, PrefVolume.Value) / 10f;
				LogDebug($"[AUDIO] Playing clip {num} at volume {num2 * 100f:F0}% (raw:{PrefVolume.Value:F0}) at position {position}");
				AudioSource.PlayClipAtPoint(val, position, num2);
				LogDebug("[AUDIO] Playback started");
			}
			catch (Exception value)
			{
				LogError($"PlayWhizSound error: {value}");
			}
		}

		private static bool IsLocalPlayerGun(Gun gun)
		{
			//IL_0029: 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_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			//IL_00df: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e6: Unknown result type (might be due to invalid IL or missing references)
			//IL_008c: 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)
			try
			{
				if ((Object)(object)gun == (Object)null)
				{
					LogDebug("[OWNER] Gun is null, assuming local");
					return true;
				}
				Vector3 position = ((Component)gun).transform.position;
				Transform head = Player.Head;
				Hand leftHand = Player.LeftHand;
				Hand rightHand = Player.RightHand;
				float num = (((Object)(object)head != (Object)null) ? Vector3.Distance(position, head.position) : float.MaxValue);
				float num2 = float.MaxValue;
				float num3 = float.MaxValue;
				try
				{
					if ((Object)(object)leftHand != (Object)null && (Object)(object)((Component)leftHand).transform != (Object)null)
					{
						num2 = Vector3.Distance(position, ((Component)leftHand).transform.position);
					}
				}
				catch (Exception ex)
				{
					LogDebug("[OWNER] LeftHand access error: " + ex.Message);
				}
				try
				{
					if ((Object)(object)rightHand != (Object)null && (Object)(object)((Component)rightHand).transform != (Object)null)
					{
						num3 = Vector3.Distance(position, ((Component)rightHand).transform.position);
					}
				}
				catch (Exception ex2)
				{
					LogDebug("[OWNER] RightHand access error: " + ex2.Message);
				}
				float num4 = Mathf.Min(new float[3] { num, num2, num3 });
				bool flag = num4 < 2f;
				LogDebug($"[OWNER] Gun '{((Object)gun).name}' | HeadDist:{num:F2} LeftDist:{num2:F2} RightDist:{num3:F2} | MinDist:{num4:F2} | IsLocal:{flag}");
				return flag;
			}
			catch (Exception value)
			{
				LogError($"IsLocalPlayerGun error: {value}");
				return true;
			}
		}

		public static void Log(string msg)
		{
			Core instance = Instance;
			if (instance != null)
			{
				((MelonBase)instance).LoggerInstance.Msg(msg);
			}
		}

		public static void LogWarning(string msg)
		{
			Core instance = Instance;
			if (instance != null)
			{
				((MelonBase)instance).LoggerInstance.Warning(msg);
			}
		}

		public static void LogError(string msg)
		{
			Core instance = Instance;
			if (instance != null)
			{
				((MelonBase)instance).LoggerInstance.Error(msg);
			}
		}

		public static void LogDebug(string msg)
		{
			if (DebugMode)
			{
				Core instance = Instance;
				if (instance != null)
				{
					((MelonBase)instance).LoggerInstance.Msg("[DEBUG] " + msg);
				}
			}
		}
	}
}