Decompiled source of SnailMaster v0.5.0

plugins/com.github.Kirshoo.SnailMaster.dll

Decompiled a month ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using SnailMaster.Patches;
using UnityEngine;
using UnityEngine.Audio;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: IgnoresAccessChecksTo("Assembly-CSharp")]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("com.github.Kirshoo.SnailMaster")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("0.5.0.0")]
[assembly: AssemblyInformationalVersion("0.5.0")]
[assembly: AssemblyProduct("com.github.Kirshoo.SnailMaster")]
[assembly: AssemblyTitle("SnailMaster")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("0.5.0.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
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;
		}
	}
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.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 BepInEx
{
	[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)]
	[Conditional("CodeGeneration")]
	internal sealed class BepInAutoPluginAttribute : Attribute
	{
		public BepInAutoPluginAttribute(string? id = null, string? name = null, string? version = null)
		{
		}
	}
}
namespace BepInEx.Preloader.Core.Patching
{
	[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)]
	[Conditional("CodeGeneration")]
	internal sealed class PatcherAutoPluginAttribute : Attribute
	{
		public PatcherAutoPluginAttribute(string? id = null, string? name = null, string? version = null)
		{
		}
	}
}
namespace SnailMaster
{
	[BepInPlugin("com.github.Kirshoo.SnailMaster", "SnailMaster", "0.5.0")]
	public class Plugin : BaseUnityPlugin
	{
		public const string Id = "com.github.Kirshoo.SnailMaster";

		internal static ManualLogSource Logger { get; private set; }

		public static string Name => "SnailMaster";

		public static string Version => "0.5.0";

		private void Awake()
		{
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Expected O, but got Unknown
			Logger = ((BaseUnityPlugin)this).Logger;
			BindConfig();
			Logger.LogInfo((object)("Plugin " + Name + " is loaded!"));
			Harmony val = new Harmony("com.github.Kirshoo.SnailMaster");
			val.PatchAll(typeof(ScoutMasterPatch));
			val.PatchAll(typeof(RunStartPatch));
			val.PatchAll(typeof(PickUpItemPatch));
			val.PatchAll(typeof(SnailMasterMovementPatch));
			Logger.LogInfo((object)"All patches were successful!");
		}

		private void BindConfig()
		{
			SnailMasterConfig.BindConfig(((BaseUnityPlugin)this).Config);
		}
	}
	internal abstract class Singleton<T> where T : class, new()
	{
		private static T _instance = null;

		private static readonly object _lock = new object();

		public static T Instance
		{
			get
			{
				if (_instance == null)
				{
					lock (_lock)
					{
						_instance = new T();
					}
				}
				return _instance;
			}
		}
	}
	public enum PlayerChaseSelection
	{
		Highest,
		Closest,
		Random
	}
	public enum TeleportModeSelection
	{
		Default,
		OnTargetSwitch,
		Never
	}
	internal class SnailMasterConfig
	{
		public static ConfigEntry<float> ClimbSpeed;

		public static ConfigEntry<float> WalkSpeed;

		public static ConfigEntry<PlayerChaseSelection> ChaseMode;

		public static ConfigEntry<float> RetargetCooldown;

		public static ConfigEntry<float> GraceTime;

		public static ConfigEntry<TeleportModeSelection> TeleportMode;

		public static ConfigEntry<float> TeleportDistance;

		public static ConfigEntry<float> SpawnMinDistance;

		public static ConfigEntry<float> SpawnMaxDistance;

		public static ConfigEntry<float> SpawnMaxHeightDifference;

		public static void BindConfig(ConfigFile configfile)
		{
			WalkSpeed = configfile.Bind<float>("SnailMaster", "WalkSpeed", 1f, "Speed at which snailmaster will walk towards players.");
			ClimbSpeed = configfile.Bind<float>("SnailMaster", "ClimbSpeed", 1f, "Speed at which snailmaster will climb the walls and obstacles.");
			ChaseMode = configfile.Bind<PlayerChaseSelection>("SnailMaster", "PlayerToChase", PlayerChaseSelection.Random, "Determines which player will be targeted by the snailmaster.\r\nAnyone but the target will be ignored by the snailmaster.\r\nHighest - player that is nearest to the peak will become the target of a snailmaster.\r\nClosest - player closest to the snailmaster.\r\nRandom - completely random player will become the target.");
			RetargetCooldown = configfile.Bind<float>("SnailMaster", "TargetCooldown", 30f, "Time in seconds how often will the snailmaster switch targets.\r\nMinimum is 5 seconds for optimization reasons.");
			if (RetargetCooldown.Value < 5f)
			{
				RetargetCooldown.Value = 5f;
			}
			GraceTime = configfile.Bind<float>("SnailMaster", "GraceTime", 30f, "Time after the snailmaster is spawned and before he starts chasing players.");
			TeleportMode = configfile.Bind<TeleportModeSelection>("SnailMaster", "TeleportMode", TeleportModeSelection.Default, "Determines the mode at which SnailMaster's teleportation operates.\r\nDefault - teleports if distance to target exceeds the value of TeleportDistance.\r\nOnTargetSwitch - teleports only to the target the exceeds the value of TeleportDistance when switching targets, otherwise wont teleport.\r\nNever - completely shuts down the teleportation mechanic.");
			TeleportDistance = configfile.Bind<float>("SnailMaster", "TeleportDistance", 80f * CharacterStats.unitsToMeters, "If distance to target exceeds this value, forces SnailMaster to teleport closer.\r\nValue is in in-game meters.\r\nIf TeleportMode is set to 'Never', this setting is ignored.");
			SpawnMinDistance = configfile.Bind<float>("SnailMaster", "MinSpawnDistance", 50f * CharacterStats.unitsToMeters, "Minimum distance away from the target snailmaster is allowed to spawn/teleport to.\r\nRange is from 0 - infinity.\r\nIf set to 0, SM will be allowed to teleport right on top of the player.");
			if (SpawnMinDistance.Value < 0f)
			{
				SpawnMinDistance.Value = 0f;
			}
			SpawnMaxDistance = configfile.Bind<float>("SnailMaster", "MaxSpawnDistance", 70f * CharacterStats.unitsToMeters, "Maximum distance away from the target snailmaster is allowed to spawn/teleport to.\r\nRange is from MinSpawnDistance - infinity.");
			if (SpawnMaxDistance.Value < SpawnMinDistance.Value)
			{
				SpawnMaxDistance.Value = SpawnMinDistance.Value;
			}
			SpawnMaxHeightDifference = configfile.Bind<float>("SnailMaster", "MaxSpawnHeightDifference", 15f * CharacterStats.unitsToMeters, "Denotes how how high or below the target snailmaster is allowed to spawn/teleport.\r\nRange is 1 - infinity.\r\nRecommended to keep within 20-30 meter range, otherwise SM might not spawn at all.");
			if (SpawnMaxHeightDifference.Value < 1f)
			{
				SpawnMaxHeightDifference.Value = 1f;
			}
		}
	}
	internal class SnailMasterManager : Singleton<SnailMasterManager>
	{
		private bool _snailmode;

		public void EnableSnailMode()
		{
			if (!IsSnailActivated())
			{
				Scoutmaster val = default(Scoutmaster);
				if (Scoutmaster.GetPrimaryScoutmaster(ref val))
				{
					_snailmode = true;
					ScoutMasterPatch.AssignNewTarget(val);
					val.chillForSeconds = SnailMasterConfig.GraceTime.Value;
					val.TeleportCloseToTarget();
				}
				Plugin.Logger.LogDebug((object)$"Attempting to enable snail mode. IsSuccessful? {_snailmode}");
			}
		}

		public void DisableSnailMode()
		{
			Plugin.Logger.LogDebug((object)"Disabling snail mode.");
			_snailmode = false;
		}

		public bool IsSnailActivated()
		{
			return _snailmode;
		}
	}
}
namespace SnailMaster.Patches
{
	internal class PickUpItemPatch
	{
		private const ushort CONCH_ITEM_ID = 69;

		private const string SummonInteract = "INTERACT_SUMMON";

		[HarmonyPatch(typeof(Item), "GetItemActions")]
		[HarmonyPrefix]
		private static void AddSnailMasterAction(Item __instance)
		{
			if (__instance.itemID == 69 && !Singleton<SnailMasterManager>.Instance.IsSnailActivated())
			{
				AddInteractTranslations();
				LoadBugleSFX(((Component)__instance).gameObject);
				__instance.totalUses = 1;
				Action_ReduceUses reduceUsesAction = ((Component)__instance).gameObject.AddComponent<Action_ReduceUses>();
				CallSnailMaster callSnailMaster = ((Component)__instance).gameObject.AddComponent<CallSnailMaster>();
				__instance.usingTimePrimary = 5f;
				__instance.showUseProgress = true;
				callSnailMaster.reduceUsesAction = reduceUsesAction;
				__instance.UIData.hasMainInteract = true;
				__instance.UIData.mainInteractPrompt = "INTERACT_SUMMON";
			}
		}

		private static void LoadBugleSFX(GameObject target)
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Expected O, but got Unknown
			//IL_007a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0080: Expected O, but got Unknown
			GameObject val = new GameObject("SM Audio Play");
			val.transform.SetParent(target.transform, false);
			AudioSource val2 = val.GetComponent<AudioSource>();
			if ((Object)(object)val2 == (Object)null)
			{
				val2 = val.AddComponent<AudioSource>();
			}
			val2.playOnAwake = false;
			val2.maxDistance = 2000f;
			val2.minDistance = 25f;
			val2.pitch = 1f;
			val2.loop = false;
			val2.rolloffMode = (AudioRolloffMode)0;
			val2.outputAudioMixerGroup = GetSFXMixer();
			GameObject val3 = new GameObject("SM Audio End");
			val3.transform.SetParent(target.transform, false);
			AudioSource val4 = val3.GetComponent<AudioSource>();
			if ((Object)(object)val4 == (Object)null)
			{
				val4 = val3.AddComponent<AudioSource>();
			}
			val4.playOnAwake = false;
			val4.maxDistance = 500f;
			val4.minDistance = 1f;
			val4.pitch = 1f;
			val4.rolloffMode = (AudioRolloffMode)2;
			val4.clip = GetBugleStopClip();
			val4.outputAudioMixerGroup = GetSFXMixer();
			BugleSFX val5 = target.GetComponent<BugleSFX>();
			if ((Object)(object)val5 == (Object)null)
			{
				val5 = target.AddComponent<BugleSFX>();
			}
			val5.bugleEnd = val4;
			val5.buglePlayer = val2;
			val5.bugle = GetBugleSFXs();
		}

		private static AudioMixerGroup? GetSFXMixer()
		{
			GameObject val = GameObject.Find("Audio End");
			if ((Object)(object)val == (Object)null)
			{
				Plugin.Logger.LogWarning((object)"Unable to locate BugleStop audio clip");
				return null;
			}
			return val.GetComponent<AudioSource>().outputAudioMixerGroup;
		}

		private static AudioClip? GetBugleStopClip()
		{
			GameObject val = GameObject.Find("Audio End");
			if ((Object)(object)val == (Object)null)
			{
				Plugin.Logger.LogWarning((object)"Unable to locate BugleStop audio clip");
				return null;
			}
			return val.GetComponent<AudioSource>().clip;
		}

		private static AudioClip[] GetBugleSFXs()
		{
			GameObject val = Resources.Load<GameObject>("0_Items/Bugle_Scoutmaster Variant");
			if ((Object)(object)val == (Object)null)
			{
				Plugin.Logger.LogWarning((object)"Unable to load Scoutmaster bugle SFXs");
				return Array.Empty<AudioClip>();
			}
			return val.GetComponent<BugleSFX>().bugle;
		}

		private static void AddInteractTranslations()
		{
			if (!LocalizedText.mainTable.ContainsKey("INTERACT_SUMMON".ToUpperInvariant()))
			{
				List<string> value = new List<string>(Enum.GetValues(typeof(Language)).Length)
				{
					"summon", "invoquer", "evocare", "beschwören", "invocar", "invocar", "invocar", "призывать", "призвати", "召唤",
					"召喚", "召喚する", "소환하다", "przyzwać", "çağırmak"
				};
				LocalizedText.mainTable.Add("INTERACT_SUMMON".ToUpperInvariant(), value);
			}
		}
	}
	internal class CallSnailMaster : Action_CallScoutmaster
	{
		public Action_ReduceUses reduceUsesAction;

		public CallSnailMaster()
		{
			((ItemAction)this).OnCastFinished = true;
		}

		public override void RunAction()
		{
			Singleton<SnailMasterManager>.Instance.EnableSnailMode();
			if (Singleton<SnailMasterManager>.Instance.IsSnailActivated())
			{
				((ItemActionBase)this).item.UIData.hasMainInteract = false;
				((ItemActionBase)reduceUsesAction).RunAction();
			}
		}
	}
	internal class RunStartPatch
	{
		[HarmonyPatch(typeof(RunManager), "StartRun")]
		[HarmonyPrefix]
		private static void DisableSnailMode()
		{
			Singleton<SnailMasterManager>.Instance.DisableSnailMode();
		}
	}
	internal class ScoutMasterPatch
	{
		[ThreadStatic]
		private static bool CanSwitchTeleport;

		[HarmonyPatch(typeof(Scoutmaster), "Chase")]
		[HarmonyTranspiler]
		private static IEnumerable<CodeInstruction> RemoveSprint(IEnumerable<CodeInstruction> instructions, ILGenerator il)
		{
			//IL_00fc: Unknown result type (might be due to invalid IL or missing references)
			//IL_0102: Expected O, but got Unknown
			//IL_010b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0111: Expected O, but got Unknown
			//IL_011e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0124: Expected O, but got Unknown
			List<CodeInstruction> list = new List<CodeInstruction>(instructions);
			int num = -1;
			int num2 = -1;
			for (int i = 0; i < list.Count; i++)
			{
				if (list[i].opcode == OpCodes.Ldarg_0)
				{
					num = i;
				}
				else if (list[i].opcode == OpCodes.Stfld && (FieldInfo)list[i].operand == AccessTools.Field(typeof(CharacterInput), "sprintIsPressed"))
				{
					num2 = i;
					break;
				}
			}
			if (num == -1 || num2 == -1)
			{
				Plugin.Logger.LogError((object)"[Transpiler] Failed to find sprintIsSpressed assignment...");
				return instructions;
			}
			Label label = il.DefineLabel();
			list[num2 + 1].labels.Add(label);
			MethodInfo methodInfo = AccessTools.PropertyGetter(typeof(SnailMasterManager), "Instance");
			MethodInfo methodInfo2 = AccessTools.Method(typeof(SnailMasterManager), "IsSnailActivated", (Type[])null, (Type[])null);
			list.InsertRange(num, (IEnumerable<CodeInstruction>)(object)new CodeInstruction[3]
			{
				new CodeInstruction(OpCodes.Call, (object)methodInfo),
				new CodeInstruction(OpCodes.Call, (object)methodInfo2),
				new CodeInstruction(OpCodes.Brtrue, (object)label)
			});
			return list;
		}

		[HarmonyPatch(typeof(Scoutmaster), "Update")]
		[HarmonyTranspiler]
		private static IEnumerable<CodeInstruction> SetTeleportDistance(IEnumerable<CodeInstruction> instructions)
		{
			//IL_010d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0113: Expected O, but got Unknown
			//IL_011c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0122: Expected O, but got Unknown
			//IL_0139: Unknown result type (might be due to invalid IL or missing references)
			//IL_013f: Expected O, but got Unknown
			//IL_0147: Unknown result type (might be due to invalid IL or missing references)
			//IL_014d: Expected O, but got Unknown
			//IL_015a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0160: Expected O, but got Unknown
			List<CodeInstruction> list = new List<CodeInstruction>(instructions);
			int num = -1;
			for (int i = 2; i < list.Count; i++)
			{
				if (list[i].opcode == OpCodes.Ldc_R4 && list[i - 1].opcode == OpCodes.Ldfld && (FieldInfo)list[i - 1].operand == AccessTools.Field(typeof(Scoutmaster), "distanceToTarget"))
				{
					num = i;
					break;
				}
			}
			if (num == -1)
			{
				Plugin.Logger.LogError((object)"[Transpiler] Failed to find distanceCheck...");
				return instructions;
			}
			list[num].operand = SnailMasterConfig.TeleportDistance.Value / CharacterStats.unitsToMeters;
			Label label = (Label)list[num + 1].operand;
			FieldInfo fieldInfo = AccessTools.Field(typeof(SnailMasterConfig), "TeleportMode");
			MethodInfo methodInfo = AccessTools.PropertyGetter(typeof(ConfigEntry<TeleportModeSelection>), "Value");
			list.InsertRange(num + 2, (IEnumerable<CodeInstruction>)(object)new CodeInstruction[5]
			{
				new CodeInstruction(OpCodes.Ldsfld, (object)fieldInfo),
				new CodeInstruction(OpCodes.Callvirt, (object)methodInfo),
				new CodeInstruction(OpCodes.Ldc_I4, (object)Convert.ToInt32(TeleportModeSelection.Never)),
				new CodeInstruction(OpCodes.Ceq, (object)null),
				new CodeInstruction(OpCodes.Brtrue, (object)label)
			});
			return list;
		}

		[HarmonyPatch(typeof(Scoutmaster), "TeleportCloseToTarget")]
		[HarmonyPrefix]
		private static bool TeleportMasterPrefix(Scoutmaster __instance)
		{
			if (!EnforceTeleportModes(__instance))
			{
				return false;
			}
			bool flag = ConfigureTeleportDistance(__instance);
			if (!flag)
			{
				return false;
			}
			return flag;
		}

		private static bool EnforceTeleportModes(Scoutmaster __instance)
		{
			if (!Singleton<SnailMasterManager>.Instance.IsSnailActivated())
			{
				return true;
			}
			if (SnailMasterConfig.TeleportMode.Value != TeleportModeSelection.OnTargetSwitch || CanSwitchTeleport)
			{
				CanSwitchTeleport = false;
				return true;
			}
			__instance.Chase();
			return false;
		}

		private static bool ConfigureTeleportDistance(Scoutmaster __instance)
		{
			if (!Singleton<SnailMasterManager>.Instance.IsSnailActivated())
			{
				return true;
			}
			__instance.Teleport(__instance.currentTarget, SnailMasterConfig.SpawnMinDistance.Value / CharacterStats.unitsToMeters, SnailMasterConfig.SpawnMaxDistance.Value / CharacterStats.unitsToMeters, SnailMasterConfig.SpawnMaxHeightDifference.Value / CharacterStats.unitsToMeters);
			return false;
		}

		[HarmonyPatch(typeof(Scoutmaster), "ClimbTowards")]
		[HarmonyPrefix]
		private static void ModifyClimbSpeed(ref float mult)
		{
			if (Singleton<SnailMasterManager>.Instance.IsSnailActivated())
			{
				mult *= SnailMasterConfig.ClimbSpeed.Value;
			}
		}

		[HarmonyPatch(typeof(Scoutmaster), "EvasiveBehaviour")]
		[HarmonyPrefix]
		private static bool ForceAlwaysFollowing()
		{
			if (!Singleton<SnailMasterManager>.Instance.IsSnailActivated())
			{
				return true;
			}
			return false;
		}

		[HarmonyPatch(typeof(Scoutmaster), "VerifyTarget")]
		[HarmonyPrefix]
		private static bool ResetTarget(Scoutmaster __instance)
		{
			CanSwitchTeleport = false;
			return SwitchTargets(__instance);
		}

		[HarmonyPatch(typeof(Scoutmaster), "LookForTarget")]
		[HarmonyPrefix]
		private static bool SwitchTargets(Scoutmaster __instance)
		{
			if (!Singleton<SnailMasterManager>.Instance.IsSnailActivated())
			{
				return true;
			}
			if (__instance.sinceLookForTarget < SnailMasterConfig.RetargetCooldown.Value || __instance.isThrowing)
			{
				return false;
			}
			AssignNewTarget(__instance);
			return false;
		}

		public static void AssignNewTarget(Scoutmaster __instance)
		{
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			__instance.sinceLookForTarget = 0f;
			Character val = null;
			switch (SnailMasterConfig.ChaseMode.Value)
			{
			case PlayerChaseSelection.Highest:
				val = __instance.GetHighestCharacter((Character)null);
				break;
			case PlayerChaseSelection.Closest:
				val = GetClosestCharacter(__instance.character.Center);
				break;
			case PlayerChaseSelection.Random:
				val = GetRandomCharacter();
				break;
			}
			CanSwitchTeleport = (Object)(object)val != (Object)(object)__instance.currentTarget;
			Plugin.Logger.LogDebug((object)$"New target selected. Teleport is allowed? {CanSwitchTeleport}");
			__instance.SetCurrentTarget(val, 0f);
		}

		public static Character? GetClosestCharacter(Vector3 position)
		{
			//IL_006a: Unknown result type (might be due to invalid IL or missing references)
			//IL_006d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0072: Unknown result type (might be due to invalid IL or missing references)
			//IL_0077: 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_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			List<Character> allCharacters = Character.AllCharacters;
			Character val = null;
			float num = float.MaxValue;
			foreach (Character item in allCharacters)
			{
				if (item.isBot || item.data.dead || item.data.fullyPassedOut)
				{
					continue;
				}
				Vector3 val2;
				if (!((Object)(object)val == (Object)null))
				{
					val2 = position - item.Center;
					if (!(((Vector3)(ref val2)).sqrMagnitude < num))
					{
						continue;
					}
				}
				val = item;
				val2 = position - item.Center;
				num = ((Vector3)(ref val2)).sqrMagnitude;
			}
			return val;
		}

		public static Character? GetRandomCharacter()
		{
			List<Character> list = Character.AllCharacters.Where((Character player) => !player.isBot && !player.data.dead && !player.data.fullyPassedOut).ToList();
			if (list.Count <= 0)
			{
				return null;
			}
			int index = Random.Range(0, list.Count);
			return list[index];
		}
	}
	internal class SnailMasterMovementPatch
	{
		[HarmonyPatch(typeof(Character), "CalculateWorldMovementDir")]
		[HarmonyTranspiler]
		private static IEnumerable<CodeInstruction> ScaleSnailMovement(IEnumerable<CodeInstruction> instructions, ILGenerator il)
		{
			List<CodeInstruction> list = new List<CodeInstruction>(instructions);
			int num = -1;
			for (int i = 10; i < list.Count; i++)
			{
				if (IsLerpAssignment(list, i))
				{
					num = i - 10;
					break;
				}
			}
			if (num == -1)
			{
				Plugin.Logger.LogError((object)"[Transpiler] Failed to find movement assignment");
				return instructions;
			}
			if (list[num - 1].opcode != OpCodes.Stloc && list[num - 1].opcode != OpCodes.Stloc_S)
			{
				Plugin.Logger.LogError((object)$"[Transpiler] Failed to find local movement vector assignment (have {list[num - 1]})");
				return instructions;
			}
			LocalBuilder movementVector = (LocalBuilder)list[num - 1].operand;
			Label label = il.DefineLabel();
			List<Label> collection = new List<Label>(list[num].labels);
			list[num].labels.RemoveAll((Label lbl) => true);
			list[num].labels.Add(label);
			list.InsertRange(num, CreateVectorScaler(movementVector));
			IEnumerable<CodeInstruction> enumerable = CreateSnailCheck(label);
			enumerable.First().labels.AddRange(collection);
			list.InsertRange(num, enumerable);
			return list;
		}

		private static IEnumerable<CodeInstruction> CreateVectorScaler(LocalBuilder movementVector)
		{
			//IL_006e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0074: Expected O, but got Unknown
			//IL_007c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0082: Expected O, but got Unknown
			//IL_008a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0090: Expected O, but got Unknown
			//IL_0098: Unknown result type (might be due to invalid IL or missing references)
			//IL_009e: Expected O, but got Unknown
			//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ac: Expected O, but got Unknown
			FieldInfo fieldInfo = AccessTools.Field(typeof(SnailMasterConfig), "WalkSpeed");
			MethodInfo methodInfo = AccessTools.PropertyGetter(typeof(ConfigEntry<float>), "Value");
			MethodInfo methodInfo2 = AccessTools.Method(typeof(Vector3), "op_Multiply", new Type[2]
			{
				typeof(Vector3),
				typeof(float)
			}, (Type[])null);
			return (IEnumerable<CodeInstruction>)(object)new CodeInstruction[5]
			{
				new CodeInstruction(OpCodes.Ldloc_S, (object)movementVector),
				new CodeInstruction(OpCodes.Ldsfld, (object)fieldInfo),
				new CodeInstruction(OpCodes.Callvirt, (object)methodInfo),
				new CodeInstruction(OpCodes.Call, (object)methodInfo2),
				new CodeInstruction(OpCodes.Stloc_S, (object)movementVector)
			};
		}

		private static IEnumerable<CodeInstruction> CreateSnailCheck(Label labelToEscapeTo)
		{
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0055: Expected O, but got Unknown
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0063: Expected O, but got Unknown
			//IL_0070: Unknown result type (might be due to invalid IL or missing references)
			//IL_0076: Expected O, but got Unknown
			//IL_007e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0084: Expected O, but got Unknown
			//IL_008c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0092: Expected O, but got Unknown
			//IL_009f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a5: Expected O, but got Unknown
			FieldInfo fieldInfo = AccessTools.Field(typeof(Character), "isBot");
			MethodInfo methodInfo = AccessTools.PropertyGetter(typeof(SnailMasterManager), "Instance");
			MethodInfo methodInfo2 = AccessTools.Method(typeof(SnailMasterManager), "IsSnailActivated", (Type[])null, (Type[])null);
			return (IEnumerable<CodeInstruction>)(object)new CodeInstruction[6]
			{
				new CodeInstruction(OpCodes.Ldarg_0, (object)null),
				new CodeInstruction(OpCodes.Ldfld, (object)fieldInfo),
				new CodeInstruction(OpCodes.Brfalse, (object)labelToEscapeTo),
				new CodeInstruction(OpCodes.Call, (object)methodInfo),
				new CodeInstruction(OpCodes.Call, (object)methodInfo2),
				new CodeInstruction(OpCodes.Brfalse, (object)labelToEscapeTo)
			};
		}

		private static bool IsLerpAssignment(List<CodeInstruction> code, int index)
		{
			if (code.Count < 10 || index < 10)
			{
				return false;
			}
			FieldInfo fieldInfo = AccessTools.Field(typeof(CharacterData), "worldMovementInput_Lerp");
			if (code[index].opcode == OpCodes.Stfld && (FieldInfo)code[index].operand == fieldInfo && code[index - 10].opcode == OpCodes.Ldarg_0)
			{
				return code[index - 8].opcode == OpCodes.Ldarg_0;
			}
			return false;
		}
	}
}
namespace System.Runtime.CompilerServices
{
	[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
	internal sealed class IgnoresAccessChecksToAttribute : Attribute
	{
		public IgnoresAccessChecksToAttribute(string assemblyName)
		{
		}
	}
}