using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using BoneLib.BoneMenu;
using HalfSwordControls.Core;
using HalfSwordControls.Features.ApiProbe;
using HalfSwordControls.Features.BoneMenuUi;
using HalfSwordControls.Features.DecoupledInput;
using HalfSwordControls.Util;
using HarmonyLib;
using MelonLoader;
using MelonLoader.Preferences;
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(ModEntry), "HalfSwordControls", "0.5.0", "maxt8", null)]
[assembly: MelonGame("Stress Level Zero", "BONELAB")]
[assembly: MelonPriority(1000)]
[assembly: HarmonyDontPatchAll]
[assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")]
[assembly: AssemblyCompany("HalfSwordControls")]
[assembly: AssemblyConfiguration("Debug")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("HalfSwordControls")]
[assembly: AssemblyTitle("HalfSwordControls")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.0.0")]
[module: UnverifiableCode]
[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 HalfSwordControls.Util
{
internal static class FlatPlayerBridge
{
private sealed class Member
{
private readonly PropertyInfo _p;
private readonly FieldInfo _f;
public readonly string Description;
private Member(PropertyInfo p, FieldInfo f)
{
_p = p;
_f = f;
Description = ((p != null) ? (p.Name + " (property " + p.PropertyType.Name + " on " + p.DeclaringType.Name + ")") : (f.Name + " (field " + f.FieldType.Name + " on " + f.DeclaringType.Name + ")"));
}
public static Member Find(Type type, string name)
{
Type type2 = type;
while (type2 != null && type2 != typeof(object))
{
try
{
PropertyInfo property = type2.GetProperty(name, BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
if (property != null)
{
return new Member(property, null);
}
FieldInfo field = type2.GetField(name, BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
if (field != null)
{
return new Member(null, field);
}
}
catch
{
}
type2 = type2.BaseType;
}
return null;
}
public object Get(object obj)
{
return (_p != null) ? _p.GetValue(obj) : _f.GetValue(obj);
}
public void Set(object obj, object value)
{
if (_p != null)
{
_p.SetValue(obj, value);
}
else
{
_f.SetValue(obj, value);
}
}
}
private const BindingFlags Stat = BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic;
private const BindingFlags Inst = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;
private const BindingFlags Declared = BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;
private static Instance _log;
private static bool _resolved;
private static bool _devicesChecked;
private static bool _devicesOk;
private static FieldInfo _sInstance;
private static FieldInfo _sIsReady;
private static FieldInfo _sHmd;
private static FieldInfo _sLeftCtl;
private static FieldInfo _sRightCtl;
private static FieldInfo _fCam;
private static FieldInfo _fLeftRot;
private static FieldInfo _fRightRot;
private static FieldInfo _fLeftPos;
private static FieldInfo _fRightPos;
private static MethodInfo _resetPositions;
private static Member _hmdRot;
private static Member _ctlRot;
private static Member _ctlPos;
private static object Booter => _sInstance.GetValue(null);
public static bool IsReady
{
get
{
if (!_resolved)
{
return false;
}
if (Booter == null)
{
return false;
}
if (!(_sIsReady.GetValue(null) is bool flag) || !flag)
{
return false;
}
return EnsureDevices();
}
}
public static bool Resolve(Instance log)
{
if (_resolved)
{
return true;
}
_log = log;
Type type = TypeExplorer.FindType("FlatPlayer.FlatBooter");
if (type == null)
{
log.Warning("[FlatPlayerBridge] FlatPlayer.FlatBooter not found. Is FlatPlayer installed?");
return false;
}
_sInstance = type.GetField("Instance", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
_sIsReady = type.GetField("IsReady", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
_sHmd = type.GetField("XRHmd", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
_sLeftCtl = type.GetField("LeftController", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
_sRightCtl = type.GetField("RightController", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
_fCam = type.GetField("cameraLastRotation", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
_fLeftRot = type.GetField("leftHandRot", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
_fRightRot = type.GetField("rightHandRot", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
_fLeftPos = type.GetField("leftHandPos", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
_fRightPos = type.GetField("rightHandPos", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
_resetPositions = type.GetMethod("ResetPositions", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, Type.EmptyTypes, null);
if (_resetPositions == null)
{
log.Warning("[FlatPlayerBridge] FlatBooter.ResetPositions not found: Reset Hands button inactive.");
}
bool flag = true;
flag &= Check(_sInstance, "Instance");
flag &= Check(_sIsReady, "IsReady");
flag &= Check(_sHmd, "XRHmd");
flag &= Check(_sLeftCtl, "LeftController");
flag &= Check(_sRightCtl, "RightController");
flag &= Check(_fCam, "cameraLastRotation");
flag &= Check(_fLeftRot, "leftHandRot");
flag &= Check(_fRightRot, "rightHandRot");
flag &= Check(_fLeftPos, "leftHandPos");
flag = (_resolved = flag & Check(_fRightPos, "rightHandPos"));
if (flag)
{
log.Msg("[FlatPlayerBridge] FlatBooter members resolved.");
}
return flag;
}
private static bool Check(object member, string name)
{
if (member != null)
{
return true;
}
_log.Warning("[FlatPlayerBridge] FlatBooter member not found: " + name);
return false;
}
private static bool EnsureDevices()
{
if (_devicesChecked)
{
return _devicesOk;
}
object value = _sHmd.GetValue(null);
object value2 = _sLeftCtl.GetValue(null);
object value3 = _sRightCtl.GetValue(null);
if (value == null || value2 == null || value3 == null)
{
return false;
}
_devicesChecked = true;
_hmdRot = Member.Find(value.GetType(), "_rotation");
_ctlRot = Member.Find(value2.GetType(), "_rotation");
_ctlPos = Member.Find(value2.GetType(), "_position");
_devicesOk = _hmdRot != null && _ctlRot != null && _ctlPos != null;
if (_devicesOk)
{
_log.Msg("[FlatPlayerBridge] XRHmd." + _hmdRot.Description);
_log.Msg("[FlatPlayerBridge] Controller." + _ctlRot.Description + " / " + _ctlPos.Description);
}
else
{
_log.Error("[FlatPlayerBridge] Cannot find _rotation/_position on virtual devices. Writing type dump to Desktop.");
DumpFailure(value, value2);
}
return _devicesOk;
}
private static void DumpFailure(object hmd, object left)
{
try
{
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.AppendLine("HMD runtime type: " + hmd.GetType().FullName);
stringBuilder.AppendLine("Controller runtime type: " + left.GetType().FullName);
Type type = hmd.GetType();
while (type != null && type != typeof(object))
{
TypeExplorer.DumpTypes(new string[1] { type.FullName }, stringBuilder);
type = type.BaseType;
}
Type type2 = left.GetType();
while (type2 != null && type2 != typeof(object))
{
TypeExplorer.DumpTypes(new string[1] { type2.FullName }, stringBuilder);
type2 = type2.BaseType;
}
string text = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory), "HalfSword_BridgeDump.txt");
File.WriteAllText(text, stringBuilder.ToString());
_log.Msg("[FlatPlayerBridge] Dump written to " + text);
}
catch (Exception ex)
{
_log.Error("[FlatPlayerBridge] Dump failed: " + ex.Message);
}
}
public static Vector2 GetCamera()
{
//IL_000f: Unknown result type (might be due to invalid IL or missing references)
return (Vector2)_fCam.GetValue(Booter);
}
public static void SetCamera(Vector2 v)
{
//IL_000a: Unknown result type (might be due to invalid IL or missing references)
_fCam.SetValue(Booter, v);
}
public static Quaternion GetHmd()
{
//IL_0015: Unknown result type (might be due to invalid IL or missing references)
return (Quaternion)_hmdRot.Get(_sHmd.GetValue(null));
}
public static void SetHmd(Quaternion q)
{
//IL_0010: Unknown result type (might be due to invalid IL or missing references)
_hmdRot.Set(_sHmd.GetValue(null), q);
}
public static Quaternion GetHandRot(bool right)
{
//IL_0019: Unknown result type (might be due to invalid IL or missing references)
return (Quaternion)(right ? _fRightRot : _fLeftRot).GetValue(Booter);
}
public static void SetHandRot(bool right, Quaternion q)
{
//IL_0014: Unknown result type (might be due to invalid IL or missing references)
(right ? _fRightRot : _fLeftRot).SetValue(Booter, q);
}
public static Vector3 GetHandPos(bool right)
{
//IL_0019: Unknown result type (might be due to invalid IL or missing references)
return (Vector3)(right ? _fRightPos : _fLeftPos).GetValue(Booter);
}
public static void SetHandPos(bool right, Vector3 p)
{
//IL_0014: Unknown result type (might be due to invalid IL or missing references)
(right ? _fRightPos : _fLeftPos).SetValue(Booter, p);
}
public static void ResetHands()
{
if (_resolved && !(_resetPositions == null))
{
object booter = Booter;
if (booter != null)
{
_resetPositions.Invoke(booter, null);
}
}
}
public static void SetControllerPose(bool right, Quaternion rot, Vector3 pos)
{
//IL_001d: Unknown result type (might be due to invalid IL or missing references)
//IL_002f: Unknown result type (might be due to invalid IL or missing references)
object value = (right ? _sRightCtl : _sLeftCtl).GetValue(null);
_ctlRot.Set(value, rot);
_ctlPos.Set(value, pos);
}
public static string DescribeController(bool right)
{
object value = (right ? _sRightCtl : _sLeftCtl).GetValue(null);
if (value == null)
{
return "(no controller)";
}
StringBuilder stringBuilder = new StringBuilder();
string[] array = new string[4] { "Grip", "GripButton", "GripForce", "Trigger" };
foreach (string text in array)
{
Member member = Member.Find(value.GetType(), text);
stringBuilder.Append(text).Append('=').Append((member != null) ? member.Get(value) : "?")
.Append(' ');
}
return stringBuilder.ToString();
}
}
internal static class MouseInput
{
private static bool _initTried;
private static bool _newOk;
private static PropertyInfo _currentProp;
private static PropertyInfo _deltaProp;
private static PropertyInfo _scrollProp;
private static readonly Dictionary<Type, MethodInfo> _readCache = new Dictionary<Type, MethodInfo>();
private static bool _warnedDelta;
private static bool _warnedScroll;
public static bool CameraModeActive => Input.GetKey(ModConfig.CameraKey.Value);
public static Vector2 ReadMouseDelta()
{
//IL_0011: Unknown result type (might be due to invalid IL or missing references)
//IL_0012: Unknown result type (might be due to invalid IL or missing references)
//IL_0066: Unknown result type (might be due to invalid IL or missing references)
//IL_006b: Unknown result type (might be due to invalid IL or missing references)
//IL_002b: Unknown result type (might be due to invalid IL or missing references)
//IL_0030: Unknown result type (might be due to invalid IL or missing references)
//IL_006e: Unknown result type (might be due to invalid IL or missing references)
if (TryReadNew(_deltaProp, out var value))
{
return value;
}
try
{
return new Vector2(Input.GetAxisRaw("Mouse X"), Input.GetAxisRaw("Mouse Y"));
}
catch (Exception ex)
{
if (!_warnedDelta)
{
_warnedDelta = true;
MelonLogger.Warning("[MouseInput] No mouse delta source works: " + ex.GetType().Name);
}
return Vector2.zero;
}
}
public static float ReadScrollNotches()
{
//IL_0011: Unknown result type (might be due to invalid IL or missing references)
//IL_0022: Unknown result type (might be due to invalid IL or missing references)
if (TryReadNew(_scrollProp, out var value))
{
return value.y / 120f;
}
try
{
return Input.mouseScrollDelta.y;
}
catch (Exception ex)
{
if (!_warnedScroll)
{
_warnedScroll = true;
MelonLogger.Warning("[MouseInput] No scroll source works: " + ex.GetType().Name);
}
return 0f;
}
}
private static bool InitNew()
{
if (_initTried)
{
return _newOk;
}
_initTried = true;
try
{
Type type = TypeExplorer.FindType("UnityEngine.InputSystem.Mouse");
if (type == null)
{
return false;
}
_currentProp = type.GetProperty("current", BindingFlags.Static | BindingFlags.Public | BindingFlags.FlattenHierarchy);
_deltaProp = type.GetProperty("delta", BindingFlags.Instance | BindingFlags.Public);
_scrollProp = type.GetProperty("scroll", BindingFlags.Instance | BindingFlags.Public);
_newOk = _currentProp != null && _deltaProp != null && _scrollProp != null;
}
catch
{
_newOk = false;
}
MelonLogger.Msg("[MouseInput] New Input System mouse path: " + (_newOk ? "available" : "NOT available, using legacy Input"));
return _newOk;
}
private static bool TryReadNew(PropertyInfo control, out Vector2 value)
{
//IL_0002: Unknown result type (might be due to invalid IL or missing references)
//IL_00c7: Unknown result type (might be due to invalid IL or missing references)
//IL_00cc: Unknown result type (might be due to invalid IL or missing references)
//IL_00da: Unknown result type (might be due to invalid IL or missing references)
//IL_00dc: Unknown result type (might be due to invalid IL or missing references)
value = default(Vector2);
if (!InitNew() || control == null)
{
return false;
}
try
{
object value2 = _currentProp.GetValue(null);
if (value2 == null)
{
return false;
}
object value3 = control.GetValue(value2);
if (value3 == null)
{
return false;
}
Type type = value3.GetType();
if (!_readCache.TryGetValue(type, out var value4))
{
value4 = type.GetMethod("ReadValue", Type.EmptyTypes);
_readCache[type] = value4;
}
if (value4 == null)
{
return false;
}
if (value4.Invoke(value3, null) is Vector2 val)
{
value = val;
return true;
}
}
catch
{
}
return false;
}
}
internal static class RigAccess
{
private static readonly string[] RightNames = new string[4] { "m_handRt", "handRt", "rightHand", "m_rightHand" };
private static readonly string[] LeftNames = new string[4] { "m_handLf", "handLf", "leftHand", "m_leftHand" };
private static readonly string[] HeadNames = new string[5] { "m_head", "head", "hmdTransform", "hmd", "m_hmd" };
private static MemberInfo _right;
private static MemberInfo _left;
private static MemberInfo _head;
private static bool _dumped;
private const BindingFlags Flags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;
public static Transform GetHand(object rig, bool right)
{
return right ? Read(rig, RightNames, ref _right) : Read(rig, LeftNames, ref _left);
}
public static Transform GetHead(object rig)
{
return Read(rig, HeadNames, ref _head);
}
private static Transform Read(object rig, string[] names, ref MemberInfo cache)
{
if (rig == null)
{
return null;
}
if (cache == null)
{
cache = Find(rig.GetType(), names);
if (cache == null)
{
if (!_dumped)
{
_dumped = true;
MelonLogger.Warning("[RigAccess] Could not find any of: " + string.Join(", ", names));
Dump(rig.GetType());
}
return null;
}
MelonLogger.Msg("[RigAccess] Resolved '" + cache.Name + "' on " + rig.GetType().Name);
}
object obj = ((cache is PropertyInfo propertyInfo) ? propertyInfo.GetValue(rig) : ((FieldInfo)cache).GetValue(rig));
return (Transform)((obj is Transform) ? obj : null);
}
private static MemberInfo Find(Type type, string[] names)
{
foreach (string name in names)
{
PropertyInfo property = type.GetProperty(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
if (property != null && typeof(Transform).IsAssignableFrom(property.PropertyType))
{
return property;
}
FieldInfo field = type.GetField(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
if (field != null && typeof(Transform).IsAssignableFrom(field.FieldType))
{
return field;
}
}
return null;
}
public static void Dump(Type type)
{
MelonLogger.Msg("[RigAccess] ---- Dump of " + type.FullName + " ----");
Type type2 = type;
while (type2 != null && type2 != typeof(object) && (type2.Namespace == null || !type2.Namespace.StartsWith("Il2CppInterop")))
{
MelonLogger.Msg("[RigAccess] class " + type2.FullName);
PropertyInfo[] properties = type2.GetProperties(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
foreach (PropertyInfo propertyInfo in properties)
{
if (Interesting(propertyInfo.PropertyType))
{
MelonLogger.Msg("[RigAccess] prop " + propertyInfo.PropertyType.Name + " " + propertyInfo.Name);
}
}
FieldInfo[] fields = type2.GetFields(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
foreach (FieldInfo fieldInfo in fields)
{
if (Interesting(fieldInfo.FieldType))
{
MelonLogger.Msg("[RigAccess] field " + fieldInfo.FieldType.Name + " " + fieldInfo.Name);
}
}
type2 = type2.BaseType;
}
MelonLogger.Msg("[RigAccess] ---- end dump ----");
}
private static bool Interesting(Type t)
{
return typeof(Transform).IsAssignableFrom(t) || t.Name.IndexOf("Controller", StringComparison.OrdinalIgnoreCase) >= 0 || t.Name.IndexOf("Hand", StringComparison.OrdinalIgnoreCase) >= 0;
}
}
internal static class TypeExplorer
{
private const BindingFlags F = BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic;
public static Type FindType(string fullName)
{
Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
foreach (Assembly assembly in assemblies)
{
try
{
Type type = assembly.GetType(fullName, throwOnError: false);
if (type != null)
{
return type;
}
}
catch
{
}
}
return null;
}
public static void DumpTypes(IEnumerable<string> fullNames, StringBuilder sb)
{
foreach (string fullName in fullNames)
{
Type type = FindType(fullName);
if (type == null)
{
sb.AppendLine("### NOT FOUND: " + fullName);
sb.AppendLine();
}
else
{
DumpType(type, sb);
}
}
}
public static void DumpAssembly(string simpleName, StringBuilder sb)
{
Assembly assembly = AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault((Assembly a) => a.GetName().Name == simpleName);
if (assembly == null)
{
sb.AppendLine("### ASSEMBLY NOT LOADED: " + simpleName);
return;
}
Type[] array;
try
{
array = assembly.GetTypes();
}
catch (ReflectionTypeLoadException ex)
{
array = ex.Types.Where((Type x) => x != null).ToArray();
}
sb.AppendLine("######## ASSEMBLY " + simpleName + " (" + array.Length + " types) ########");
foreach (Type item in array.OrderBy((Type x) => x.FullName))
{
DumpType(item, sb);
}
}
private static void DumpType(Type t, StringBuilder sb)
{
sb.AppendLine("### " + t.FullName + " : " + ((t.BaseType != null) ? t.BaseType.FullName : "-"));
try
{
FieldInfo[] fields = t.GetFields(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
foreach (FieldInfo fieldInfo in fields)
{
if (!Skip(fieldInfo.Name))
{
sb.AppendLine(" field " + (fieldInfo.IsStatic ? "static " : "") + fieldInfo.FieldType.Name + " " + fieldInfo.Name);
}
}
PropertyInfo[] properties = t.GetProperties(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
foreach (PropertyInfo propertyInfo in properties)
{
if (!Skip(propertyInfo.Name))
{
sb.AppendLine(" prop " + propertyInfo.PropertyType.Name + " " + propertyInfo.Name);
}
}
MethodInfo[] methods = t.GetMethods(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
foreach (MethodInfo methodInfo in methods)
{
if (!methodInfo.IsSpecialName && !Skip(methodInfo.Name))
{
string text = string.Join(", ", from x in methodInfo.GetParameters()
select x.ParameterType.Name + " " + x.Name);
sb.AppendLine(" method " + (methodInfo.IsStatic ? "static " : "") + methodInfo.ReturnType.Name + " " + methodInfo.Name + "(" + text + ")");
}
}
}
catch (Exception ex)
{
sb.AppendLine(" (dump failed: " + ex.GetType().Name + ")");
}
sb.AppendLine();
}
private static bool Skip(string name)
{
return name.StartsWith("NativeFieldInfoPtr_") || name.StartsWith("NativeMethodInfoPtr_") || name.StartsWith("NativeClassPtr") || name.Contains("<");
}
}
}
namespace HalfSwordControls.Features.DecoupledInput
{
internal sealed class DecoupledInputFeature : IFeature
{
private sealed class HandState
{
public bool Held;
public Vector3 BaseEuler;
public Vector3 BasePos;
public float YawOff;
public float PitchOff;
public float XOff;
public float YOff;
public float Reach;
public bool LogPending;
}
private readonly HandState _left = new HandState();
private readonly HandState _right = new HandState();
private Instance _log;
private bool _active;
private bool _failed;
private int _transitionLogs;
private bool _haveSnap;
private Vector2 _snapCam;
private Quaternion _snapHmd = Quaternion.identity;
private float _snapZLeft;
private float _snapZRight;
private bool _orderConfirmed;
private int _mouseMoveFrames;
private bool _orderWarned;
public string Name => "DecoupledInput";
public bool Enabled => ModConfig.DecoupledInputEnabled.Value;
public void Initialize(Harmony harmony, Instance log)
{
_log = log;
if (!FlatPlayerBridge.Resolve(log))
{
_failed = true;
log.Error("FlatPlayer bridge unavailable, feature inactive.");
}
}
public void OnUpdate()
{
//IL_0040: Unknown result type (might be due to invalid IL or missing references)
//IL_0046: Invalid comparison between Unknown and I4
//IL_0064: 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_00d0: Unknown result type (might be due to invalid IL or missing references)
//IL_00d5: 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)
//IL_0120: Unknown result type (might be due to invalid IL or missing references)
//IL_0130: Unknown result type (might be due to invalid IL or missing references)
if (_failed)
{
return;
}
if (!FlatPlayerBridge.IsReady)
{
ReleaseAll();
_haveSnap = false;
return;
}
bool flag = !ModConfig.RequireCursorLock.Value || (int)Cursor.lockState == 1;
bool cameraModeActive = MouseInput.CameraModeActive;
SetHeld(_left, flag && !cameraModeActive && Input.GetKey(ModConfig.LeftHandButton.Value), right: false);
SetHeld(_right, flag && !cameraModeActive && Input.GetKey(ModConfig.RightHandButton.Value), right: true);
_active = _left.Held || _right.Held;
if (_active)
{
Vector2 d = MouseInput.ReadMouseDelta();
if (((Vector2)(ref d)).sqrMagnitude > 0.25f)
{
_mouseMoveFrames++;
}
bool key = Input.GetKey(ModConfig.RotateModifierKey.Value);
float dReach = MouseInput.ReadScrollNotches() * ModConfig.ReachPerScroll.Value;
Integrate(_left, d, key, dReach);
Integrate(_right, d, key, dReach);
}
}
public void OnLateUpdate()
{
//IL_0113: Unknown result type (might be due to invalid IL or missing references)
//IL_0118: Unknown result type (might be due to invalid IL or missing references)
//IL_011e: 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)
//IL_012a: Unknown result type (might be due to invalid IL or missing references)
//IL_013b: Unknown result type (might be due to invalid IL or missing references)
//IL_0044: Unknown result type (might be due to invalid IL or missing references)
//IL_0049: Unknown result type (might be due to invalid IL or missing references)
//IL_0052: Unknown result type (might be due to invalid IL or missing references)
//IL_0054: Unknown result type (might be due to invalid IL or missing references)
//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_0092: Unknown result type (might be due to invalid IL or missing references)
//IL_009e: Unknown result type (might be due to invalid IL or missing references)
if (_failed)
{
return;
}
try
{
if (!FlatPlayerBridge.IsReady)
{
_haveSnap = false;
return;
}
if (_active && _haveSnap)
{
Vector2 camera = FlatPlayerBridge.GetCamera();
if (!_orderConfirmed)
{
Vector2 val = camera - _snapCam;
if (((Vector2)(ref val)).sqrMagnitude > 1E-08f)
{
_orderConfirmed = true;
_log.Msg("Order confirmed: FlatPlayer moved the camera before us, restoring it.");
}
}
FlatPlayerBridge.SetCamera(_snapCam);
FlatPlayerBridge.SetHmd(_snapHmd);
ApplyHand(_left, right: false, _snapZLeft);
ApplyHand(_right, right: true, _snapZRight);
if (!_orderConfirmed && !_orderWarned && _mouseMoveFrames >= 60)
{
_orderWarned = true;
_log.Warning("Camera never changed while the mouse moved: FlatPlayer may run AFTER us. Send me this log.");
}
}
_snapCam = FlatPlayerBridge.GetCamera();
_snapHmd = FlatPlayerBridge.GetHmd();
_snapZLeft = FlatPlayerBridge.GetHandPos(right: false).z;
_snapZRight = FlatPlayerBridge.GetHandPos(right: true).z;
_haveSnap = true;
}
catch (Exception ex)
{
_failed = true;
_active = false;
_log.Error("DecoupledInput disabled after error: " + ex);
}
}
private void SetHeld(HandState h, bool want, bool right)
{
//IL_001b: Unknown result type (might be due to invalid IL or missing references)
//IL_0020: Unknown result type (might be due to invalid IL or missing references)
//IL_0023: Unknown result type (might be due to invalid IL or missing references)
//IL_0028: Unknown result type (might be due to invalid IL or missing references)
//IL_002f: Unknown result type (might be due to invalid IL or missing references)
//IL_0034: Unknown result type (might be due to invalid IL or missing references)
if (want != h.Held)
{
if (want)
{
Quaternion handRot = FlatPlayerBridge.GetHandRot(right);
h.BaseEuler = ((Quaternion)(ref handRot)).eulerAngles;
h.BasePos = FlatPlayerBridge.GetHandPos(right);
h.YawOff = 0f;
h.PitchOff = 0f;
h.XOff = 0f;
h.YOff = 0f;
h.Reach = h.BasePos.z;
h.LogPending = true;
}
h.Held = want;
if (_transitionLogs++ < 12)
{
_log.Msg((right ? "RIGHT" : "LEFT") + " hand control " + (want ? "ON" : "OFF"));
}
}
}
private static void Integrate(HandState h, Vector2 d, bool rotate, float dReach)
{
//IL_00c0: Unknown result type (might be due to invalid IL or missing references)
//IL_004e: Unknown result type (might be due to invalid IL or missing references)
//IL_00eb: Unknown result type (might be due to invalid IL or missing references)
//IL_00e3: Unknown result type (might be due to invalid IL or missing references)
//IL_0079: 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)
if (h.Held)
{
bool value = ModConfig.InvertY.Value;
if (rotate)
{
float value2 = ModConfig.RotSensitivity.Value;
float value3 = ModConfig.AimYawLimit.Value;
float value4 = ModConfig.AimPitchLimit.Value;
h.YawOff = Mathf.Clamp(h.YawOff + d.x * value2, 0f - value3, value3);
h.PitchOff = Mathf.Clamp(h.PitchOff + (value ? d.y : (0f - d.y)) * value2, 0f - value4, value4);
}
else
{
float value5 = ModConfig.PosSensitivity.Value;
float value6 = ModConfig.AimXLimit.Value;
float value7 = ModConfig.AimYLimit.Value;
h.XOff = Mathf.Clamp(h.XOff + d.x * value5, 0f - value6, value6);
h.YOff = Mathf.Clamp(h.YOff + (value ? (0f - d.y) : d.y) * value5, 0f - value7, value7);
}
h.Reach = Mathf.Clamp(h.Reach + dReach, 0f, 1f);
}
}
private void ReleaseAll()
{
_left.Held = false;
_right.Held = false;
_active = false;
}
public void Shutdown()
{
ReleaseAll();
}
private void ApplyHand(HandState h, bool right, float snapZ)
{
//IL_0087: 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_008e: 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)
//IL_009d: Unknown result type (might be due to invalid IL or missing references)
//IL_003b: Unknown result type (might be due to invalid IL or missing references)
//IL_0040: Unknown result type (might be due to invalid IL or missing references)
//IL_0073: 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_00a7: 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_00ad: Unknown result type (might be due to invalid IL or missing references)
//IL_00b3: Unknown result type (might be due to invalid IL or missing references)
//IL_00b8: Unknown result type (might be due to invalid IL or missing references)
//IL_00b9: Unknown result type (might be due to invalid IL or missing references)
Quaternion val;
Vector3 handPos = default(Vector3);
if (h.Held)
{
val = Quaternion.Euler(h.BaseEuler.x + h.PitchOff, h.BaseEuler.y + h.YawOff, h.BaseEuler.z);
((Vector3)(ref handPos))..ctor(h.BasePos.x + h.XOff, h.BasePos.y + h.YOff, h.Reach);
FlatPlayerBridge.SetHandRot(right, val);
FlatPlayerBridge.SetHandPos(right, handPos);
}
else
{
val = FlatPlayerBridge.GetHandRot(right);
handPos = FlatPlayerBridge.GetHandPos(right);
handPos.z = snapZ;
FlatPlayerBridge.SetHandPos(right, handPos);
}
FlatPlayerBridge.SetControllerPose(right, _snapHmd * val, _snapHmd * handPos);
if (h.LogPending && h.Held)
{
h.LogPending = false;
_log.Msg((right ? "RIGHT" : "LEFT") + " controller input at press: " + FlatPlayerBridge.DescribeController(right));
}
}
}
}
namespace HalfSwordControls.Features.BoneMenuUi
{
internal sealed class BoneMenuFeature : IFeature
{
private const float SaveDelay = 1f;
private Instance _log;
private bool _dirty;
private float _lastChange;
public string Name => "BoneMenu";
public bool Enabled => true;
public void Initialize(Harmony harmony, Instance log)
{
_log = log;
if (TypeExplorer.FindType("BoneLib.BoneMenu.Page") == null)
{
log.Warning("BoneLib not found: BoneMenu section skipped.");
return;
}
try
{
Build();
}
catch (Exception ex)
{
log.Error("BoneMenu build failed: " + ex);
}
}
[MethodImpl(MethodImplOptions.NoInlining)]
private void Build()
{
//IL_000b: Unknown result type (might be due to invalid IL or missing references)
//IL_001e: Unknown result type (might be due to invalid IL or missing references)
//IL_0063: Unknown result type (might be due to invalid IL or missing references)
//IL_00a8: Unknown result type (might be due to invalid IL or missing references)
//IL_00cf: Unknown result type (might be due to invalid IL or missing references)
Page val = Page.Root.CreatePage("HalfSwordControls", Color.cyan, 0, true);
val.CreateFloat("Pos Sensitivity", Color.white, Mathf.Clamp(ModConfig.PosSensitivity.Value, 0.001f, 0.02f), 0.001f, 0.001f, 0.02f, (Action<float>)delegate(float v)
{
ModConfig.PosSensitivity.Value = Round(v);
MarkDirty();
});
val.CreateFloat("Rot Sensitivity", Color.white, Mathf.Clamp(ModConfig.RotSensitivity.Value, 0.5f, 5f), 0.25f, 0.5f, 5f, (Action<float>)delegate(float v)
{
ModConfig.RotSensitivity.Value = Round(v);
MarkDirty();
});
val.CreateBool("Invert Y", Color.white, ModConfig.InvertY.Value, (Action<bool>)delegate(bool v)
{
ModConfig.InvertY.Value = v;
MarkDirty();
});
val.CreateFunction("Reset Hands", Color.yellow, (Action)delegate
{
FlatPlayerBridge.ResetHands();
_log.Msg("Reset Hands pressed.");
});
_log.Msg("BoneMenu page 'HalfSwordControls' created.");
}
private static float Round(float v)
{
return Mathf.Round(v * 10000f) / 10000f;
}
private void MarkDirty()
{
_dirty = true;
_lastChange = Time.realtimeSinceStartup;
}
public void OnUpdate()
{
if (_dirty && Time.realtimeSinceStartup - _lastChange > 1f)
{
Save();
}
}
private void Save()
{
_dirty = false;
MelonPreferences.Save();
}
public void Shutdown()
{
if (_dirty)
{
Save();
}
}
}
}
namespace HalfSwordControls.Features.ApiProbe
{
internal sealed class ApiProbeFeature : IFeature
{
private const string RigRootName = "RigManager(bonelab) [0]";
private static readonly string[] Types = new string[17]
{
"Il2CppSLZ.Marrow.Hand", "Il2CppSLZ.Marrow.Grip", "Il2CppSLZ.Marrow.InteractableHost", "Il2CppSLZ.Marrow.ForcePullGrip", "Il2CppSLZ.Marrow.BaseController", "Il2CppSLZ.Marrow.OpenController", "Il2CppSLZ.Marrow.ControllerRig", "Il2CppSLZ.Marrow.OpenControllerRig", "Il2CppSLZ.Marrow.RigManager", "Il2CppSLZ.Marrow.PhysicsRig",
"Il2CppSLZ.Marrow.RemapRig", "Il2CppSLZ.Marrow.PhysGrounder", "Il2CppSLZ.Marrow.Magazine", "Il2CppSLZ.Marrow.MagazineState", "Il2CppSLZ.Marrow.AmmoSocket", "Il2CppSLZ.Marrow.Gun", "Il2CppSLZ.Marrow.Blade"
};
private float _rigSeenAt = -1f;
private bool _autoDumped;
public string Name => "ApiProbe";
public bool Enabled => false;
public void Initialize(Harmony harmony, Instance log)
{
}
public void OnUpdate()
{
bool flag = Input.GetKeyDown((KeyCode)290);
if (!_autoDumped)
{
bool flag2 = (Object)(object)GameObject.Find("RigManager(bonelab) [0]") != (Object)null;
if (flag2 && _rigSeenAt < 0f)
{
_rigSeenAt = Time.time;
}
if (flag2 && Time.time - _rigSeenAt > 10f)
{
_autoDumped = true;
flag = true;
}
}
if (flag)
{
Dump();
}
}
private static void Dump()
{
try
{
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.AppendLine("######## BONELAB API DUMP ########");
GameObject val = GameObject.Find("RigManager(bonelab) [0]");
stringBuilder.AppendLine("######## HIERARCHY of RigManager(bonelab) [0] ########");
if ((Object)(object)val != (Object)null)
{
int budget = 3000;
DumpHierarchy(val.transform, 0, 6, stringBuilder, ref budget);
}
else
{
stringBuilder.AppendLine("(rig root not found)");
}
stringBuilder.AppendLine();
TypeExplorer.DumpTypes(Types, stringBuilder);
TypeExplorer.DumpAssembly("FlatPlayer", stringBuilder);
string text = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory), "HalfSword_ApiDump.txt");
File.WriteAllText(text, stringBuilder.ToString());
MelonLogger.Msg("[ApiProbe] Dump written to " + text);
}
catch (Exception ex)
{
MelonLogger.Error("[ApiProbe] Dump failed: " + ex);
}
}
private static void DumpHierarchy(Transform t, int depth, int maxDepth, StringBuilder sb, ref int budget)
{
if (budget-- <= 0)
{
return;
}
sb.AppendLine(new string(' ', depth * 2) + ((Object)t).name + (((Component)t).gameObject.activeSelf ? "" : " (inactive)"));
if (depth < maxDepth)
{
for (int i = 0; i < t.childCount; i++)
{
DumpHierarchy(t.GetChild(i), depth + 1, maxDepth, sb, ref budget);
}
}
}
public void Shutdown()
{
}
}
}
namespace HalfSwordControls.Core
{
internal static class FeatureRegistry
{
private static readonly List<IFeature> _all = new List<IFeature>();
private static readonly List<IFeature> _active = new List<IFeature>();
private static Instance _log;
public static void Register(IFeature feature)
{
_all.Add(feature);
}
public static void InitializeAll(Harmony harmony, Instance log)
{
_log = log;
foreach (IFeature item in _all)
{
if (!item.Enabled)
{
log.Msg("[" + item.Name + "] disabled in config.");
continue;
}
try
{
item.Initialize(harmony, log);
_active.Add(item);
log.Msg("[" + item.Name + "] initialized.");
}
catch (Exception value)
{
log.Error($"[{item.Name}] failed to initialize: {value}");
}
}
}
public static void Update()
{
for (int i = 0; i < _active.Count; i++)
{
try
{
_active[i].OnUpdate();
}
catch (Exception value)
{
_log.Error($"[{_active[i].Name}] OnUpdate error, feature disabled: {value}");
_active.RemoveAt(i);
i--;
}
}
}
public static void LateUpdate()
{
for (int i = 0; i < _active.Count; i++)
{
try
{
_active[i].OnLateUpdate();
}
catch (Exception value)
{
_log.Error($"[{_active[i].Name}] OnLateUpdate error, feature disabled: {value}");
_active.RemoveAt(i);
i--;
}
}
}
public static void Shutdown()
{
foreach (IFeature item in _active)
{
try
{
item.Shutdown();
}
catch
{
}
}
_active.Clear();
}
}
internal interface IFeature
{
string Name { get; }
bool Enabled { get; }
void Initialize(Harmony harmony, Instance log);
void OnUpdate();
void OnLateUpdate()
{
}
void Shutdown();
}
internal static class ModConfig
{
private static MelonPreferences_Category _cat;
public static MelonPreferences_Entry<bool> DecoupledInputEnabled;
public static MelonPreferences_Entry<KeyCode> CameraKey;
public static MelonPreferences_Entry<KeyCode> LeftHandButton;
public static MelonPreferences_Entry<KeyCode> RightHandButton;
public static MelonPreferences_Entry<KeyCode> RotateModifierKey;
public static MelonPreferences_Entry<float> RotSensitivity;
public static MelonPreferences_Entry<float> PosSensitivity;
public static MelonPreferences_Entry<bool> InvertY;
public static MelonPreferences_Entry<bool> RequireCursorLock;
public static MelonPreferences_Entry<float> AimYawLimit;
public static MelonPreferences_Entry<float> AimPitchLimit;
public static MelonPreferences_Entry<float> AimXLimit;
public static MelonPreferences_Entry<float> AimYLimit;
public static MelonPreferences_Entry<float> ReachPerScroll;
public static void Init()
{
_cat = MelonPreferences.CreateCategory("HalfSwordControls");
DecoupledInputEnabled = _cat.CreateEntry<bool>("DecoupledInput_Enabled", true, "Feature 1: Decoupled Input", (string)null, false, false, (ValueValidator)null, (string)null);
CameraKey = _cat.CreateEntry<KeyCode>("CameraKey", (KeyCode)308, "Hold: hand control off, camera free", (string)null, false, false, (ValueValidator)null, (string)null);
LeftHandButton = _cat.CreateEntry<KeyCode>("LeftHandButton", (KeyCode)323, "Hold to control LEFT hand", (string)null, false, false, (ValueValidator)null, (string)null);
RightHandButton = _cat.CreateEntry<KeyCode>("RightHandButton", (KeyCode)324, "Hold to control RIGHT hand", (string)null, false, false, (ValueValidator)null, (string)null);
RotateModifierKey = _cat.CreateEntry<KeyCode>("RotateModifierKey", (KeyCode)304, "Hold with hand button: mouse rotates wrist instead of moving hand", (string)null, false, false, (ValueValidator)null, (string)null);
RotSensitivity = _cat.CreateEntry<float>("RotSensitivity", 2f, "Wrist rotation (deg per mouse count)", (string)null, false, false, (ValueValidator)null, (string)null);
PosSensitivity = _cat.CreateEntry<float>("PosSensitivity", 0.003f, "Hand movement (meters per mouse count)", (string)null, false, false, (ValueValidator)null, (string)null);
InvertY = _cat.CreateEntry<bool>("InvertY", false, "Invert hand Y", (string)null, false, false, (ValueValidator)null, (string)null);
RequireCursorLock = _cat.CreateEntry<bool>("RequireCursorLock", true, "Only control hands while cursor is locked", (string)null, false, false, (ValueValidator)null, (string)null);
AimYawLimit = _cat.CreateEntry<float>("AimYawLimit", 120f, "Max wrist yaw from pose at press (deg, each side)", (string)null, false, false, (ValueValidator)null, (string)null);
AimPitchLimit = _cat.CreateEntry<float>("AimPitchLimit", 85f, "Max wrist pitch from pose at press (deg, each side)", (string)null, false, false, (ValueValidator)null, (string)null);
AimXLimit = _cat.CreateEntry<float>("AimXLimit", 0.5f, "Max hand X offset from pose at press (meters, each side)", (string)null, false, false, (ValueValidator)null, (string)null);
AimYLimit = _cat.CreateEntry<float>("AimYLimit", 0.5f, "Max hand Y offset from pose at press (meters, each side)", (string)null, false, false, (ValueValidator)null, (string)null);
ReachPerScroll = _cat.CreateEntry<float>("ReachPerScroll", 0.05f, "Thrust per scroll notch (meters)", (string)null, false, false, (ValueValidator)null, (string)null);
MelonPreferences.Save();
}
}
public sealed class ModEntry : MelonMod
{
public override void OnInitializeMelon()
{
ModConfig.Init();
FeatureRegistry.Register(new DecoupledInputFeature());
FeatureRegistry.Register(new ApiProbeFeature());
FeatureRegistry.Register(new BoneMenuFeature());
FeatureRegistry.InitializeAll(((MelonBase)this).HarmonyInstance, ((MelonBase)this).LoggerInstance);
((MelonBase)this).LoggerInstance.Msg("HalfSwordControls 0.5.0 loaded (BoneMenu).");
}
public override void OnUpdate()
{
FeatureRegistry.Update();
}
public override void OnLateUpdate()
{
FeatureRegistry.LateUpdate();
}
public override void OnDeinitializeMelon()
{
FeatureRegistry.Shutdown();
}
}
}