using System;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BoneLib;
using HandUnstuck;
using Il2CppSLZ.Marrow;
using MelonLoader;
using UnityEngine;
[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: MelonInfo(typeof(Mod), "Hand Unstuck", "1.2.1", "andre", null)]
[assembly: MelonGame("Stress Level Zero", "BONELAB")]
[assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")]
[assembly: AssemblyCompany("HandUnstuck")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("HandUnstuck")]
[assembly: AssemblyTitle("HandUnstuck")]
[assembly: AssemblyVersion("1.0.0.0")]
namespace HandUnstuck;
public class Mod : MelonMod
{
private const float StuckSeconds = 1.5f;
private float _leftSince = -1f;
private float _rightSince = -1f;
public override void OnApplicationQuit()
{
TimelineNoBreaks.Restore();
}
public override void OnUpdate()
{
TimelineNoBreaks.Update();
if (Player.HandsExist)
{
Check(Player.LeftHand, ref _leftSince);
Check(Player.RightHand, ref _rightSince);
}
}
private void Check(Hand hand, ref float since)
{
//IL_006a: Unknown result type (might be due to invalid IL or missing references)
if ((Object)(object)hand == (Object)null)
{
since = -1f;
}
else if ((Object)(object)hand.m_CurrentAttachedGO != (Object)null || !hand.GrabLock)
{
since = -1f;
}
else if (since < 0f)
{
since = Time.time;
}
else if (!(Time.time - since < 1.5f))
{
since = -1f;
((MelonBase)this).LoggerInstance.Msg($"{hand.handedness} hand was stuck (grab lock with nothing held), releasing it");
hand.GrabLock = false;
if (hand.hoverLocked)
{
hand.HoverUnlock();
}
}
}
}
internal static class TimelineNoBreaks
{
private static readonly (string cat, string id, bool followsGoreOff)[] Keys = new(string, string, bool)[3]
{
("TimelineClones", "BreakBones", false),
("PlayerGore", "BreakMine", true),
("PlayerGore", "BreakPlayers", true)
};
private static FieldInfo _playing;
private static FieldInfo _recording;
private static bool _lookedUp;
private static readonly bool[] _forced = new bool[Keys.Length];
private static bool TimelineInUse()
{
if (!_lookedUp)
{
_lookedUp = true;
Type? type = (from a in AppDomain.CurrentDomain.GetAssemblies()
select a.GetType("Timeline.WorldRecording.WorldPlayer", throwOnError: false)).FirstOrDefault((Type x) => x != null);
_playing = type?.GetField("playing", BindingFlags.Static | BindingFlags.Public);
_recording = type?.GetField("recording", BindingFlags.Static | BindingFlags.Public);
MelonLogger.Msg((type == null) ? "[NoBreaks] Timeline not found" : "[NoBreaks] Timeline found");
}
if (_playing == null)
{
return false;
}
if (!(bool)_playing.GetValue(null))
{
return (bool)(_recording?.GetValue(null) ?? ((object)false));
}
return true;
}
internal static void Update()
{
bool flag = TimelineInUse();
MelonPreferences_Entry<bool> entry = MelonPreferences.GetEntry<bool>("PlayerGore", "On");
bool flag2 = entry != null && !entry.Value;
for (int i = 0; i < Keys.Length; i++)
{
MelonPreferences_Entry<bool> entry2 = MelonPreferences.GetEntry<bool>(Keys[i].cat, Keys[i].id);
if (entry2 != null)
{
bool flag3 = flag || (flag2 && Keys[i].followsGoreOff);
string value = (flag ? "Timeline in use" : "Player Gore is off");
if (flag3 && entry2.Value)
{
entry2.Value = false;
_forced[i] = true;
MelonLogger.Msg($"[NoBreaks] {value}: {Keys[i].cat}.{Keys[i].id} off");
}
else if (!flag3 && _forced[i])
{
Put(i, entry2);
}
}
}
}
internal static void Restore()
{
for (int i = 0; i < Keys.Length; i++)
{
if (_forced[i])
{
Put(i, MelonPreferences.GetEntry<bool>(Keys[i].cat, Keys[i].id));
}
}
}
private static void Put(int i, MelonPreferences_Entry<bool> e)
{
_forced[i] = false;
if (e != null)
{
e.Value = true;
MelonLogger.Msg($"[NoBreaks] {Keys[i].cat}.{Keys[i].id} back on");
}
}
}