using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BoneLib;
using BoneLib.BoneMenu;
using HarmonyLib;
using Il2CppInterop.Runtime.InteropTypes.Arrays;
using Il2CppSLZ.Marrow;
using MelonLoader;
using MelonLoader.Preferences;
using NearMissCrack;
using UnityEngine;
using UnityEngine.UI;
[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: MelonInfo(typeof(Mod), "Snap!", "1.5.0", "NearMissCrack", null)]
[assembly: MelonAdditionalDependencies(new string[] { "BoneLib" })]
[assembly: MelonGame("Stress Level Zero", "BONELAB")]
[assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = "")]
[assembly: AssemblyCompany("NearMissCrack")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("NearMissCrack")]
[assembly: AssemblyTitle("NearMissCrack")]
[assembly: AssemblyVersion("1.0.0.0")]
namespace NearMissCrack;
public sealed class Mod : MelonMod
{
private static readonly AudioClip[] clips = (AudioClip[])(object)new AudioClip[8];
private static readonly Random soundRandom = new Random();
private static AudioClip pistolWizz1;
private static AudioClip pistolWizz3;
private static FieldInfo playbackGunField;
private static readonly HashSet<int> played = new HashSet<int>();
private static MelonPreferences_Entry<bool> enabled;
private static MelonPreferences_Entry<bool> checkWalls;
private static int shotsSeen;
private static int noTracking;
private static int pointingAway;
private static int outsideRadius;
private static int blocked;
private static string lastObstacle = "none";
private static float nearestPass = float.PositiveInfinity;
private static int cracksPlayed;
private static MelonPreferences_Entry<float> radius;
private static MelonPreferences_Entry<float> volume;
private static MelonPreferences_Entry<bool> vignetteEnabled;
private static MelonPreferences_Entry<float> vignetteStrength;
private static MelonPreferences_Entry<float> vignetteDuration;
private static RawImage vignetteImage;
private static float vignetteTimeLeft;
public override void OnInitializeMelon()
{
//IL_0061: Unknown result type (might be due to invalid IL or missing references)
//IL_0074: Unknown result type (might be due to invalid IL or missing references)
//IL_00ae: Unknown result type (might be due to invalid IL or missing references)
//IL_00f7: Unknown result type (might be due to invalid IL or missing references)
//IL_0157: Unknown result type (might be due to invalid IL or missing references)
//IL_01dd: Unknown result type (might be due to invalid IL or missing references)
//IL_0217: Unknown result type (might be due to invalid IL or missing references)
//IL_0260: Unknown result type (might be due to invalid IL or missing references)
//IL_02a9: Unknown result type (might be due to invalid IL or missing references)
//IL_02c6: Unknown result type (might be due to invalid IL or missing references)
//IL_0309: Unknown result type (might be due to invalid IL or missing references)
//IL_031e: Unknown result type (might be due to invalid IL or missing references)
//IL_032b: Expected O, but got Unknown
//IL_032b: Expected O, but got Unknown
//IL_0359: Unknown result type (might be due to invalid IL or missing references)
//IL_0366: Expected O, but got Unknown
//IL_03c7: Unknown result type (might be due to invalid IL or missing references)
//IL_03d4: Expected O, but got Unknown
MelonPreferences_Category obj = MelonPreferences.CreateCategory("NearMissCrack");
radius = obj.CreateEntry<float>("Radius", 0.75f, (string)null, (string)null, false, false, (ValueValidator)null, (string)null);
volume = obj.CreateEntry<float>("Volume", 0.8f, (string)null, (string)null, false, false, (ValueValidator)null, (string)null);
enabled = obj.CreateEntry<bool>("Enabled", true, (string)null, (string)null, false, false, (ValueValidator)null, (string)null);
Page val = Page.Root.CreatePage("Snap!", Color.cyan, 0, true);
val.CreateBool("Enabled", Color.white, enabled.Value, (Action<bool>)delegate(bool value)
{
enabled.Value = value;
MelonPreferences.Save();
});
val.CreateFloat("Volume", Color.white, volume.Value, 0.05f, 0f, 1f, (Action<float>)delegate(float value)
{
volume.Value = value;
MelonPreferences.Save();
});
val.CreateFloat("Radius (m)", Color.white, radius.Value, 0.25f, 0.25f, 5f, (Action<float>)delegate(float value)
{
radius.Value = value;
MelonPreferences.Save();
});
checkWalls = obj.CreateEntry<bool>("CheckWalls", true, (string)null, (string)null, false, false, (ValueValidator)null, (string)null);
val.CreateBool("Check Walls", Color.white, checkWalls.Value, (Action<bool>)delegate(bool value)
{
checkWalls.Value = value;
MelonPreferences.Save();
});
vignetteEnabled = obj.CreateEntry<bool>("VignetteEnabled", true, (string)null, (string)null, false, false, (ValueValidator)null, (string)null);
vignetteStrength = obj.CreateEntry<float>("VignetteStrength", 0.65f, (string)null, (string)null, false, false, (ValueValidator)null, (string)null);
vignetteDuration = obj.CreateEntry<float>("VignetteDuration", 0.22f, (string)null, (string)null, false, false, (ValueValidator)null, (string)null);
val.CreateBool("Suppression Vignette", Color.white, vignetteEnabled.Value, (Action<bool>)delegate(bool value)
{
vignetteEnabled.Value = value;
MelonPreferences.Save();
});
val.CreateFloat("Vignette Strength", Color.white, vignetteStrength.Value, 0.05f, 0f, 0.8f, (Action<float>)delegate(float value)
{
vignetteStrength.Value = value;
MelonPreferences.Save();
});
val.CreateFloat("Vignette Duration", Color.white, vignetteDuration.Value, 0.02f, 0.05f, 1f, (Action<float>)delegate(float value)
{
vignetteDuration.Value = value;
MelonPreferences.Save();
});
val.CreateFunction("Test Vignette", Color.green, (Action)TriggerVignette);
val.CreateFunction("Test Sound", Color.green, (Action)delegate
{
//IL_0100: Unknown result type (might be due to invalid IL or missing references)
//IL_0106: Unknown result type (might be due to invalid IL or missing references)
//IL_0110: Unknown result type (might be due to invalid IL or missing references)
//IL_0115: Unknown result type (might be due to invalid IL or missing references)
Transform head = Player.Head;
((MelonBase)this).LoggerInstance.Msg($"Test Sound. Projectiles seen: {shotsSeen}. Cracks played: {cracksPlayed}. No tracking: {noTracking}, pointing away: {pointingAway}, outside radius: {outsideRadius}, nearest pass: {nearestPass:F2} m, blocked: {blocked} ({lastObstacle}).");
if ((Object)(object)head != (Object)null)
{
PlayCrack(head.position + head.right * 0.5f);
}
});
((MelonBase)this).HarmonyInstance.Patch((MethodBase)AccessTools.Method(typeof(Projectile), "Update", (Type[])null, (Type[])null), new HarmonyMethod(typeof(Mod), "BeforeProjectileUpdate", (Type[])null), new HarmonyMethod(typeof(Mod), "AfterProjectileUpdate", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
((MelonBase)this).HarmonyInstance.Patch((MethodBase)AccessTools.Method(typeof(Projectile), "OnEnable", (Type[])null, (Type[])null), (HarmonyMethod)null, new HarmonyMethod(typeof(Mod), "ResetProjectile", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
Type type = AccessTools.TypeByName("Timeline.WorldRecording.Recorders.GunRecorder");
if (type != null)
{
playbackGunField = AccessTools.Field(type, "playbackGun");
MethodInfo methodInfo = AccessTools.Method(type, "PlayGunSFX", (Type[])null, (Type[])null);
if (playbackGunField != null && methodInfo != null)
{
((MelonBase)this).HarmonyInstance.Patch((MethodBase)methodInfo, (HarmonyMethod)null, new HarmonyMethod(typeof(Mod), "OnTimelineShot", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
((MelonBase)this).LoggerInstance.Msg("Timeline replay gunshot support enabled.");
}
}
((MelonBase)this).LoggerInstance.Msg("Ready. Open BoneMenu > Snap! > Test Sound.");
}
private static void OnTimelineShot(object __instance)
{
//IL_006a: Unknown result type (might be due to invalid IL or missing references)
//IL_006f: Unknown result type (might be due to invalid IL or missing references)
//IL_0071: Unknown result type (might be due to invalid IL or missing references)
//IL_0077: Unknown result type (might be due to invalid IL or missing references)
//IL_007c: Unknown result type (might be due to invalid IL or missing references)
//IL_0081: Unknown result type (might be due to invalid IL or missing references)
//IL_0094: Unknown result type (might be due to invalid IL or missing references)
//IL_0099: Unknown result type (might be due to invalid IL or missing references)
//IL_009c: Unknown result type (might be due to invalid IL or missing references)
//IL_00a1: Unknown result type (might be due to invalid IL or missing references)
//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
//IL_00a9: Unknown result type (might be due to invalid IL or missing references)
//IL_00ae: Unknown result type (might be due to invalid IL or missing references)
//IL_0140: Unknown result type (might be due to invalid IL or missing references)
//IL_0137: Unknown result type (might be due to invalid IL or missing references)
//IL_010c: Unknown result type (might be due to invalid IL or missing references)
//IL_0111: Unknown result type (might be due to invalid IL or missing references)
//IL_0117: Unknown result type (might be due to invalid IL or missing references)
//IL_011c: Unknown result type (might be due to invalid IL or missing references)
//IL_0121: Unknown result type (might be due to invalid IL or missing references)
shotsSeen++;
if (!enabled.Value || volume.Value <= 0f)
{
return;
}
object? value = playbackGunField.GetValue(__instance);
Gun val = (Gun)((value is Gun) ? value : null);
Transform head = Player.Head;
if ((Object)(object)val == (Object)null || (Object)(object)val.firePointTransform == (Object)null || (Object)(object)head == (Object)null)
{
return;
}
Transform firePointTransform = val.firePointTransform;
Vector3 forward = firePointTransform.forward;
float num = Vector3.Dot(head.position - firePointTransform.position, forward);
if (num <= 0f)
{
return;
}
Vector3 val2 = firePointTransform.position + forward * num;
float num2 = Vector3.Distance(head.position, val2);
nearestPass = Mathf.Min(nearestPass, num2);
if (num2 > radius.Value)
{
return;
}
float num3 = Mathf.Max(0f, num - radius.Value - 0.25f);
if ((!checkWalls.Value || !(num3 > 0f) || !Physics.Raycast(firePointTransform.position + forward * 0.25f, forward, num3, -5, (QueryTriggerInteraction)1)) && (IsPistol(val) ? PlayPistolWizz(val2) : PlayCrack(val2)))
{
cracksPlayed++;
TriggerVignette();
if (cracksPlayed <= 5)
{
MelonLogger.Msg($"Timeline near-miss crack played at {num2:F2} m.");
}
}
}
private static void ResetProjectile(Projectile __instance)
{
played.Remove(((Object)__instance).GetInstanceID());
shotsSeen++;
}
private static void BeforeProjectileUpdate(Projectile __instance, out Vector3 __state)
{
//IL_0007: Unknown result type (might be due to invalid IL or missing references)
//IL_000c: Unknown result type (might be due to invalid IL or missing references)
__state = ((Component)__instance).transform.position;
}
private static void AfterProjectileUpdate(Projectile __instance, Vector3 __state)
{
//IL_0052: Unknown result type (might be due to invalid IL or missing references)
//IL_0057: Unknown result type (might be due to invalid IL or missing references)
//IL_0058: Unknown result type (might be due to invalid IL or missing references)
//IL_005d: Unknown result type (might be due to invalid IL or missing references)
//IL_0070: Unknown result type (might be due to invalid IL or missing references)
//IL_0075: Unknown result type (might be due to invalid IL or missing references)
//IL_0076: Unknown result type (might be due to invalid IL or missing references)
//IL_007b: Unknown result type (might be due to invalid IL or missing references)
//IL_00a1: Unknown result type (might be due to invalid IL or missing references)
//IL_00a2: Unknown result type (might be due to invalid IL or missing references)
//IL_00a5: Unknown result type (might be due to invalid IL or missing references)
//IL_00aa: Unknown result type (might be due to invalid IL or missing references)
//IL_00af: Unknown result type (might be due to invalid IL or missing references)
//IL_00b2: Unknown result type (might be due to invalid IL or missing references)
//IL_00b7: Unknown result type (might be due to invalid IL or missing references)
//IL_00ec: Unknown result type (might be due to invalid IL or missing references)
//IL_00ee: Unknown result type (might be due to invalid IL or missing references)
//IL_00ef: Unknown result type (might be due to invalid IL or missing references)
//IL_00f4: Unknown result type (might be due to invalid IL or missing references)
//IL_0169: Unknown result type (might be due to invalid IL or missing references)
//IL_0114: Unknown result type (might be due to invalid IL or missing references)
//IL_0115: Unknown result type (might be due to invalid IL or missing references)
//IL_0119: Unknown result type (might be due to invalid IL or missing references)
//IL_0123: Unknown result type (might be due to invalid IL or missing references)
if (!enabled.Value || volume.Value <= 0f || played.Contains(((Object)__instance).GetInstanceID()))
{
return;
}
Transform head = Player.Head;
if ((Object)(object)head == (Object)null)
{
noTracking++;
return;
}
Vector3 val = ((Component)__instance).transform.position - __state;
float sqrMagnitude = ((Vector3)(ref val)).sqrMagnitude;
if (sqrMagnitude < 1E-06f)
{
return;
}
float num = Vector3.Dot(head.position - __state, val);
if (num <= 0f)
{
pointingAway++;
return;
}
float num2 = Mathf.Clamp01(num / sqrMagnitude);
Vector3 val2 = __state + val * num2;
float num3 = Vector3.Distance(head.position, val2);
nearestPass = Mathf.Min(nearestPass, num3);
if (num3 > radius.Value)
{
outsideRadius++;
return;
}
Vector3 val3 = val2 - __state;
float magnitude = ((Vector3)(ref val3)).magnitude;
RaycastHit val4 = default(RaycastHit);
if (checkWalls.Value && magnitude > 0.001f && Physics.Raycast(__state, val3 / magnitude, ref val4, magnitude, LayerMask.op_Implicit(__instance.collisionLayers), (QueryTriggerInteraction)1))
{
blocked++;
lastObstacle = (((Object)(object)((RaycastHit)(ref val4)).collider == (Object)null) ? "unknown" : ((Object)((RaycastHit)(ref val4)).collider).name);
}
else if (PlayCrack(val2))
{
played.Add(((Object)__instance).GetInstanceID());
cracksPlayed++;
TriggerVignette();
if (cracksPlayed <= 5)
{
MelonLogger.Msg($"Near Miss Crack played for projectile at {num3:F2} m.");
}
}
}
public override void OnSceneWasLoaded(int buildIndex, string sceneName)
{
played.Clear();
vignetteImage = null;
}
public override void OnUpdate()
{
//IL_008f: Unknown result type (might be due to invalid IL or missing references)
if (!(vignetteTimeLeft <= 0f) && !((Object)(object)vignetteImage == (Object)null))
{
vignetteTimeLeft = Mathf.Max(0f, vignetteTimeLeft - Time.unscaledDeltaTime);
float num = ((vignetteDuration.Value <= 0f) ? 0f : (vignetteTimeLeft / vignetteDuration.Value));
num = num * num * (3f - 2f * num);
((Graphic)vignetteImage).color = new Color(1f, 1f, 1f, vignetteStrength.Value * num);
}
}
private static void TriggerVignette()
{
//IL_005d: Unknown result type (might be due to invalid IL or missing references)
if (vignetteEnabled.Value && !(vignetteStrength.Value <= 0f) && EnsureVignette())
{
vignetteTimeLeft = Mathf.Max(0.05f, vignetteDuration.Value);
((Graphic)vignetteImage).color = new Color(1f, 1f, 1f, vignetteStrength.Value);
}
}
private static bool EnsureVignette()
{
//IL_0025: Unknown result type (might be due to invalid IL or missing references)
//IL_002b: Expected O, but got Unknown
//IL_0059: Unknown result type (might be due to invalid IL or missing references)
//IL_005e: Unknown result type (might be due to invalid IL or missing references)
//IL_0084: 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_00ac: Unknown result type (might be due to invalid IL or missing references)
//IL_00c0: Unknown result type (might be due to invalid IL or missing references)
//IL_00fd: Unknown result type (might be due to invalid IL or missing references)
if ((Object)(object)vignetteImage != (Object)null)
{
return true;
}
Camera main = Camera.main;
if ((Object)(object)main == (Object)null)
{
return false;
}
GameObject val = new GameObject("NearMissCrack Vignette");
Canvas obj = val.AddComponent<Canvas>();
obj.renderMode = (RenderMode)1;
obj.worldCamera = main;
obj.planeDistance = 0.3f;
obj.sortingOrder = 32767;
GameObject val2 = new GameObject("NearMissCrack Vignette Image");
val2.transform.SetParent(val.transform, false);
vignetteImage = val2.AddComponent<RawImage>();
((Graphic)vignetteImage).rectTransform.anchorMin = Vector2.zero;
((Graphic)vignetteImage).rectTransform.anchorMax = Vector2.one;
((Graphic)vignetteImage).rectTransform.offsetMin = Vector2.zero;
((Graphic)vignetteImage).rectTransform.offsetMax = Vector2.zero;
vignetteImage.texture = (Texture)(object)CreateVignetteTexture();
((Graphic)vignetteImage).raycastTarget = false;
((Graphic)vignetteImage).color = new Color(1f, 1f, 1f, 0f);
return true;
}
private static Texture2D CreateVignetteTexture()
{
//IL_000c: Unknown result type (might be due to invalid IL or missing references)
//IL_0012: Expected O, but got Unknown
//IL_00d3: Unknown result type (might be due to invalid IL or missing references)
Texture2D val = new Texture2D(128, 128, (TextureFormat)4, false);
Il2CppStructArray<Color> val2 = new Il2CppStructArray<Color>(16384L);
for (int i = 0; i < 128; i++)
{
for (int j = 0; j < 128; j++)
{
float num = ((float)j + 0.5f) / 128f * 2f - 1f;
float num2 = ((float)i + 0.5f) / 128f * 2f - 1f;
float num3 = Mathf.Sqrt(num * num + num2 * num2) / 1.414214f;
float num4 = Mathf.Max(Mathf.Abs(num), Mathf.Abs(num2));
float num5 = Mathf.Lerp(num3, num4, 0.55f);
float num6 = Mathf.SmoothStep(0.08f, 0.88f, num5);
((Il2CppArrayBase<Color>)(object)val2)[i * 128 + j] = new Color(0f, 0f, 0f, Mathf.Pow(num6, 0.62f));
}
}
val.SetPixels(val2);
val.Apply();
return val;
}
private static bool PlayCrack(Vector3 position)
{
//IL_0053: Unknown result type (might be due to invalid IL or missing references)
//IL_0058: Unknown result type (might be due to invalid IL or missing references)
//IL_005e: Unknown result type (might be due to invalid IL or missing references)
//IL_0064: Unknown result type (might be due to invalid IL or missing references)
//IL_00eb: Expected O, but got Unknown
int num = soundRandom.Next(0, clips.Length);
if ((Object)(object)clips[num] == (Object)null)
{
clips[num] = CreateJsrsCrack(num + 1);
}
AudioClip val = clips[num];
if ((Object)(object)val == (Object)null)
{
MelonLogger.Error("Near Miss Crack could not create its audio clip.");
return false;
}
GameObject val2 = new GameObject("Snap! Audio");
val2.transform.position = position;
AudioSource obj = val2.AddComponent<AudioSource>();
obj.clip = val;
obj.spatialBlend = 1f;
obj.dopplerLevel = 0f;
obj.rolloffMode = (AudioRolloffMode)1;
obj.minDistance = Mathf.Max(6f, radius.Value + 1f);
obj.maxDistance = obj.minDistance + 20f;
obj.volume = Mathf.Clamp01(volume.Value);
obj.Play();
Object.Destroy((Object)val2, val.length + 0.1f);
return true;
}
private static bool PlayPistolWizz(Vector3 position)
{
//IL_0079: Unknown result type (might be due to invalid IL or missing references)
//IL_007e: Unknown result type (might be due to invalid IL or missing references)
//IL_0084: 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_0111: Expected O, but got Unknown
bool num = soundRandom.NextDouble() < 0.2;
if (num)
{
if (pistolWizz3 == null)
{
pistolWizz3 = CreateClip("NearMissCrack.JsrsWizz3.pcm", "Crack", 44100);
}
}
else if (pistolWizz1 == null)
{
pistolWizz1 = CreateClip("NearMissCrack.JsrsWizz1.pcm", "Crack", 44100);
}
AudioClip val = (num ? pistolWizz3 : pistolWizz1);
if ((Object)(object)val == (Object)null)
{
return false;
}
GameObject val2 = new GameObject("Snap! Pistol Wizz");
val2.transform.position = position;
AudioSource obj = val2.AddComponent<AudioSource>();
obj.clip = val;
obj.spatialBlend = 1f;
obj.dopplerLevel = 0f;
obj.rolloffMode = (AudioRolloffMode)1;
obj.minDistance = Mathf.Max(6f, radius.Value + 1f);
obj.maxDistance = obj.minDistance + 20f;
obj.volume = Mathf.Clamp01(volume.Value);
obj.Play();
Object.Destroy((Object)val2, val.length + 0.1f);
return true;
}
private static bool IsPistol(Gun gun)
{
Transform val = ((Component)gun).transform;
while ((Object)(object)val != (Object)null)
{
string text = ((Object)val).name.ToLowerInvariant();
if (text.Contains("pistol") || text.Contains("handgun") || text.Contains("glock") || text.Contains("beretta") || text.Contains("1911") || text.Contains("revolver") || text.Contains("magnum") || text.Contains("deagle") || text.Contains("m9") || text.Contains("usp"))
{
return true;
}
val = val.parent;
}
return false;
}
private static AudioClip CreateJsrsCrack(int variation)
{
return CreateClip($"NearMissCrack.JsrsCrack{variation}.pcm", "Crack", 44100);
}
private static AudioClip CreateClip(string resourceName, string clipName, int sampleRate)
{
using Stream stream = typeof(Mod).Assembly.GetManifestResourceStream(resourceName);
if (stream == null)
{
return null;
}
using BinaryReader binaryReader = new BinaryReader(stream);
Il2CppStructArray<float> val = new Il2CppStructArray<float>((long)(int)(stream.Length / 2));
for (int i = 0; i < ((Il2CppArrayBase<float>)(object)val).Length; i++)
{
((Il2CppArrayBase<float>)(object)val)[i] = (float)binaryReader.ReadInt16() / 32768f;
}
AudioClip val2 = AudioClip.Create(clipName, ((Il2CppArrayBase<float>)(object)val).Length, 1, sampleRate, false);
return val2.SetData(val, 0) ? val2 : null;
}
}