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<bool>)delegate(bool value)
{
Config.Enabled = value;
});
page.CreateBool("Motor Sound", Color.yellow, Config.MotorSound, (Action<bool>)delegate(bool value)
{
Config.MotorSound = value;
});
page.CreateFloat("Damage", Color.red, Config.Damage, 1f, 0f, 50f, (Action<float>)delegate(float value)
{
Config.Damage = value;
});
page.CreateFloat("Blade Speed", Color.cyan, Config.BladeDegreesPerSecond, 100f, 0f, 5000f, (Action<float>)delegate(float value)
{
Config.BladeDegreesPerSecond = value;
});
page.CreateFloat("Damage Interval", Color.magenta, Config.DamageInterval, 0.025f, 0.025f, 0.5f, (Action<float>)delegate(float value)
{
Config.DamageInterval = value;
});
page.CreateBool("Debug Mode", Color.white, Config.DebugMode, (Action<bool>)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<GameObject> spawnedChainsaws = new List<GameObject>();
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<string>.op_Implicit((Il2CppArrayBase<string>)(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<GameObject>(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<GameObject>(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<Transform> blades = new List<Transform>();
private readonly List<Collider> bladeColliders = new List<Collider>();
private readonly List<AudioSource> motorSounds = new List<AudioSource>();
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<Transform>.op_Implicit(root.GetComponentsInChildren<Transform>(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<Collider>.op_Implicit(root.GetComponentsInChildren<Collider>(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<AudioSource>.op_Implicit(root.GetComponentsInChildren<AudioSource>(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<AIBrain> hashSet = new HashSet<AIBrain>();
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<Collider>.op_Implicit((Il2CppArrayBase<Collider>)(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<AIBrain>();
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<Hand, ChainsawRuntime> held = new Dictionary<Hand, ChainsawRuntime>();
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<Transform>.op_Implicit(((Component)val3).GetComponentsInChildren<Transform>(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<Transform>.op_Implicit(grabbed.GetComponentsInChildren<Transform>(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;
}
}