Decompiled source of madman v1.0.0

Mods/madman.dll

Decompiled a day ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Resources;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using HarmonyLib;
using Il2CppInterop.Runtime;
using Il2CppInterop.Runtime.InteropTypes;
using Il2CppInterop.Runtime.InteropTypes.Arrays;
using Il2CppScheduleOne.Combat;
using Il2CppScheduleOne.Effects;
using Il2CppScheduleOne.Equipping;
using Il2CppScheduleOne.FX;
using Il2CppScheduleOne.ItemFramework;
using Il2CppScheduleOne.NPCs;
using Il2CppScheduleOne.PlayerScripts;
using Il2CppScheduleOne.PlayerScripts.Health;
using MadmanMod;
using MelonLoader;
using MelonLoader.Preferences;
using Microsoft.CodeAnalysis;
using S1API.Console;
using S1API.Lifecycle;
using S1APITemplate;
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(Core), "madman", "1.0.0", "Timber Van Thorn", null)]
[assembly: MelonGame("TVGS", "Schedule I")]
[assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")]
[assembly: AssemblyCompany("madman")]
[assembly: AssemblyConfiguration("CrossCompat")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+ff93ce131518eef04a8566b62f1d3c0b8347bdfc")]
[assembly: AssemblyProduct("madman")]
[assembly: AssemblyTitle("madman")]
[assembly: NeutralResourcesLanguage("en-US")]
[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.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]
	[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;
		}
	}
	[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 S1APITemplate
{
	public sealed class Core : MelonMod
	{
		private static MelonPreferences_Category? _preferencesCategory;

		private static MelonPreferences_Entry<bool>? _debugLogsEntry;

		public static Core? Instance { get; private set; }

		public static bool DebugLogsEnabled => _debugLogsEntry?.Value ?? false;

		public override void OnInitializeMelon()
		{
			Instance = this;
			InitializePreferences();
			GameLifecycle.OnPreLoad += OnPreLoad;
			GameLifecycle.OnLoadComplete += OnLoadComplete;
			((MelonBase)this).LoggerInstance.Msg("madman initialized.");
		}

		public override void OnUpdate()
		{
			CheatCode.Tick();
			PlayerAttack.Tick();
			PlayerEffects.Tick();
			InfiniteAmmo.Tick();
		}

		public override void OnApplicationQuit()
		{
			GameLifecycle.OnPreLoad -= OnPreLoad;
			GameLifecycle.OnLoadComplete -= OnLoadComplete;
			Instance = null;
		}

		private void OnPreLoad()
		{
			((MelonBase)this).LoggerInstance.Msg("Game is preparing to load. Register stable S1API content needed by save data here.");
		}

		private void OnLoadComplete()
		{
			((MelonBase)this).LoggerInstance.Msg("Save loading is complete. Initialize systems that depend on the loaded world here.");
		}

		public override void OnSceneWasLoaded(int buildIndex, string sceneName)
		{
			MelonLogger.Msg("[Madman] scene loaded: " + sceneName);
			if (sceneName == "Menu" || sceneName == "Main")
			{
				MadmanState.Reset();
			}
		}

		private static void InitializePreferences()
		{
			_preferencesCategory = MelonPreferences.CreateCategory("madman");
			_debugLogsEntry = _preferencesCategory.CreateEntry<bool>("DebugLogs", false, "Enable Debug Logs", "Show detailed debug messages in the MelonLoader console.", false, false, (ValueValidator)null, (string)null);
			_preferencesCategory.SaveToFile(false);
		}
	}
}
namespace S1APITemplate.Utils
{
	public static class Constants
	{
		public static class Defaults
		{
			public const bool DebugLogsEnabled = false;
		}

		public static class Constraints
		{
			public const float MinConstraint = 0f;

			public const float MaxConstraint = 100f;
		}

		public static class Game
		{
			public const string Studio = "TVGS";

			public const string Name = "Schedule I";
		}

		public const string ModName = "madman";

		public const string ModVersion = "1.0.0";

		public const string ModAuthor = "Timber Van Thorn";

		public const string ModDescription = "Be careful, cops can still arrest a god for the many horrible things he done-did. uwu";

		public const string PreferencesCategory = "madman";
	}
}
namespace MadmanMod
{
	public static class CheatCode
	{
		private static int taps;

		private static float lastTap;

		public static void Tick()
		{
			if (Input.GetKeyDown((KeyCode)289))
			{
				float unscaledTime = Time.unscaledTime;
				taps = ((!(unscaledTime - lastTap < 1.2f)) ? 1 : (taps + 1));
				lastTap = unscaledTime;
				if (taps >= 3)
				{
					taps = 0;
					MadmanState.Toggle();
				}
			}
		}
	}
	public static class ExplosionFx
	{
		private const bool UseGameExplosion = true;

		private const bool KeepOwnBlast = false;

		private const float NpcCooldown = 0.75f;

		private const float GlobalCooldown = 0.25f;

		private const bool FollowRagdoll = true;

		private const float MinFollowSeconds = 0.25f;

		private const float MaxFollowSeconds = 1f;

		private const float SettleDistance = 0.1f;

		private const float DamageRadius = 6f;

		private const float MaxDamage = 200f;

		private const float PushRadius = 8f;

		private const float MaxPush = 15f;

		private static CombatManager combat;

		private static object dataObj;

		private static float lastAny;

		private static readonly Dictionary<int, float> lastNpc = new Dictionary<int, float>();

		private const float FollowSeconds = 0.2f;

		public static void Boom(Vector3 pos, NPCHealth primary, bool follow = false)
		{
			//IL_0064: Unknown result type (might be due to invalid IL or missing references)
			//IL_0065: Unknown result type (might be due to invalid IL or missing references)
			//IL_006a: 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)
			float time = Time.time;
			int instanceID = ((Object)primary).GetInstanceID();
			if (!(time - lastAny < 0.25f) && (!lastNpc.TryGetValue(instanceID, out var value) || !(time - value < 0.75f)))
			{
				lastAny = time;
				lastNpc[instanceID] = time;
				SkinnedMeshRenderer body = FindBody(primary);
				Vector3 start = BodyPoint(body, primary, pos);
				bool flag = false;
				MelonCoroutines.Start(BoomAfter(primary, body, start, instanceID, 0.2f));
			}
		}

		private static IEnumerator BoomAfter(NPCHealth h, SkinnedMeshRenderer body, Vector3 start, int id, float seconds)
		{
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			Vector3 last = start;
			float t = 0.1f;
			while (t < seconds)
			{
				yield return null;
				t += Time.deltaTime;
				if ((Object)(object)h == (Object)null)
				{
					break;
				}
				last = BodyPoint(body, h, last);
			}
			Explode(last, id);
		}

		private static SkinnedMeshRenderer FindBody(NPCHealth h)
		{
			try
			{
				NPC componentInParent = ((Component)h).GetComponentInParent<NPC>();
				Transform val = (((Object)(object)componentInParent != (Object)null) ? ((Component)componentInParent).transform : ((Component)h).transform);
				return ((Component)val).GetComponentInChildren<SkinnedMeshRenderer>();
			}
			catch
			{
				return null;
			}
		}

		private static Vector3 BodyPoint(SkinnedMeshRenderer body, NPCHealth h, Vector3 fallback)
		{
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: 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_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				if ((Object)(object)body != (Object)null)
				{
					Bounds bounds = ((Renderer)body).bounds;
					return ((Bounds)(ref bounds)).center;
				}
			}
			catch
			{
			}
			return ((Object)(object)h != (Object)null) ? ((Component)h).transform.position : fallback;
		}

		private static void Explode(Vector3 pos, int id)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			if (!GameExplosion(pos + Vector3.up * 0.5f))
			{
				OwnBlast(pos, id);
			}
		}

		private static IEnumerator BoomWhenSettled(NPCHealth h, Vector3 hitPos)
		{
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			int id = ((Object)h).GetInstanceID();
			Vector3 last = hitPos;
			float elapsed = 0f;
			float sample = 0f;
			while (elapsed < 1f)
			{
				yield return null;
				elapsed += Time.deltaTime;
				sample += Time.deltaTime;
				if ((Object)(object)h == (Object)null)
				{
					break;
				}
				if (!(sample < 0.15f))
				{
					sample = 0f;
					Vector3 now = BodyPos(h, last);
					bool settled = Vector3.Distance(now, last) < 0.1f;
					last = now;
					if (elapsed >= 0.25f && settled)
					{
						break;
					}
				}
			}
			Explode(last, id);
		}

		private static Vector3 BodyPos(NPCHealth h, Vector3 fallback)
		{
			//IL_0060: 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_0065: 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)
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_0069: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				NPC componentInParent = ((Component)h).GetComponentInParent<NPC>();
				Transform val = (((Object)(object)componentInParent != (Object)null) ? ((Component)componentInParent).transform : ((Component)h).transform);
				SkinnedMeshRenderer componentInChildren = ((Component)val).GetComponentInChildren<SkinnedMeshRenderer>();
				if ((Object)(object)componentInChildren != (Object)null)
				{
					Bounds bounds = ((Renderer)componentInChildren).bounds;
					return ((Bounds)(ref bounds)).center;
				}
			}
			catch
			{
			}
			return ((Object)(object)h != (Object)null) ? ((Component)h).transform.position : fallback;
		}

		private static bool GameExplosion(Vector3 pos)
		{
			//IL_006b: 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)
			try
			{
				if ((Object)(object)combat == (Object)null)
				{
					combat = Object.FindObjectOfType<CombatManager>();
				}
				if ((Object)(object)combat == (Object)null)
				{
					MelonLogger.Warning("[Madman] CombatManager not found");
					return false;
				}
				if (dataObj == null)
				{
					dataObj = BuildData();
				}
				MadmanState.InBlast = true;
				MelonCoroutines.Start(ClearGuardLater(0.6f));
				combat.CreateExplosion(pos, (ExplosionData)dataObj);
				return true;
			}
			catch (Exception ex)
			{
				MelonLogger.Warning("[Madman] CreateExplosion failed: " + ex.Message);
				return false;
			}
		}

		private static IEnumerator ClearGuardLater(float seconds)
		{
			float t = 0f;
			while (t < seconds)
			{
				t += Time.deltaTime;
				yield return null;
			}
			MadmanState.InBlast = false;
		}

		private static object BuildData()
		{
			//IL_0143: Unknown result type (might be due to invalid IL or missing references)
			//IL_0149: Unknown result type (might be due to invalid IL or missing references)
			Type typeFromHandle = typeof(ExplosionData);
			MemberInfo[] members = typeFromHandle.GetMembers(BindingFlags.Static | BindingFlags.Public);
			foreach (MemberInfo memberInfo in members)
			{
				object obj = null;
				try
				{
					if (memberInfo is FieldInfo fieldInfo && fieldInfo.FieldType == typeFromHandle)
					{
						obj = fieldInfo.GetValue(null);
					}
					else if (memberInfo is PropertyInfo propertyInfo && propertyInfo.PropertyType == typeFromHandle && propertyInfo.GetIndexParameters().Length == 0)
					{
						obj = propertyInfo.GetValue(null);
					}
				}
				catch
				{
				}
				if (obj != null)
				{
					MelonLogger.Msg("[Madman] using ExplosionData preset '" + memberInfo.Name + "'");
					return obj;
				}
			}
			MelonLogger.Msg("[Madman] ExplosionData members: " + string.Join(", ", from m in typeFromHandle.GetMembers(BindingFlags.Instance | BindingFlags.Public)
				where m is FieldInfo || m is PropertyInfo
				select m.Name + ":" + ((m as FieldInfo)?.FieldType ?? (m as PropertyInfo).PropertyType).Name));
			object obj3 = (object)default(ExplosionData);
			SetNum(obj3, "DamageRadius", 6f);
			SetNum(obj3, "MaxDamage", 200f);
			SetNum(obj3, "PushForceRadius", 8f);
			SetNum(obj3, "MaxPushForce", 15f);
			return obj3;
		}

		private static void SetNum(object boxed, string name, float value)
		{
			Type type = boxed.GetType();
			FieldInfo field = type.GetField(name, BindingFlags.Instance | BindingFlags.Public);
			if (field != null)
			{
				field.SetValue(boxed, Convert.ChangeType(value, field.FieldType));
				return;
			}
			PropertyInfo property = type.GetProperty(name, BindingFlags.Instance | BindingFlags.Public);
			if (property != null && property.CanWrite)
			{
				property.SetValue(boxed, Convert.ChangeType(value, property.PropertyType));
			}
			else
			{
				MelonLogger.Warning("[Madman] ExplosionData has no '" + name + "'");
			}
		}

		private static void OwnBlast(Vector3 pos, int skipNpcId)
		{
			//IL_0001: 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_00b0: Unknown result type (might be due to invalid IL or missing references)
			SpawnVisual(pos);
			MadmanState.InBlast = true;
			try
			{
				HashSet<int> hashSet = new HashSet<int> { skipNpcId };
				foreach (Collider item in (Il2CppArrayBase<Collider>)(object)Physics.OverlapSphere(pos, 4f))
				{
					if (!((Object)(object)((Component)item).GetComponentInParent<Player>() != (Object)null))
					{
						NPCHealth componentInParent = ((Component)item).GetComponentInParent<NPCHealth>();
						if ((Object)(object)componentInParent != (Object)null && hashSet.Add(((Object)componentInParent).GetInstanceID()) && !Reflect.Flag(componentInParent, "IsDead"))
						{
							bool flag = true;
							MadmanCombatPatch.Kill(componentInParent);
						}
						Rigidbody attachedRigidbody = item.attachedRigidbody;
						if ((Object)(object)attachedRigidbody != (Object)null)
						{
							attachedRigidbody.AddExplosionForce(600f, pos, 4f, 0.5f);
						}
					}
				}
			}
			catch (Exception ex)
			{
				MelonLogger.Warning("[Madman] blast failed: " + ex.Message);
			}
			finally
			{
				MadmanState.InBlast = false;
			}
		}

		private static void SpawnVisual(Vector3 pos)
		{
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0066: Expected O, but got Unknown
			//IL_0081: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				GameObject val = GameObject.CreatePrimitive((PrimitiveType)0);
				Object.Destroy((Object)(object)val.GetComponent<Collider>());
				val.transform.position = pos;
				Shader val2 = Shader.Find("Unlit/Color") ?? Shader.Find("Sprites/Default");
				Renderer component = val.GetComponent<Renderer>();
				if ((Object)(object)val2 != (Object)null && (Object)(object)component != (Object)null)
				{
					component.material = new Material(val2);
					component.material.color = new Color(1f, 0.5f, 0.1f, 1f);
				}
				MelonCoroutines.Start(Expand(val));
			}
			catch (Exception ex)
			{
				MelonLogger.Warning("[Madman] blast visual failed: " + ex.Message);
			}
		}

		public static void Reset()
		{
			combat = null;
			lastAny = 0f;
			lastNpc.Clear();
		}

		private static IEnumerator Expand(GameObject go)
		{
			float t = 0f;
			while (t < 0.35f)
			{
				if ((Object)(object)go == (Object)null)
				{
					yield break;
				}
				t += Time.deltaTime;
				go.transform.localScale = Vector3.one * Mathf.Lerp(0.5f, 8f, t / 0.35f);
				yield return null;
			}
			if ((Object)(object)go != (Object)null)
			{
				Object.Destroy((Object)(object)go);
			}
		}
	}
	internal static class InfiniteAmmo
	{
		private static readonly string[] CapacityNames = new string[6] { "MagazineSize", "magazineSize", "MaxAmmo", "maxAmmo", "AmmoCapacity", "Capacity" };

		private static readonly List<Equippable_RangedWeapon> weapons = new List<Equippable_RangedWeapon>();

		private static readonly Dictionary<string, int> capCache = new Dictionary<string, int>();

		private static readonly Dictionary<string, int> best = new Dictionary<string, int>();

		private const float RefillInterval = 0.1f;

		private const float ScanInterval = 2f;

		private static float nextRefill;

		private static float nextScan;

		private static bool logged;

		private static bool warned;

		private static IntPtr fieldPtr;

		private static bool loggedItemPath;

		public static void Tick()
		{
			if (!MadmanState.Enabled || Time.time < nextRefill)
			{
				return;
			}
			nextRefill = Time.time + 0.1f;
			try
			{
				if (Time.time >= nextScan)
				{
					nextScan = Time.time + 2f;
					weapons.Clear();
					foreach (Equippable_RangedWeapon item in Object.FindObjectsOfType<Equippable_RangedWeapon>())
					{
						weapons.Add(item);
					}
				}
				for (int num = weapons.Count - 1; num >= 0; num--)
				{
					Equippable_RangedWeapon val = weapons[num];
					if ((Object)(object)val == (Object)null)
					{
						weapons.RemoveAt(num);
					}
					else
					{
						if (!logged)
						{
							logged = true;
							LogMembers(val);
						}
						int num2 = Capacity(val);
						int ammo = val.Ammo;
						if (num2 <= 0)
						{
							best.TryGetValue(((Object)val).name, out var value);
							value = Math.Max(value, ammo);
							best[((Object)val).name] = value;
							num2 = Math.Max(value, 8);
						}
						if (ammo < num2 && !SetAmmo(val, num2) && !warned)
						{
							warned = true;
							MelonLogger.Warning("[Madman] could not write Ammo");
						}
					}
				}
			}
			catch (Exception ex)
			{
				MelonLogger.Warning("[Madman] infinite ammo failed: " + ex.Message);
				nextRefill = Time.time + 5f;
			}
		}

		private static int Capacity(Equippable_RangedWeapon w)
		{
			string name = ((Object)w).name;
			if (capCache.TryGetValue(name, out var value))
			{
				return value;
			}
			string[] capacityNames = CapacityNames;
			foreach (string name2 in capacityNames)
			{
				if (Reflect.Get(w, name2) is int num && num > 0)
				{
					capCache[name] = num;
					return num;
				}
			}
			capCache[name] = 0;
			return 0;
		}

		private static bool SetAmmo(Equippable_RangedWeapon w, int value)
		{
			MethodInfo methodInfo = typeof(Equippable_RangedWeapon).GetProperty("Ammo", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetSetMethod(nonPublic: true);
			if (methodInfo != null)
			{
				methodInfo.Invoke(w, new object[1] { value });
				return true;
			}
			if (SetViaItemInstance(w, value))
			{
				return true;
			}
			if (fieldPtr == IntPtr.Zero)
			{
				fieldPtr = FindAmmoField();
			}
			if (fieldPtr == IntPtr.Zero)
			{
				return false;
			}
			IntPtr ptr = IL2CPP.Il2CppObjectBaseToPtrNotNull((Il2CppObjectBase)(object)w);
			int ofs = (int)IL2CPP.il2cpp_field_get_offset(fieldPtr);
			Marshal.WriteInt32(ptr, ofs, value);
			return true;
		}

		private static bool SetViaItemInstance(Equippable_RangedWeapon w, int value)
		{
			string[] array = new string[3] { "itemInstance", "ItemInstance", "instance" };
			foreach (string text in array)
			{
				object obj = Reflect.Get(w, text);
				Il2CppObjectBase val = (Il2CppObjectBase)((obj is Il2CppObjectBase) ? obj : null);
				if (val == null)
				{
					continue;
				}
				IntegerItemInstance val2 = val.TryCast<IntegerItemInstance>();
				if (val2 != null)
				{
					if (!loggedItemPath)
					{
						loggedItemPath = true;
						MelonLogger.Msg("[Madman] ammo via item instance '" + text + "'");
					}
					if (Reflect.Invoke(val2, "SetValue", value))
					{
						return true;
					}
					PropertyInfo property = ((object)val2).GetType().GetProperty("Value", BindingFlags.Instance | BindingFlags.Public);
					if (property != null && property.CanWrite)
					{
						property.SetValue(val2, value);
						return true;
					}
				}
			}
			return false;
		}

		private static IntPtr FindAmmoField()
		{
			FieldInfo[] fields = typeof(Equippable_RangedWeapon).GetFields(BindingFlags.DeclaredOnly | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
			foreach (FieldInfo fieldInfo in fields)
			{
				if (fieldInfo.Name.StartsWith("NativeFieldInfoPtr") && fieldInfo.Name.IndexOf("Ammo", StringComparison.OrdinalIgnoreCase) >= 0 && fieldInfo.GetValue(null) is IntPtr intPtr && intPtr != IntPtr.Zero)
				{
					MelonLogger.Msg("[Madman] ammo field: " + fieldInfo.Name);
					return intPtr;
				}
			}
			return IntPtr.Zero;
		}

		private static void LogMembers(object w)
		{
			IEnumerable<string> values = from m in w.GetType().GetMembers(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
				where (m is PropertyInfo || m is FieldInfo) && !m.Name.StartsWith("NativeFieldInfoPtr")
				select m.Name + ":" + ((m as FieldInfo)?.FieldType ?? (m as PropertyInfo).PropertyType).Name;
			MelonLogger.Msg("[Madman] Equippable_RangedWeapon members: " + string.Join(", ", values));
		}
	}
	internal static class MadmanCombatPatch
	{
		public static bool Busy;

		public static float? CurrentHealth(NPCHealth h)
		{
			string[] array = new string[3] { "Health", "CurrentHealth", "currentHealth" };
			foreach (string name in array)
			{
				if (Reflect.Get(h, name) is float value)
				{
					return value;
				}
			}
			return null;
		}

		public static bool Alive(NPCHealth h)
		{
			if (Reflect.Flag(h, "IsDead"))
			{
				return false;
			}
			float? num = CurrentHealth(h);
			return !num.HasValue || !(num.Value <= 0f);
		}

		public static void Kill(NPCHealth h)
		{
			Busy = true;
			try
			{
				if (Reflect.TryInvoke(h, "Die"))
				{
					WantedLevel.Raise();
				}
			}
			finally
			{
				Busy = false;
			}
		}

		public static bool KnockOut(NPCHealth h)
		{
			Busy = true;
			try
			{
				return Reflect.TryInvoke(h, "KnockOut", "Knockout");
			}
			finally
			{
				Busy = false;
			}
		}
	}
	internal static class PlayerAttack
	{
		private const bool RequireRecentAttack = false;

		private static float last;

		public static bool Allowed => true;

		public static void Tick()
		{
			if (Input.GetMouseButton(0))
			{
				last = Time.time;
			}
		}
	}
	[HarmonyPatch]
	internal static class MadmanGunPatch
	{
		private static MethodBase target;

		private static bool Prepare()
		{
			target = Reflect.Find(typeof(NPCHealth), "TakeDamage");
			if (target == null)
			{
				MelonLogger.Warning("[Madman] NPCHealth.TakeDamage not found, gun rule disabled");
			}
			return target != null;
		}

		private static MethodBase TargetMethod()
		{
			return target;
		}

		private static bool Prefix(object __instance)
		{
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			if (!MadmanState.Enabled || MadmanState.InBlast || MadmanCombatPatch.Busy)
			{
				return true;
			}
			NPCHealth val = (NPCHealth)((__instance is NPCHealth) ? __instance : null);
			if ((Object)(object)val == (Object)null || !PlayerAttack.Allowed)
			{
				return true;
			}
			Vector3 position = ((Component)val).transform.position;
			ExplosionFx.Boom(position, val);
			if (!MadmanCombatPatch.Alive(val))
			{
				return true;
			}
			MeleeTracker.Forget(((Object)val).GetInstanceID());
			MadmanCombatPatch.Kill(val);
			return false;
		}
	}
	[HarmonyPatch]
	internal static class MadmanMeleePatch
	{
		private static MethodBase target;

		private static readonly Dictionary<int, float> lastEvent = new Dictionary<int, float>();

		private static bool Prepare()
		{
			target = typeof(NPCHealth).GetMethods(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).FirstOrDefault((MethodInfo m) => m.Name.Contains("set_value") && m.Name.Contains("currentHealth"));
			if (target == null)
			{
				MelonLogger.Warning("[Madman] currentHealth setter not found, melee rule disabled");
			}
			return target != null;
		}

		private static MethodBase TargetMethod()
		{
			return target;
		}

		private static bool Prefix(object __instance, object[] __args)
		{
			//IL_0136: 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_013d: Unknown result type (might be due to invalid IL or missing references)
			if (!MadmanState.Enabled || MadmanState.InBlast || MadmanCombatPatch.Busy)
			{
				return true;
			}
			NPCHealth val = (NPCHealth)((__instance is NPCHealth) ? __instance : null);
			if ((Object)(object)val == (Object)null || __args == null || __args.Length < 2)
			{
				return true;
			}
			if (!(__args[0] is float num) || !(__args[1] is bool flag) || !flag)
			{
				return true;
			}
			float? num2 = MadmanCombatPatch.CurrentHealth(val);
			if (!num2.HasValue || num2.Value <= 0f || num >= num2.Value - 0.01f)
			{
				return true;
			}
			if (!PlayerAttack.Allowed)
			{
				return true;
			}
			int instanceID = ((Object)val).GetInstanceID();
			float time = Time.time;
			if (lastEvent.TryGetValue(instanceID, out var value) && time - value < 0.15f)
			{
				return false;
			}
			lastEvent[instanceID] = time;
			Vector3 position = ((Component)val).transform.position;
			ExplosionFx.Boom(position, val);
			if (MeleeTracker.RegisterHit(instanceID) >= 2)
			{
				MeleeTracker.Forget(instanceID);
				MadmanCombatPatch.Kill(val);
			}
			else if (!MadmanCombatPatch.KnockOut(val))
			{
				__args[0] = 0f;
				return true;
			}
			return false;
		}
	}
	public class MadmanCommand : BaseConsoleCommand
	{
		public override string CommandWord => "madman";

		public override string CommandDescription => "Toggle madman mode. Optional: madman on / madman off";

		public override string ExampleUsage => "madman";

		public override void ExecuteCommand(List<string> args)
		{
			if (args != null && args.Count > 0)
			{
				string text = args[0].ToLowerInvariant();
				MadmanState.Set(text == "on" || text == "1" || text == "true");
			}
			else
			{
				MadmanState.Toggle();
			}
		}
	}
	[HarmonyPatch]
	internal static class MadmanFusePatch
	{
		private static readonly List<MethodBase> targets = new List<MethodBase>();

		private static bool Prepare()
		{
			Add(typeof(Explosive), "ApplyToNPC");
			Add(typeof(Explosive), "ApplyToPlayer");
			Add(typeof(CountdownExplosion), "Trigger");
			return targets.Count > 0;
		}

		private static void Add(Type t, string name)
		{
			MethodBase methodBase = Reflect.Find(t, name);
			if (methodBase != null)
			{
				targets.Add(methodBase);
				return;
			}
			MelonLogger.Warning($"[Madman] {t.Name}.{name} not found, fuse block incomplete");
		}

		private static IEnumerable<MethodBase> TargetMethods()
		{
			return targets;
		}

		private static bool Prefix(MethodBase __originalMethod)
		{
			if (!MadmanState.Enabled)
			{
				return true;
			}
			MelonLogger.Msg("[Madman] blocked " + __originalMethod.DeclaringType.Name + "." + __originalMethod.Name);
			return false;
		}
	}
	[HarmonyPatch]
	internal static class MadmanGodmodePatch
	{
		private static MethodBase target;

		private static bool Prepare()
		{
			target = Reflect.Find(typeof(PlayerHealth), "TakeDamage");
			if (target == null)
			{
				MelonLogger.Warning("[Madman] PlayerHealth.TakeDamage not found, godmode disabled");
			}
			return target != null;
		}

		private static MethodBase TargetMethod()
		{
			return target;
		}

		private static bool Prefix()
		{
			return !MadmanState.Enabled;
		}
	}
	public class MadmanScanCommand : BaseConsoleCommand
	{
		public override string CommandWord => "madmanscan";

		public override string CommandDescription => "Logs game types/members matching words (read-only). e.g. madmanscan ammo";

		public override string ExampleUsage => "madmanscan ammo reload";

		public override void ExecuteCommand(List<string> args)
		{
			string[] words = ((args != null && args.Count > 0) ? args.Select((string a) => a.ToLowerInvariant()).ToArray() : new string[3] { "explo", "blast", "detonat" });
			Assembly assembly = AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault((Assembly a) => a.GetName().Name == "Assembly-CSharp");
			if (assembly == null)
			{
				MelonLogger.Warning("[Scan] Assembly-CSharp not found");
				return;
			}
			Type[] array;
			try
			{
				array = assembly.GetTypes();
			}
			catch (ReflectionTypeLoadException ex)
			{
				array = ex.Types.Where((Type t) => t != null).ToArray();
			}
			int num = 0;
			Type[] array2 = array;
			foreach (Type type in array2)
			{
				if (!(type.Namespace ?? "").StartsWith("Il2CppScheduleOne"))
				{
					continue;
				}
				MemberInfo[] members;
				try
				{
					members = type.GetMembers(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
				}
				catch
				{
					continue;
				}
				List<MemberInfo> source = members.Where((MemberInfo m) => !m.Name.StartsWith("NativeFieldInfoPtr") && !m.Name.StartsWith("NativeMethodInfoPtr") && !m.Name.StartsWith("get_") && !m.Name.StartsWith("set_") && (m is MethodInfo || m is PropertyInfo || m is FieldInfo)).ToList();
				bool flag = Hit(type.Name);
				List<MemberInfo> list = (flag ? source.Take(30).ToList() : source.Where((MemberInfo m) => Hit(m.Name)).ToList());
				if (list.Count == 0)
				{
					continue;
				}
				MelonLogger.Msg($"[Scan] {type.Namespace}.{type.Name}{(flag ? "  (type match)" : "")}");
				foreach (MemberInfo item in list)
				{
					string text = ((item is MethodInfo methodInfo) ? ("(" + string.Join(", ", from p in methodInfo.GetParameters()
						select p.ParameterType.Name) + ")") : ((item is PropertyInfo propertyInfo) ? (" : " + propertyInfo.PropertyType.Name) : (" : " + ((FieldInfo)item).FieldType.Name)));
					MelonLogger.Msg("[Scan]     " + item.Name + text);
					if (++num >= 150)
					{
						MelonLogger.Msg("[Scan] stopped at 150 results");
						return;
					}
				}
			}
			MelonLogger.Msg($"[Scan] done, {num} members");
			bool Hit(string s)
			{
				return words.Any((string w) => s.IndexOf(w, StringComparison.OrdinalIgnoreCase) >= 0);
			}
		}
	}
	public static class MadmanState
	{
		public static bool InBlast;

		public const float ComboWindow = 4f;

		public const float BlastRadius = 4f;

		public const float BlastForce = 600f;

		public const bool BlastKills = true;

		private static float noPlayerSince;

		public static bool Enabled { get; private set; }

		public static void Set(bool enable)
		{
			if (enable && (Object)(object)Player.Local == (Object)null)
			{
				MelonLogger.Warning("[Madman] load a save first");
				return;
			}
			Enabled = enable;
			MeleeTracker.Clear();
			PlayerEffects.Apply(enable);
			MelonLogger.Msg("[Madman] " + (enable ? "ON" : "OFF"));
		}

		public static void Toggle()
		{
			Set(!Enabled);
		}

		public static void Reset()
		{
			Enabled = false;
			InBlast = false;
			MeleeTracker.Clear();
			PlayerEffects.Reset();
			ExplosionFx.Reset();
			MelonLogger.Msg("[Madman] reset");
		}

		public static void Tick()
		{
			if (!Enabled || (Object)(object)Player.Local != (Object)null)
			{
				noPlayerSince = 0f;
			}
			else if (noPlayerSince == 0f)
			{
				noPlayerSince = Time.time;
			}
			else if (Time.time - noPlayerSince > 2f)
			{
				noPlayerSince = 0f;
				Reset();
			}
		}
	}
	public static class MeleeTracker
	{
		private static readonly Dictionary<int, int> counts = new Dictionary<int, int>();

		private static readonly Dictionary<int, float> lastHit = new Dictionary<int, float>();

		public static int RegisterHit(int id)
		{
			float time = Time.time;
			if (!lastHit.TryGetValue(id, out var value) || time - value > 4f)
			{
				counts[id] = 0;
			}
			counts[id] = ((!counts.TryGetValue(id, out var value2)) ? 1 : (value2 + 1));
			lastHit[id] = time;
			return counts[id];
		}

		public static void Forget(int id)
		{
			counts.Remove(id);
			lastHit.Remove(id);
		}

		public static void Clear()
		{
			counts.Clear();
			lastHit.Clear();
		}
	}
	internal static class PlayerEffects
	{
		private static readonly Type[] Wanted = new Type[4]
		{
			typeof(Sneaky),
			typeof(Energizing),
			typeof(AntiGravity),
			typeof(Athletic)
		};

		private static readonly Dictionary<Type, object> cache = new Dictionary<Type, object>();

		private const float RefreshSeconds = 1f;

		private static bool active;

		private static float next;

		public static void Apply(bool on)
		{
			active = on;
			next = 0f;
			Player local = Player.Local;
			if ((Object)(object)local == (Object)null)
			{
				MelonLogger.Warning("[Madman] no local player yet, effects will apply once one exists");
				return;
			}
			Type[] wanted = Wanted;
			foreach (Type type in wanted)
			{
				object obj = Get(type);
				if (obj == null)
				{
					MelonLogger.Warning("[Madman] could not get " + type.Name);
					continue;
				}
				bool flag = (on ? Reflect.Invoke(obj, "ApplyToPlayer", local) : Reflect.Invoke(obj, "ClearFromPlayer", local));
				MelonLogger.Msg($"[Madman] {type.Name} {(on ? "apply" : "clear")}: {(flag ? "ok" : "method not found")}");
			}
		}

		public static void Reset()
		{
			active = false;
			next = 0f;
			cache.Clear();
		}

		public static void Tick()
		{
			if (!active || Time.time < next)
			{
				return;
			}
			next = Time.time + 1f;
			Player local = Player.Local;
			if ((Object)(object)local == (Object)null)
			{
				return;
			}
			Type[] wanted = Wanted;
			foreach (Type t in wanted)
			{
				object obj = Get(t);
				if (obj != null)
				{
					Reflect.Invoke(obj, "ClearFromPlayer", local);
					Reflect.Invoke(obj, "ApplyToPlayer", local);
				}
			}
		}

		private static object Get(Type t)
		{
			if (cache.TryGetValue(t, out object value) && value != null)
			{
				return value;
			}
			value = Create(t);
			if (value != null)
			{
				cache[t] = value;
			}
			return value;
		}

		private static object Create(Type t)
		{
			try
			{
				Il2CppReferenceArray<Object> val = Resources.FindObjectsOfTypeAll(Il2CppType.From(t));
				if (val != null && ((Il2CppArrayBase<Object>)(object)val).Length > 0)
				{
					return Activator.CreateInstance(t, ((Il2CppObjectBase)((Il2CppArrayBase<Object>)(object)val)[0]).Pointer);
				}
			}
			catch
			{
			}
			try
			{
				return Activator.CreateInstance(t);
			}
			catch (Exception ex)
			{
				MelonLogger.Warning("[Madman] " + t.Name + " instance failed: " + ex.Message);
				return null;
			}
		}
	}
	internal static class Reflect
	{
		private const BindingFlags All = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy;

		public static MethodBase Find(Type t, string name)
		{
			return (from m in t.GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy)
				where m.Name == name
				orderby m.GetParameters().Length
				select m).FirstOrDefault();
		}

		public static object Get(object o, string name)
		{
			if (o == null)
			{
				return null;
			}
			Type type = o.GetType();
			PropertyInfo property = type.GetProperty(name, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy);
			if (property != null)
			{
				try
				{
					return property.GetValue(o);
				}
				catch
				{
				}
			}
			FieldInfo field = type.GetField(name, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy);
			if (field != null)
			{
				try
				{
					return field.GetValue(o);
				}
				catch
				{
				}
			}
			return null;
		}

		public static bool Flag(object o, string name)
		{
			object obj = Get(o, name);
			bool flag = default(bool);
			int num;
			if (obj is bool)
			{
				flag = (bool)obj;
				num = 1;
			}
			else
			{
				num = 0;
			}
			return (byte)((uint)num & (flag ? 1u : 0u)) != 0;
		}

		public static bool TryInvoke(object o, params string[] names)
		{
			foreach (string text in names)
			{
				MethodBase methodBase = Find(o.GetType(), text);
				if (methodBase == null)
				{
					continue;
				}
				try
				{
					object[] parameters = (from p in methodBase.GetParameters()
						select p.ParameterType.IsValueType ? Activator.CreateInstance(p.ParameterType) : null).ToArray();
					methodBase.Invoke(o, parameters);
					return true;
				}
				catch (Exception ex)
				{
					MelonLogger.Warning("[Madman] " + text + " failed: " + ex.Message);
				}
			}
			MelonLogger.Warning("[Madman] none of [" + string.Join(", ", names) + "] found on " + o.GetType().Name);
			return false;
		}

		public static bool Invoke(object o, string name, params object[] args)
		{
			MethodInfo methodInfo = o.GetType().GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy).FirstOrDefault((MethodInfo x) => x.Name == name && x.GetParameters().Length == args.Length);
			if (methodInfo == null)
			{
				return false;
			}
			try
			{
				methodInfo.Invoke(o, args);
				return true;
			}
			catch (Exception ex)
			{
				MelonLogger.Warning("[Madman] " + name + " threw: " + ex.Message);
				return false;
			}
		}
	}
	internal static class WantedLevel
	{
		private const string LevelName = "Lethal";

		private static float last;

		public static void Raise()
		{
			if (Time.time - last < 1f)
			{
				return;
			}
			last = Time.time;
			try
			{
				Player local = Player.Local;
				object obj = (((Object)(object)local == (Object)null) ? null : Reflect.Get(local, "CrimeData"));
				if (obj == null)
				{
					MelonLogger.Warning("[Madman] no CrimeData on the player");
					return;
				}
				MethodInfo methodInfo = obj.GetType().GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).FirstOrDefault((MethodInfo x) => x.Name == "SetPursuitLevel");
				if (methodInfo == null)
				{
					MelonLogger.Warning("[Madman] SetPursuitLevel not found on CrimeData");
					return;
				}
				ParameterInfo[] parameters = methodInfo.GetParameters();
				if (parameters.Length == 0 || !parameters[0].ParameterType.IsEnum)
				{
					MelonLogger.Warning("[Madman] unexpected SetPursuitLevel signature");
					return;
				}
				object level;
				try
				{
					level = Enum.Parse(parameters[0].ParameterType, "Lethal");
				}
				catch
				{
					MelonLogger.Warning("[Madman] no level 'Lethal'. Options: " + string.Join(", ", Enum.GetNames(parameters[0].ParameterType)));
					return;
				}
				object[] parameters2 = parameters.Select((ParameterInfo p, int i) => (i == 0) ? level : (p.ParameterType.IsValueType ? Activator.CreateInstance(p.ParameterType) : null)).ToArray();
				methodInfo.Invoke(obj, parameters2);
				Reflect.Invoke(obj, "RecordLastKnownPosition", true);
			}
			catch (Exception ex)
			{
				MelonLogger.Warning("[Madman] wanted level failed: " + ex.Message);
			}
		}
	}
}