using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Text.Json; using System.Text.Json.Serialization; using BodylogExtender; using BodylogExtender.AvatarData; using BodylogExtender.Managers; using BodylogExtender.Util; using BoneLib; using BoneLib.BoneMenu; using HarmonyLib; using Il2CppSLZ.Bonelab; using Il2CppSLZ.Bonelab.SaveData; using Il2CppSLZ.Marrow; using Il2CppSLZ.Marrow.SaveData; using Il2CppSLZ.Marrow.Warehouse; using Il2CppSystem.Collections.Generic; using LabFusion.Utilities; using MelonLoader; using MelonLoader.Utils; using Microsoft.CodeAnalysis; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: MelonInfo(typeof(Mod), "BodylogExtender", "1.0.0", "mash", null)] [assembly: MelonGame("Stress Level Zero", "BONELAB")] [assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = "")] [assembly: AssemblyCompany("BodylogExtender")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0")] [assembly: AssemblyProduct("BodylogExtender")] [assembly: AssemblyTitle("BodylogExtender")] [assembly: AssemblyVersion("1.0.0.0")] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } } namespace BodylogExtender { public class Mod : MelonMod { public static bool IsFusionLoaded; public override void OnInitializeMelon() { //IL_0005: Unknown result type (might be due to invalid IL or missing references) new Harmony("BodylogExtender").PatchAll(); IsFusionLoaded = MelonBase.FindMelon("LabFusion", "Lakatrazz") != null; PresetManager.LoadPresetManager(); ModBoneMenu.CreateBoneMenu(); } public override void OnUpdate() { BodyLogManager.Update(); } } public abstract class ModBoneMenu { public static void CreateBoneMenu() { //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_0054: Unknown result type (might be due to invalid IL or missing references) Page obj = Page.Root.CreatePage("BodyLog Extender", Color.magenta, 0, true); obj.CreateInt("Preset Count", Color.magenta, (int)PresetManager.GetPresetCount(), 1, 0, 10, (Action)delegate(int value) { PresetManager.SetPresetCount((uint)value); }); obj.CreateFunction("Force Save", Color.green, (Action)PresetManager.SavePresetManager); } } } namespace BodylogExtender.Util { public class BodyLog { public const int BodylogAvatarCount = 6; private readonly PullCordDevice _pullCordDevice; private readonly MeshRenderer _meshRenderer; public bool IsGrabbed; public bool IsColorDirty; public int AvatarIndex => _pullCordDevice.avatarIndex - 1; public BodyLog(PullCordDevice pullCordDevice) { _pullCordDevice = pullCordDevice; _meshRenderer = pullCordDevice.previewMeshRenderer; } public static AvatarPreset GetPreset() { List favoriteAvatars_k__BackingField = DataManager.ActiveSave._PlayerSettings_k__BackingField._FavoriteAvatars_k__BackingField; string[] array = new string[6]; for (int i = 0; i < favoriteAvatars_k__BackingField._size; i++) { array[i] = favoriteAvatars_k__BackingField._items[i]; } return new AvatarPreset(array); } private static List GetAvatarsNativeList(AvatarPreset preset) { List val = new List(); for (int i = 0; i < 6; i++) { val.Add(preset.Avatars[i]); } return val; } public static void SetPreset(AvatarPreset preset, BodyLog? bodyLog = null) { List avatarsNativeList = GetAvatarsNativeList(preset); DataManager.ActiveSave._PlayerSettings_k__BackingField._FavoriteAvatars_k__BackingField = avatarsNativeList; DataManager.TrySaveActiveSave((SaveFlags)0); bodyLog?.SyncToGameObject(); } public void SetColor(Color color) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) ((Renderer)_meshRenderer).material.color = color; ((Renderer)_meshRenderer).material.SetColor("_EmissionColor", color); } public bool IsValid() { return Object.op_Implicit((Object)(object)_pullCordDevice); } public void SyncToGameObject() { //IL_0011: Unknown result type (might be due to invalid IL or missing references) _pullCordDevice.LoadFavoriteAvatars(); _pullCordDevice.UpdateAllPreviewMeshes(); _pullCordDevice.lastAvatarIndex = (_pullCordDevice.avatarIndex + 1) % 6; _pullCordDevice.SetPreviewMesh(_pullCordDevice.avatarIndex); IsColorDirty = true; } public void AssignSlot() { if (IsGrabbed) { int avatarIndex = AvatarIndex; string id = ((ScannableReference)Player.RigManager._avatarCrate)._barcode._id; if (!string.IsNullOrEmpty(id)) { AvatarPreset preset = GetPreset(); preset.Avatars[avatarIndex] = id; SetPreset(preset, this); } } } } } namespace BodylogExtender.Patches { [HarmonyPatch(typeof(PullCordDevice))] public class BodyLogPatches { [HarmonyPrefix] [HarmonyPatch("OnBallGripAttached")] private static bool OnBallGripAttachedPrefix(PullCordDevice __instance) { BodyLogManager.OnBodyLogGrabbed(__instance); return true; } [HarmonyPrefix] [HarmonyPatch("OnBallGripDetached")] private static bool OnBallGripDetachedPrefix(PullCordDevice __instance) { BodyLogManager.OnBodyLogReleased(__instance); return true; } } } namespace BodylogExtender.Managers { public static class BodyLogManager { private static BodyLog? _activeBodyLog; private static bool _overwriteComplete; private static float _overwriteTimer; private static bool IsFusionLocalRig(RigManager rig) { return FusionPlayer.IsLocalPlayer(rig); } private static bool IsLocalPlayer(RigManager rig) { if (Mod.IsFusionLoaded) { return IsFusionLocalRig(rig); } return true; } public static void OnBodyLogGrabbed(PullCordDevice pullCordDevice) { if (IsLocalPlayer(pullCordDevice.rm)) { if (_activeBodyLog == null || !_activeBodyLog.IsValid()) { _activeBodyLog = new BodyLog(pullCordDevice); BodyLog.SetPreset(PresetManager.GetActivePreset(), _activeBodyLog); _activeBodyLog.IsGrabbed = true; } else if (!_activeBodyLog.IsGrabbed) { _activeBodyLog.IsGrabbed = true; PresetManager.SetActivePreset(BodyLog.GetPreset()); } } } public static void OnBodyLogReleased(PullCordDevice pullCordDevice) { if (IsLocalPlayer(pullCordDevice.rm) && _activeBodyLog != null && _activeBodyLog.IsValid() && _activeBodyLog.IsGrabbed) { _activeBodyLog.IsGrabbed = false; PresetManager.SavePresetManager(); } } private static void UpdateOverwriteSlot() { if (!InputManager.IsOverwriteButtonDown()) { _overwriteTimer = 0f; _overwriteComplete = false; } else if (!_overwriteComplete) { _overwriteTimer += Time.deltaTime; if (!(_overwriteTimer < 2.5f)) { _overwriteComplete = true; _activeBodyLog?.AssignSlot(); } } } private static void UpdateSwitchPreset() { if (InputManager.IsSwitchPresetButtonDown()) { PresetManager.SetActivePreset(BodyLog.GetPreset()); PresetManager.ToNextPreset(); BodyLog.SetPreset(PresetManager.GetActivePreset(), _activeBodyLog); } } public static void Update() { //IL_0033: Unknown result type (might be due to invalid IL or missing references) if (_activeBodyLog != null && _activeBodyLog.IsValid() && _activeBodyLog.IsGrabbed) { if (_activeBodyLog.IsColorDirty) { _activeBodyLog.SetColor(PresetManager.GetPresetColor()); } UpdateSwitchPreset(); UpdateOverwriteSlot(); } } } public abstract class InputManager { private static BaseController GetGrabbingController() { if (!PlayerRefs._Instance_k__BackingField._bodyVitals.bodyLogFlipped) { return Player.LeftController; } return Player.RightController; } public static bool IsSwitchPresetButtonDown() { return GetGrabbingController()._menuTap; } public static bool IsOverwriteButtonDown() { return GetGrabbingController()._bButton; } } public static class PresetManager { private static readonly string FilePath = MelonEnvironment.UserDataDirectory + "/FavoriteAvatars.json"; private static PresetState _state = new PresetState(); public static void SetActivePreset(AvatarPreset preset) { _state.ActivePreset = preset; } public static Color GetPresetColor() { //IL_0029: Unknown result type (might be due to invalid IL or missing references) return Color.HSVToRGB(1f / (float)_state.Size * (float)_state.Index, 1f, 1f); } public static AvatarPreset GetActivePreset() { return _state.GetOrCreateActivePreset(); } public static void ToNextPreset() { _state.Index = (_state.Index + 1) % _state.Size; } public static uint GetPresetCount() { return _state.Size; } public static void SetPresetCount(uint count) { _state.Size = count; } private static PresetState? LoadPresetState() { if (!File.Exists(FilePath)) { return null; } try { return JsonSerializer.Deserialize(File.ReadAllText(FilePath)); } catch (Exception ex) { MelonLogger.Error("Failed to load presets from file!", ex); return null; } } private static void SavePresetState(PresetState presetSaveData) { try { string contents = JsonSerializer.Serialize(presetSaveData); File.WriteAllText(FilePath, contents); } catch (Exception ex) { MelonLogger.Error("Failed to save bodylog presets", ex); } } public static void LoadPresetManager() { PresetState presetState = LoadPresetState(); if (presetState != null) { _state = presetState; } } public static void SavePresetManager() { SavePresetState(_state); } } } namespace BodylogExtender.AvatarData { public class AvatarPreset { [JsonInclude] public readonly string[] Avatars; public AvatarPreset(string[] avatars) { if (avatars.Length != 6) { throw new Exception("Avatar list must have the same number of avatars."); } Avatars = avatars; } } public class PresetState { [JsonInclude] public uint Size = 4u; [JsonInclude] public uint Index; [JsonInclude] public Dictionary Presets = new Dictionary(); [JsonIgnore] public AvatarPreset ActivePreset { get { return Presets[(int)Index]; } set { Presets[(int)Index] = value; } } public AvatarPreset GetOrCreateActivePreset() { if (Presets.TryGetValue((int)Index, out AvatarPreset value)) { return value; } AvatarPreset avatarPreset = new AvatarPreset(new string[6] { "fa534c5a83ee4ec6bd641fec424c4142.Avatar.Strong", "fa534c5a83ee4ec6bd641fec424c4142.Avatar.Strong", "fa534c5a83ee4ec6bd641fec424c4142.Avatar.Strong", "fa534c5a83ee4ec6bd641fec424c4142.Avatar.Strong", "fa534c5a83ee4ec6bd641fec424c4142.Avatar.Strong", "fa534c5a83ee4ec6bd641fec424c4142.Avatar.Strong" }); Presets.Add((int)Index, avatarPreset); return avatarPreset; } } }