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