using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using BoneLib; using BoneLib.BoneMenu; using ChainsawBONELABCodeMod; using Il2CppInterop.Runtime.InteropTypes.Arrays; using Il2CppSLZ.Marrow; using Il2CppSLZ.Marrow.AI; using MelonLoader; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: MelonInfo(typeof(Main), "Chainsaw BONELAB Code Mod", "1.3.1", "TankFullOfOofs Port", null)] [assembly: MelonGame("Stress Level Zero", "BONELAB")] [assembly: TargetFramework(".NETCoreApp,Version=v8.0", FrameworkDisplayName = ".NET 8.0")] [assembly: AssemblyCompany("ChainsawBONELABCodeMod")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0+8887163c740aae2e742422dd452ade3ea13b7ce5")] [assembly: AssemblyProduct("ChainsawBONELABCodeMod")] [assembly: AssemblyTitle("ChainsawBONELABCodeMod")] [assembly: AssemblyVersion("1.0.0.0")] [module: RefSafetyRules(11)] namespace ChainsawBONELABCodeMod; internal static class ChainsawMenu { private static Page page; private static bool initialized; public static void Setup() { //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_0096: 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) //IL_0108: Unknown result type (might be due to invalid IL or missing references) //IL_0150: Unknown result type (might be due to invalid IL or missing references) //IL_0198: Unknown result type (might be due to invalid IL or missing references) //IL_01e0: Unknown result type (might be due to invalid IL or missing references) if (initialized) { return; } try { page = Page.Root.CreatePage("Chainsaw", Color.red, 0, true); page.CreateFunction("Spawn Chainsaw", Color.green, (Action)delegate { MelonLogger.Msg("[Chainsaw] Spawn button pressed."); try { ChainsawSpawner.Spawn(); MelonLogger.Msg("[Chainsaw] Spawn callback finished."); } catch (Exception ex2) { MelonLogger.Error("[Chainsaw] Spawn callback crashed: " + ex2); } }); page.CreateFunction("Despawn Chainsaws", Color.red, (Action)delegate { MelonLogger.Msg("[Chainsaw] Despawn button pressed."); ChainsawSpawner.DespawnAll(); }); page.CreateBool("Enabled", Color.green, Config.Enabled, (Action)delegate(bool value) { Config.Enabled = value; }); page.CreateBool("Motor Sound", Color.yellow, Config.MotorSound, (Action)delegate(bool value) { Config.MotorSound = value; }); page.CreateFloat("Damage", Color.red, Config.Damage, 1f, 0f, 50f, (Action)delegate(float value) { Config.Damage = value; }); page.CreateFloat("Blade Speed", Color.cyan, Config.BladeDegreesPerSecond, 100f, 0f, 5000f, (Action)delegate(float value) { Config.BladeDegreesPerSecond = value; }); page.CreateFloat("Damage Interval", Color.magenta, Config.DamageInterval, 0.025f, 0.025f, 0.5f, (Action)delegate(float value) { Config.DamageInterval = value; }); page.CreateBool("Debug Mode", Color.white, Config.DebugMode, (Action)delegate(bool value) { Config.DebugMode = value; }); initialized = true; MelonLogger.Msg("[Chainsaw] BoneMenu page created."); } catch (Exception ex) { MelonLogger.Error("[Chainsaw] BoneMenu setup failed: " + ex); } } } internal static class ChainsawSpawner { private static AssetBundle bundle; private static GameObject prefab; private static readonly List spawnedChainsaws = new List(); public static void Initialize() { if ((Object)(object)prefab != (Object)null) { return; } try { string text = FindBundlePath(); if (string.IsNullOrEmpty(text)) { MelonLogger.Warning("[Chainsaw] chainsaw.bundle was not found in any expected Quest Mods path."); MelonLogger.Warning("[Chainsaw] Expected: " + Path.Combine(Application.persistentDataPath, "Mods", "Chainsaw", "chainsaw.bundle")); return; } MelonLogger.Msg("[Chainsaw] Loading bundle from: " + text); bundle = AssetBundle.LoadFromFile(text); if ((Object)(object)bundle == (Object)null) { MelonLogger.Error("[Chainsaw] Failed to load chainsaw.bundle. The old bundle may be incompatible with the current BONELAB/Unity runtime."); return; } string[] array = Il2CppArrayBase.op_Implicit((Il2CppArrayBase)(object)bundle.GetAllAssetNames()); if (array != null) { MelonLogger.Msg("[Chainsaw] Bundle asset count: " + array.Length); string[] array2 = array; foreach (string text2 in array2) { MelonLogger.Msg("[Chainsaw] Bundle asset: " + text2); } } prefab = TryLoadPrefab("Chainsaw.prefab"); if ((Object)(object)prefab == (Object)null) { prefab = TryLoadPrefab("Chainsaw"); } if ((Object)(object)prefab == (Object)null && array != null) { string[] array2 = array; foreach (string text3 in array2) { if (!string.IsNullOrEmpty(text3)) { GameObject val = TryLoadPrefab(text3); if ((Object)(object)val != (Object)null) { prefab = val; MelonLogger.Msg("[Chainsaw] Using GameObject asset: " + text3); break; } } } } if ((Object)(object)prefab == (Object)null) { MelonLogger.Error("[Chainsaw] Bundle loaded, but no GameObject prefab could be loaded from it."); } else { MelonLogger.Msg("[Chainsaw] Spawn prefab loaded: " + ((Object)prefab).name); } } catch (Exception ex) { MelonLogger.Error("[Chainsaw] AssetBundle load failed: " + ex); } } private static string FindBundlePath() { string text = Path.Combine(Application.persistentDataPath, "Mods", "Chainsaw", "chainsaw.bundle"); if (File.Exists(text)) { return text; } string text2 = Path.Combine(Directory.GetCurrentDirectory(), "Mods", "Chainsaw", "chainsaw.bundle"); if (File.Exists(text2)) { return text2; } string text3 = "/storage/emulated/0/Android/data/com.StressLevelZero.BONELAB/files/Mods/Chainsaw/chainsaw.bundle"; if (File.Exists(text3)) { return text3; } return null; } private static GameObject TryLoadPrefab(string assetName) { try { return bundle.LoadAsset(assetName); } catch { return null; } } public unsafe static void Spawn() { //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Unknown result type (might be due to invalid IL or missing references) //IL_0076: 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_0082: Unknown result type (might be due to invalid IL or missing references) //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_0091: Unknown result type (might be due to invalid IL or missing references) //IL_0097: Unknown result type (might be due to invalid IL or missing references) //IL_0098: 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_00d6: Unknown result type (might be due to invalid IL or missing references) if (!Config.Enabled) { MelonLogger.Warning("[Chainsaw] Mod is disabled in BoneMenu."); return; } if ((Object)(object)prefab == (Object)null) { Initialize(); } if ((Object)(object)prefab == (Object)null) { MelonLogger.Warning("[Chainsaw] Can't spawn because chainsaw.bundle is missing, incompatible, or contains no loadable GameObject prefab."); return; } Camera main = Camera.main; if ((Object)(object)main == (Object)null) { MelonLogger.Warning("[Chainsaw] Main camera/player view is not ready."); return; } Vector3 val = ((Component)main).transform.position + ((Component)main).transform.forward * 1.5f; Quaternion val2 = Quaternion.LookRotation(((Component)main).transform.forward, Vector3.up); GameObject val3 = Object.Instantiate(prefab, val, val2); if ((Object)(object)val3 == (Object)null) { MelonLogger.Error("[Chainsaw] Instantiate returned null."); return; } ((Object)val3).name = "Chainsaw"; val3.SetActive(true); spawnedChainsaws.Add(val3); Vector3 val4 = val; MelonLogger.Msg("[Chainsaw] Spawned Chainsaw from BoneMenu at " + ((object)(*(Vector3*)(&val4))/*cast due to .constrained prefix*/).ToString()); } public static void DespawnAll() { for (int num = spawnedChainsaws.Count - 1; num >= 0; num--) { GameObject val = spawnedChainsaws[num]; if ((Object)(object)val != (Object)null) { Object.Destroy((Object)(object)val); } } spawnedChainsaws.Clear(); MelonLogger.Msg("[Chainsaw] Despawned all BoneMenu-spawned Chainsaws."); } } internal static class Config { public static bool Enabled = true; public static float BladeDegreesPerSecond = 1800f; public static float Damage = 7.5f; public static float DamageInterval = 0.075f; public static bool MotorSound = true; public static bool DebugMode = false; } public sealed class Main : MelonMod { private sealed class ChainsawRuntime { private readonly GameObject root; private readonly List blades = new List(); private readonly List bladeColliders = new List(); private readonly List motorSounds = new List(); private bool running; public int BladeCount => blades.Count; public int BladeColliderCount => bladeColliders.Count; public int MotorSoundCount => motorSounds.Count; public ChainsawRuntime(GameObject rootObject) { root = rootObject; Transform[] array = Il2CppArrayBase.op_Implicit(root.GetComponentsInChildren(true)); foreach (Transform val in array) { if (!((Object)(object)val == (Object)null)) { string text = ((Object)val).name ?? string.Empty; if (text.IndexOf("BladeTransform", StringComparison.OrdinalIgnoreCase) >= 0 || string.Equals(text, "Blade", StringComparison.OrdinalIgnoreCase) || text.IndexOf("slashTop", StringComparison.OrdinalIgnoreCase) >= 0 || text.IndexOf("slashBottom", StringComparison.OrdinalIgnoreCase) >= 0) { blades.Add(val); } } } Collider[] array2 = Il2CppArrayBase.op_Implicit(root.GetComponentsInChildren(true)); foreach (Collider val2 in array2) { if (!((Object)(object)val2 == (Object)null)) { string text2 = ((Object)val2).name ?? string.Empty; if (text2.IndexOf("BladeCollider", StringComparison.OrdinalIgnoreCase) >= 0 || text2.IndexOf("Blade", StringComparison.OrdinalIgnoreCase) >= 0 || text2.IndexOf("slashTop", StringComparison.OrdinalIgnoreCase) >= 0 || text2.IndexOf("slashBottom", StringComparison.OrdinalIgnoreCase) >= 0 || text2.IndexOf("StabPoint", StringComparison.OrdinalIgnoreCase) >= 0) { bladeColliders.Add(val2); } } } AudioSource[] array3 = Il2CppArrayBase.op_Implicit(root.GetComponentsInChildren(true)); foreach (AudioSource val3 in array3) { if (!((Object)(object)val3 == (Object)null)) { string text3 = ((Object)val3).name ?? string.Empty; if (text3.IndexOf("IdleSound", StringComparison.OrdinalIgnoreCase) >= 0 || text3.IndexOf("BladeAudio", StringComparison.OrdinalIgnoreCase) >= 0 || text3.IndexOf("Motor", StringComparison.OrdinalIgnoreCase) >= 0 || text3.IndexOf("Chainsaw", StringComparison.OrdinalIgnoreCase) >= 0 || text3.IndexOf("Idle", StringComparison.OrdinalIgnoreCase) >= 0) { motorSounds.Add(val3); } } } } public void Start() { running = true; if (!Config.MotorSound) { return; } foreach (AudioSource motorSound in motorSounds) { try { motorSound.loop = true; if ((Object)(object)motorSound.clip != (Object)null && !motorSound.isPlaying) { motorSound.Play(); } } catch (Exception ex) { MelonLogger.Warning("Chainsaw audio start failed: " + ex.Message); } } } public void Stop() { running = false; foreach (AudioSource motorSound in motorSounds) { try { if ((Object)(object)motorSound != (Object)null && motorSound.isPlaying) { motorSound.Stop(); } } catch { } } } public void Tick(float dt) { //IL_0037: Unknown result type (might be due to invalid IL or missing references) if (!running || (Object)(object)root == (Object)null) { return; } foreach (Transform blade in blades) { if ((Object)(object)blade != (Object)null) { blade.Rotate(Vector3.forward, Config.BladeDegreesPerSecond * dt, (Space)1); } } } public void DamageNearby() { //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_0067: 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_0070: Unknown result type (might be due to invalid IL or missing references) if (!running || (Object)(object)root == (Object)null) { return; } HashSet hashSet = new HashSet(); foreach (Collider bladeCollider in bladeColliders) { if ((Object)(object)bladeCollider == (Object)null || !bladeCollider.enabled) { continue; } Bounds bounds = bladeCollider.bounds; Vector3 center = ((Bounds)(ref bounds)).center; bounds = bladeCollider.bounds; Vector3 extents = ((Bounds)(ref bounds)).extents; float num = Mathf.Max(0.035f, ((Vector3)(ref extents)).magnitude * 0.45f); Collider[] array = Il2CppArrayBase.op_Implicit((Il2CppArrayBase)(object)Physics.OverlapSphere(center, num)); foreach (Collider val in array) { if ((Object)(object)val == (Object)null || ((Component)val).transform.IsChildOf(root.transform)) { continue; } AIBrain componentInParent = ((Component)val).GetComponentInParent(); if (!((Object)(object)componentInParent == (Object)null) && hashSet.Add(componentInParent)) { try { Extensions.DealDamage(componentInParent, Config.Damage); } catch (Exception ex) { MelonLogger.Warning("Chainsaw damage failed: " + ex.Message); } } } } } } private readonly Dictionary held = new Dictionary(); private float damageTimer; public override void OnInitializeMelon() { MelonLogger.Msg("[Chainsaw] v1.3.1 SPAWN TEST BUILD LOADED"); Hooking.OnGrabObject += OnGrab; Hooking.OnReleaseObject += OnRelease; ChainsawSpawner.Initialize(); ChainsawMenu.Setup(); MelonLogger.Msg("Chainsaw BONELAB Code Mod 1.3.1 initialized. BoneMenu spawning support enabled."); } public override void OnDeinitializeMelon() { Hooking.OnGrabObject -= OnGrab; Hooking.OnReleaseObject -= OnRelease; foreach (ChainsawRuntime value in held.Values) { value.Stop(); } held.Clear(); ChainsawSpawner.DespawnAll(); } public override void OnUpdate() { if (!Config.Enabled) { return; } float deltaTime = Time.deltaTime; damageTimer -= deltaTime; foreach (ChainsawRuntime value in held.Values) { value.Tick(deltaTime); } if (damageTimer > 0f) { return; } damageTimer = Mathf.Max(0.025f, Config.DamageInterval); foreach (ChainsawRuntime value2 in held.Values) { value2.DamageNearby(); } } private void OnGrab(GameObject objectToAttach, Hand hand) { if (!Config.Enabled || (Object)(object)objectToAttach == (Object)null || (Object)(object)hand == (Object)null) { return; } GameObject val = ResolveChainsawRoot(objectToAttach); if (!((Object)(object)val == (Object)null)) { if (held.TryGetValue(hand, out var value)) { value.Stop(); } ChainsawRuntime chainsawRuntime = new ChainsawRuntime(val); held[hand] = chainsawRuntime; chainsawRuntime.Start(); MelonLogger.Msg("Chainsaw detected: " + ((Object)val).name + ". Blades=" + chainsawRuntime.BladeCount + ", BladeColliders=" + chainsawRuntime.BladeColliderCount + ", MotorSounds=" + chainsawRuntime.MotorSoundCount); } } private void OnRelease(Hand hand) { if (!((Object)(object)hand == (Object)null) && held.TryGetValue(hand, out var value)) { value.Stop(); held.Remove(hand); MelonLogger.Msg("Chainsaw released; runtime behavior disabled."); } } private static bool LooksLikeChainsawName(string value) { if (string.IsNullOrEmpty(value)) { return false; } if (value.IndexOf("chainsaw", StringComparison.OrdinalIgnoreCase) < 0 && value.IndexOf("DoomHunterChainsaw", StringComparison.OrdinalIgnoreCase) < 0) { return value.IndexOf("Doom Hunter Chainsaw", StringComparison.OrdinalIgnoreCase) >= 0; } return true; } private static bool IsDoomHunterMarker(string value) { if (string.IsNullOrEmpty(value)) { return false; } if (value.IndexOf("slashTop", StringComparison.OrdinalIgnoreCase) < 0 && value.IndexOf("slashBottom", StringComparison.OrdinalIgnoreCase) < 0 && value.IndexOf("Pull Cord", StringComparison.OrdinalIgnoreCase) < 0) { return value.IndexOf("StabPoint", StringComparison.OrdinalIgnoreCase) >= 0; } return true; } private static GameObject ResolveChainsawRoot(GameObject grabbed) { Transform val = grabbed.transform; GameObject val2 = null; while ((Object)(object)val != (Object)null) { if (LooksLikeChainsawName(((Object)val).name)) { val2 = ((Component)val).gameObject; } val = val.parent; } if ((Object)(object)val2 != (Object)null) { return val2; } Transform val3 = grabbed.transform; while ((Object)(object)val3.parent != (Object)null && (Object)(object)val3.parent.parent != (Object)null) { val3 = val3.parent; } Transform[] array = Il2CppArrayBase.op_Implicit(((Component)val3).GetComponentsInChildren(true)); int num = 0; bool flag = false; bool flag2 = false; Transform[] array2 = array; foreach (Transform val4 in array2) { if (!((Object)(object)val4 == (Object)null)) { string text = ((Object)val4).name ?? string.Empty; if (IsDoomHunterMarker(text)) { num++; } if (text.IndexOf("Blade", StringComparison.OrdinalIgnoreCase) >= 0 || text.IndexOf("slashTop", StringComparison.OrdinalIgnoreCase) >= 0 || text.IndexOf("slashBottom", StringComparison.OrdinalIgnoreCase) >= 0) { flag = true; } if (text.IndexOf("Grip", StringComparison.OrdinalIgnoreCase) >= 0) { flag2 = true; } } } if (num >= 2 && flag && flag2) { return ((Component)val3).gameObject; } array2 = Il2CppArrayBase.op_Implicit(grabbed.GetComponentsInChildren(true)); foreach (Transform val5 in array2) { if (!((Object)(object)val5 == (Object)null)) { string text2 = ((Object)val5).name ?? string.Empty; if (text2.IndexOf("BladeTransform", StringComparison.OrdinalIgnoreCase) >= 0 || text2.IndexOf("BladeCollider", StringComparison.OrdinalIgnoreCase) >= 0 || text2.IndexOf("IdleSound", StringComparison.OrdinalIgnoreCase) >= 0) { return grabbed; } } } return null; } }