Decompiled source of Tomzeh MicPush v1.0.0

MicPush.dll

Decompiled 2 weeks ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BoneLib.BoneMenu;
using Il2CppInterop.Runtime.InteropTypes.Arrays;
using Il2CppSLZ.Marrow;
using MelonLoader;
using MelonLoader.Preferences;
using MicPush;
using Microsoft.CodeAnalysis;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: MelonInfo(typeof(Core), "MicPush", "1.0.0", "YourName", null)]
[assembly: MelonGame("Stress Level Zero", "BONELAB")]
[assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")]
[assembly: AssemblyCompany("MicPush")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("MicPush")]
[assembly: AssemblyTitle("MicPush")]
[assembly: AssemblyVersion("1.0.0.0")]
[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;
		}
	}
}
namespace MicPush
{
	internal static class MicSettings
	{
		public static MelonPreferences_Entry<bool> Enabled;

		public static MelonPreferences_Entry<float> Acceleration;

		public static MelonPreferences_Entry<float> Radius;

		public static MelonPreferences_Entry<float> GateDb;

		public static MelonPreferences_Entry<float> FullDb;

		public static MelonPreferences_Entry<float> UpwardLift;

		public static MelonPreferences_Entry<float> MaxSpeed;

		public static MelonPreferences_Entry<string> MicDevice;

		public static MelonPreferences_Entry<bool> DebugLog;

		public static void Init()
		{
			MelonPreferences_Category obj = MelonPreferences.CreateCategory("MicPush");
			Enabled = obj.CreateEntry<bool>("Enabled", true, (string)null, "Master switch. The microphone is only opened while this is on.", false, false, (ValueValidator)null, (string)null);
			Acceleration = obj.CreateEntry<float>("PushAcceleration", 40f, (string)null, "Push strength at full loudness, point blank (m/s^2). Gravity is 9.81.", false, false, (ValueValidator)null, (string)null);
			Radius = obj.CreateEntry<float>("Radius", 6f, (string)null, "How far from your head things get pushed (metres).", false, false, (ValueValidator)null, (string)null);
			GateDb = obj.CreateEntry<float>("GateDb", -40f, (string)null, "Mic level (dBFS) below which nothing happens. Set just above your room noise.", false, false, (ValueValidator)null, (string)null);
			FullDb = obj.CreateEntry<float>("FullDb", -12f, (string)null, "Mic level (dBFS) that gives a full-strength push (roughly a shout).", false, false, (ValueValidator)null, (string)null);
			UpwardLift = obj.CreateEntry<float>("UpwardLift", 0.2f, (string)null, "Small upward kick so props don't just scrape along the floor.", false, false, (ValueValidator)null, (string)null);
			MaxSpeed = obj.CreateEntry<float>("MaxSpeed", 12f, (string)null, "Things already moving away from you faster than this (m/s) stop being accelerated.", false, false, (ValueValidator)null, (string)null);
			MicDevice = obj.CreateEntry<string>("MicDevice", "", (string)null, "Exact microphone name as printed in the log; leave empty for the system default.", false, false, (ValueValidator)null, (string)null);
			DebugLog = obj.CreateEntry<bool>("DebugLog", false, (string)null, "Print the live mic level (dBFS) to the console twice a second, for tuning GateDb/FullDb.", false, false, (ValueValidator)null, (string)null);
		}
	}
	public class Core : MelonMod
	{
		private const int SampleWindow = 1024;

		private const int ClipSeconds = 10;

		private const int MaxColliders = 256;

		private const float AttackRate = 30f;

		private const float ReleaseRate = 6f;

		private const float RetrySeconds = 5f;

		private AudioClip _clip;

		private string _activeDevice;

		private string _requestedDevice;

		private bool _micRunning;

		private bool _warned;

		private bool _warnedNoHead;

		private float _retryAt;

		private float _lastLogAt;

		private float _level;

		private Il2CppStructArray<float> _samples;

		private Il2CppReferenceArray<Collider> _hits;

		private readonly HashSet<int> _seen = new HashSet<int>();

		private readonly Dictionary<int, bool> _playerBodyCache = new Dictionary<int, bool>();

		public override void OnInitializeMelon()
		{
			MicSettings.Init();
			_samples = new Il2CppStructArray<float>(1024L);
			_hits = new Il2CppReferenceArray<Collider>(256L);
			try
			{
				MicPushMenu.Build();
			}
			catch (Exception ex)
			{
				((MelonBase)this).LoggerInstance.Warning("BoneMenu page skipped (settings still work via the .cfg): " + ex.Message);
			}
		}

		public override void OnSceneWasInitialized(int buildIndex, string sceneName)
		{
			_playerBodyCache.Clear();
			_warnedNoHead = false;
			GameApi.Reset();
		}

		public override void OnApplicationQuit()
		{
			StopMic();
			MelonPreferences.Save();
		}

		public override void OnUpdate()
		{
			if (!MicSettings.Enabled.Value)
			{
				StopMic();
				_level = 0f;
				return;
			}
			string text = (string.IsNullOrWhiteSpace(MicSettings.MicDevice.Value) ? null : MicSettings.MicDevice.Value.Trim());
			if (!_micRunning || text != _requestedDevice)
			{
				if (Time.unscaledTime < _retryAt)
				{
					return;
				}
				StartMic(text);
				if (!_micRunning)
				{
					return;
				}
			}
			float num = ReadLoudness();
			if (!(num < 0f))
			{
				float num2 = ((num > _level) ? 30f : 6f);
				_level = Mathf.Lerp(_level, num, 1f - Mathf.Exp((0f - num2) * Time.unscaledDeltaTime));
			}
		}

		public override void OnFixedUpdate()
		{
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: 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_010a: Unknown result type (might be due to invalid IL or missing references)
			//IL_010f: 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)
			//IL_012a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0136: Unknown result type (might be due to invalid IL or missing references)
			//IL_016d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0172: Unknown result type (might be due to invalid IL or missing references)
			//IL_0179: Unknown result type (might be due to invalid IL or missing references)
			//IL_017e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0183: Unknown result type (might be due to invalid IL or missing references)
			//IL_018e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0193: Unknown result type (might be due to invalid IL or missing references)
			//IL_0160: Unknown result type (might be due to invalid IL or missing references)
			//IL_0164: Unknown result type (might be due to invalid IL or missing references)
			//IL_0159: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b5: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b9: Unknown result type (might be due to invalid IL or missing references)
			if (_level < 0.02f)
			{
				return;
			}
			Transform head = GameApi.GetHead();
			if ((Object)(object)head == (Object)null)
			{
				if (!_warnedNoHead)
				{
					_warnedNoHead = true;
					((MelonBase)this).LoggerInstance.Warning("Heard you, but couldn't find the player rig yet - nothing to push from.");
				}
				return;
			}
			Vector3 position = head.position;
			float num = Mathf.Max(0.5f, MicSettings.Radius.Value);
			float value = MicSettings.Acceleration.Value;
			float value2 = MicSettings.UpwardLift.Value;
			float value3 = MicSettings.MaxSpeed.Value;
			int num2 = Physics.OverlapSphereNonAlloc(position, num, _hits, -1, (QueryTriggerInteraction)1);
			_seen.Clear();
			Vector3 val3 = default(Vector3);
			for (int i = 0; i < num2; i++)
			{
				Collider val = ((Il2CppArrayBase<Collider>)(object)_hits)[i];
				if ((Object)(object)val == (Object)null)
				{
					continue;
				}
				Rigidbody attachedRigidbody = val.attachedRigidbody;
				if ((Object)(object)attachedRigidbody == (Object)null || attachedRigidbody.isKinematic)
				{
					continue;
				}
				int instanceID = ((Object)attachedRigidbody).GetInstanceID();
				if (!_seen.Add(instanceID) || IsPlayerBody(instanceID, attachedRigidbody))
				{
					continue;
				}
				Vector3 val2 = attachedRigidbody.worldCenterOfMass - position;
				float magnitude = ((Vector3)(ref val2)).magnitude;
				if (!(magnitude >= num))
				{
					((Vector3)(ref val3))..ctor(val2.x, 0f, val2.z);
					Vector3 val4 = ((((Vector3)(ref val3)).sqrMagnitude > 0.0001f) ? (((Vector3)(ref val3)).normalized + Vector3.up * value2) : ((magnitude > 0.001f) ? (val2 / magnitude) : Vector3.up));
					((Vector3)(ref val4)).Normalize();
					if (!(Vector3.Dot(attachedRigidbody.velocity, val4) > value3))
					{
						float num3 = value * _level * (1f - magnitude / num);
						attachedRigidbody.AddForce(val4 * num3, (ForceMode)5);
					}
				}
			}
		}

		private bool IsPlayerBody(int id, Rigidbody rb)
		{
			if (_playerBodyCache.TryGetValue(id, out var value))
			{
				return value;
			}
			bool flag = GameApi.IsPlayerBody(rb);
			_playerBodyCache[id] = flag;
			return flag;
		}

		private void StartMic(string device)
		{
			StopMic();
			_requestedDevice = device;
			Il2CppStringArray devices = Microphone.devices;
			if (devices == null || ((Il2CppArrayBase<string>)(object)devices).Length == 0)
			{
				Fail("No microphone detected. Check Windows privacy settings (microphone access for desktop apps).");
				return;
			}
			if (device != null)
			{
				bool flag = false;
				foreach (string item in (Il2CppArrayBase<string>)(object)devices)
				{
					if (item == device)
					{
						flag = true;
						break;
					}
				}
				if (!flag)
				{
					((MelonBase)this).LoggerInstance.Warning("Mic '" + device + "' not found, using the system default. Available: " + string.Join(", ", Il2CppArrayBase<string>.op_Implicit((Il2CppArrayBase<string>)(object)devices)));
					device = null;
				}
			}
			int num = default(int);
			int num2 = default(int);
			Microphone.GetDeviceCaps(device, ref num, ref num2);
			int num3 = ((num == 0 && num2 == 0) ? 48000 : Mathf.Clamp(48000, num, num2));
			_clip = Microphone.Start(device, true, 10, num3);
			if ((Object)(object)_clip == (Object)null)
			{
				Fail("Microphone.Start returned nothing (is another app or voice-chat mod holding the mic?).");
				return;
			}
			_activeDevice = device;
			_micRunning = true;
			_warned = false;
			((MelonBase)this).LoggerInstance.Msg($"Mic started: {device ?? "system default"} @ {num3} Hz. Devices: {string.Join(", ", Il2CppArrayBase<string>.op_Implicit((Il2CppArrayBase<string>)(object)devices))}");
		}

		private void StopMic()
		{
			if (_micRunning)
			{
				Microphone.End(_activeDevice);
				_clip = null;
				_micRunning = false;
			}
		}

		private void Fail(string message)
		{
			if (!_warned)
			{
				((MelonBase)this).LoggerInstance.Warning(message + " (will keep retrying)");
			}
			_warned = true;
			_retryAt = Time.unscaledTime + 5f;
		}

		private float ReadLoudness()
		{
			int position = Microphone.GetPosition(_activeDevice);
			if (position < 1024)
			{
				return -1f;
			}
			_clip.GetData(_samples, position - 1024);
			double num = 0.0;
			for (int i = 0; i < 1024; i++)
			{
				float num2 = ((Il2CppArrayBase<float>)(object)_samples)[i];
				num += (double)(num2 * num2);
			}
			float num3 = Mathf.Sqrt((float)(num / 1024.0));
			float num4 = 20f * Mathf.Log10(Mathf.Max(num3, 1E-05f));
			float num5 = Mathf.InverseLerp(MicSettings.GateDb.Value, MicSettings.FullDb.Value, num4);
			if (MicSettings.DebugLog.Value && Time.unscaledTime - _lastLogAt > 0.5f)
			{
				_lastLogAt = Time.unscaledTime;
				((MelonBase)this).LoggerInstance.Msg($"mic {num4:0.0} dBFS -> push {num5:0.00}");
			}
			return num5;
		}
	}
	internal static class GameApi
	{
		private static Transform _head;

		private static float _nextSearchAt;

		public static void Reset()
		{
			_head = null;
			_nextSearchAt = 0f;
		}

		public static Transform GetHead()
		{
			if ((Object)(object)_head != (Object)null)
			{
				return _head;
			}
			if (Time.unscaledTime < _nextSearchAt)
			{
				return null;
			}
			_nextSearchAt = Time.unscaledTime + 1f;
			Camera main = Camera.main;
			if ((Object)(object)main != (Object)null && (Object)(object)((Component)main).GetComponentInParent<RigManager>() != (Object)null)
			{
				return _head = ((Component)main).transform;
			}
			foreach (Camera item in Object.FindObjectsOfType<Camera>())
			{
				if ((Object)(object)item != (Object)null && ((Behaviour)item).enabled && (Object)(object)((Component)item).GetComponentInParent<RigManager>() != (Object)null)
				{
					return _head = ((Component)item).transform;
				}
			}
			return null;
		}

		public static bool IsPlayerBody(Rigidbody rb)
		{
			return (Object)(object)((Component)rb).GetComponentInParent<RigManager>() != (Object)null;
		}
	}
	internal static class MicPushMenu
	{
		public static void Build()
		{
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: 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_009f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e8: Unknown result type (might be due to invalid IL or missing references)
			//IL_0131: Unknown result type (might be due to invalid IL or missing references)
			//IL_0179: Unknown result type (might be due to invalid IL or missing references)
			Page obj = Page.Root.CreatePage("Mic Push", Color.cyan, 0, true);
			obj.CreateBool("Enabled", Color.green, MicSettings.Enabled.Value, (Action<bool>)delegate(bool v)
			{
				MicSettings.Enabled.Value = v;
			});
			obj.CreateFloat("Push Strength", Color.white, MicSettings.Acceleration.Value, 5f, 0f, 150f, (Action<float>)delegate(float v)
			{
				MicSettings.Acceleration.Value = v;
			});
			obj.CreateFloat("Radius (m)", Color.white, MicSettings.Radius.Value, 0.5f, 0.5f, 20f, (Action<float>)delegate(float v)
			{
				MicSettings.Radius.Value = v;
			});
			obj.CreateFloat("Gate (dB)", Color.white, MicSettings.GateDb.Value, 1f, -70f, -20f, (Action<float>)delegate(float v)
			{
				MicSettings.GateDb.Value = v;
			});
			obj.CreateFloat("Full Power (dB)", Color.white, MicSettings.FullDb.Value, 1f, -40f, 0f, (Action<float>)delegate(float v)
			{
				MicSettings.FullDb.Value = v;
			});
			obj.CreateFloat("Upward Lift", Color.white, MicSettings.UpwardLift.Value, 0.05f, 0f, 1f, (Action<float>)delegate(float v)
			{
				MicSettings.UpwardLift.Value = v;
			});
		}
	}
}