Decompiled source of AdultSupervision v1.4.12

AdultSupervision.dll

Decompiled a week ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Photon.Pun;
using Photon.Realtime;
using UnityEngine;
using UnityEngine.SceneManagement;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("AdultSupervision")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("AdultSupervision")]
[assembly: AssemblyTitle("AdultSupervision")]
[assembly: AssemblyVersion("1.0.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[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 AdultSupervision
{
	[BepInPlugin("emiko.adult-supervision", "Adult Supervision", "1.0.21")]
	public sealed class Plugin : BaseUnityPlugin
	{
		public const string PluginGuid = "emiko.adult-supervision";

		public const string PluginName = "Adult Supervision";

		public const string PluginVersion = "1.0.21";

		internal const string NearestFollowMode = "Nearest";

		internal const string SpecificPlayerFollowMode = "SpecificUserId";

		private const float SpawnCheckInterval = 2f;

		private const float SceneTransitionGracePeriod = 5f;

		private const float RescueDistance = 100f;

		private Harmony _harmony;

		private float _nextSpawnCheck;

		private float _sceneTransitionGraceEnds;

		private string _lastLoggedPlayerScene;

		private int _rescuedScoutmasterInstanceId;

		private ConfigEntry<string> _followMode;

		private ConfigEntry<string> _specificPlayerUserId;

		private ConfigEntry<float> _scoutmasterSfxVolume;

		private ConfigEntry<KeyboardShortcut> _respawnScoutmasterKey;

		private void Awake()
		{
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: Expected O, but got Unknown
			//IL_007b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0085: Expected O, but got Unknown
			//IL_00b9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c3: Expected O, but got Unknown
			//IL_00e3: Unknown result type (might be due to invalid IL or missing references)
			//IL_0140: Unknown result type (might be due to invalid IL or missing references)
			//IL_014a: Expected O, but got Unknown
			_followMode = ((BaseUnityPlugin)this).Config.Bind<string>("Follow Target", "Mode", "Nearest", new ConfigDescription("Choose Nearest or SpecificUserId. In specific mode, the Scoutmaster waits if that user is not in the room.", (AcceptableValueBase)(object)new AcceptableValueList<string>(new string[2] { "Nearest", "SpecificUserId" }), Array.Empty<object>()));
			_specificPlayerUserId = ((BaseUnityPlugin)this).Config.Bind<string>("Follow Target", "SpecificPlayerUserId", string.Empty, new ConfigDescription("Choose a player from the current lobby when Mode is SpecificUserId.", (AcceptableValueBase)(object)new LobbyPlayerAcceptableValues(() => _specificPlayerUserId?.Value), Array.Empty<object>()));
			_scoutmasterSfxVolume = ((BaseUnityPlugin)this).Config.Bind<float>("Follow Target", "ScoutmasterSfxVolume", 1f, new ConfigDescription("Volume multiplier for the room Scoutmaster's sound effects. 0 mutes them; 1 keeps the original volume.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>()));
			_respawnScoutmasterKey = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("Follow Target", "RespawnScoutmasterKey", new KeyboardShortcut((KeyCode)289, Array.Empty<KeyCode>()), "Respawn the room Scoutmaster. Only the room master can use this key.");
			ScoutmasterUpdatePatch.Bind(((BaseUnityPlugin)this).Logger, _followMode, _specificPlayerUserId);
			ScoutmasterSfxVolume.Bind(_scoutmasterSfxVolume);
			ScoutmasterVoidWarpPatch.Bind(((BaseUnityPlugin)this).Logger);
			ScoutmasterSpawnPosePatch.Bind(((BaseUnityPlugin)this).Logger);
			ScoutmasterAggression.Bind(((BaseUnityPlugin)this).Logger);
			_harmony = new Harmony("emiko.adult-supervision");
			_harmony.PatchAll();
			SceneManager.sceneLoaded += OnSceneLoaded;
			((BaseUnityPlugin)this).Logger.LogInfo((object)"Adult Supervision loaded.");
		}

		private void Update()
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0081: Unknown result type (might be due to invalid IL or missing references)
			//IL_0086: 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_01d1: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d7: Unknown result type (might be due to invalid IL or missing references)
			//IL_0384: Unknown result type (might be due to invalid IL or missing references)
			//IL_0389: Unknown result type (might be due to invalid IL or missing references)
			//IL_03b4: Unknown result type (might be due to invalid IL or missing references)
			//IL_03bb: Unknown result type (might be due to invalid IL or missing references)
			//IL_03a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_03aa: Unknown result type (might be due to invalid IL or missing references)
			//IL_0330: Unknown result type (might be due to invalid IL or missing references)
			//IL_0335: Unknown result type (might be due to invalid IL or missing references)
			//IL_0193: Unknown result type (might be due to invalid IL or missing references)
			//IL_03e0: Unknown result type (might be due to invalid IL or missing references)
			//IL_03e2: Unknown result type (might be due to invalid IL or missing references)
			//IL_0233: Unknown result type (might be due to invalid IL or missing references)
			//IL_0238: Unknown result type (might be due to invalid IL or missing references)
			//IL_0254: Unknown result type (might be due to invalid IL or missing references)
			//IL_0259: Unknown result type (might be due to invalid IL or missing references)
			//IL_0285: Unknown result type (might be due to invalid IL or missing references)
			//IL_028c: Unknown result type (might be due to invalid IL or missing references)
			//IL_02aa: Unknown result type (might be due to invalid IL or missing references)
			//IL_02f3: Unknown result type (might be due to invalid IL or missing references)
			KeyboardShortcut value = _respawnScoutmasterKey.Value;
			bool flag = ((KeyboardShortcut)(ref value)).IsDown();
			if (!PhotonNetwork.InRoom)
			{
				_nextSpawnCheck = 0f;
			}
			else
			{
				if (!PhotonNetwork.IsMasterClient)
				{
					return;
				}
				if (flag)
				{
					_nextSpawnCheck = 0f;
				}
				if (Time.time < _nextSpawnCheck)
				{
					return;
				}
				_nextSpawnCheck = Time.time + 2f;
				Character localCharacter = Character.localCharacter;
				if ((Object)(object)localCharacter == (Object)null || (Object)(object)localCharacter.data == (Object)null)
				{
					return;
				}
				Scene scene = ((Component)localCharacter).gameObject.scene;
				Scoutmaster val = ScoutmasterSceneLookup.FindRoomScoutmasterInScene(scene, requireMine: false);
				if (flag)
				{
					if ((Object)(object)val != (Object)null)
					{
						PhotonView component = ((Component)val).GetComponent<PhotonView>();
						if ((Object)(object)component == (Object)null || !component.IsRoomView || !component.IsMine)
						{
							((BaseUnityPlugin)this).Logger.LogWarning((object)"Cannot respawn the active Scoutmaster because this room master does not own its room object.");
							return;
						}
						try
						{
							PhotonNetwork.Destroy(((Component)val).gameObject);
							_nextSpawnCheck = Time.time + 0.5f;
							((BaseUnityPlugin)this).Logger.LogInfo((object)"Respawn requested: removed the active Scoutmaster; a replacement will spawn shortly.");
							return;
						}
						catch (Exception arg)
						{
							((BaseUnityPlugin)this).Logger.LogError((object)$"Failed to remove the active Scoutmaster for respawn: {arg}");
							return;
						}
					}
					((BaseUnityPlugin)this).Logger.LogInfo((object)"Respawn requested: no active room Scoutmaster was found; attempting to spawn one.");
				}
				if (_lastLoggedPlayerScene != ((Scene)(ref scene)).name)
				{
					_lastLoggedPlayerScene = ((Scene)(ref scene)).name;
					PhotonView val2 = (((Object)(object)val != (Object)null) ? ((Component)val).GetComponent<PhotonView>() : null);
					((BaseUnityPlugin)this).Logger.LogInfo((object)(((Object)(object)val == (Object)null) ? ("Scene " + ((Scene)(ref scene)).name + ": no active room-owned Scoutmaster found.") : $"Scene {((Scene)(ref scene)).name}: found room Scoutmaster (mine={val2.IsMine}) at {((Component)val).transform.position}."));
				}
				if ((Object)(object)val != (Object)null)
				{
					float num = Vector3.Distance(((Component)val).transform.position, localCharacter.Center);
					int instanceID = ((Object)val).GetInstanceID();
					if (!(num > 100f) || _rescuedScoutmasterInstanceId == instanceID)
					{
						return;
					}
					_rescuedScoutmasterInstanceId = instanceID;
					PhotonView component2 = ((Component)val).GetComponent<PhotonView>();
					if (!((Object)(object)component2 != (Object)null) || !component2.IsMine)
					{
						return;
					}
					Vector3 val3 = localCharacter.data.lookDirection;
					val3.y = 0f;
					if (((Vector3)(ref val3)).sqrMagnitude < 0.01f)
					{
						val3 = Vector3.forward;
					}
					((Vector3)(ref val3)).Normalize();
					Character component3 = ((Component)val).GetComponent<Character>();
					if ((Object)(object)component3 != (Object)null && (Object)(object)component2 != (Object)null && ScoutmasterSpawnPosition.TryGetGroundedCenter(localCharacter, val3 * 2f, out var target))
					{
						component2.RPC("WarpPlayerRPC", (RpcTarget)0, new object[2] { target, false });
						if ((Object)(object)component3.data != (Object)null)
						{
							component3.data.spawnPoint = ((Component)val).transform;
						}
						((BaseUnityPlugin)this).Logger.LogInfo((object)$"One-time rescue repositioned run Scoutmaster from {num:F1}m away to {target}.");
					}
					else
					{
						((BaseUnityPlugin)this).Logger.LogWarning((object)"Could not find safe ground near the player to rescue the run Scoutmaster; leaving its current position unchanged.");
					}
					return;
				}
				Scoutmaster val4 = default(Scoutmaster);
				if (Scoutmaster.GetPrimaryScoutmaster(ref val4) && (Object)(object)val4 != (Object)null)
				{
					if ((((Component)val4).gameObject.scene == scene && ((Behaviour)val4).isActiveAndEnabled && ((Component)val4).gameObject.activeInHierarchy) || Time.time < _sceneTransitionGraceEnds || TryRemoveStaleRoomScoutmaster(val4))
					{
						return;
					}
					((BaseUnityPlugin)this).Logger.LogWarning((object)"A stale Scoutmaster remains outside the active player scene; attempting a replacement for the active scene.");
				}
				Vector3 val5 = localCharacter.data.lookDirection;
				val5.y = 0f;
				if (((Vector3)(ref val5)).sqrMagnitude < 0.01f)
				{
					val5 = Vector3.forward;
				}
				((Vector3)(ref val5)).Normalize();
				if (ScoutmasterSpawnPosition.TryGetGroundedCenter(localCharacter, val5 * 5f, out var target2))
				{
					try
					{
						GameObject val6 = PhotonNetwork.InstantiateRoomObject("Character_Scoutmaster", target2, Quaternion.identity, (byte)0, (object[])null);
						if ((Object)(object)val6 == (Object)null)
						{
							((BaseUnityPlugin)this).Logger.LogError((object)"Photon failed to spawn Character_Scoutmaster; will retry.");
						}
						else
						{
							Character component4 = val6.GetComponent<Character>();
							if ((Object)(object)component4 != (Object)null && (Object)(object)component4.data != (Object)null)
							{
								component4.data.spawnPoint = val6.transform;
							}
							((BaseUnityPlugin)this).Logger.LogInfo((object)"Spawned a Scoutmaster for this level.");
						}
						return;
					}
					catch (Exception arg2)
					{
						((BaseUnityPlugin)this).Logger.LogError((object)$"Failed to spawn Character_Scoutmaster: {arg2}");
						return;
					}
				}
				((BaseUnityPlugin)this).Logger.LogWarning((object)"Could not find safe ground near the player to spawn a Scoutmaster; will retry.");
			}
		}

		private void OnSceneLoaded(Scene scene, LoadSceneMode mode)
		{
			ScoutmasterSceneLookup.Invalidate();
			_sceneTransitionGraceEnds = Time.time + 5f;
			_nextSpawnCheck = _sceneTransitionGraceEnds;
			_lastLoggedPlayerScene = null;
		}

		private bool TryRemoveStaleRoomScoutmaster(Scoutmaster scoutmaster)
		{
			PhotonView component = ((Component)scoutmaster).GetComponent<PhotonView>();
			if ((Object)(object)component == (Object)null || !component.IsRoomView || !component.IsMine)
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)"Found a stale Scoutmaster outside the player's scene, but it is not a room-owned object this client can remove. Waiting for the game to clean it up.");
				return false;
			}
			try
			{
				PhotonNetwork.Destroy(((Component)scoutmaster).gameObject);
				((BaseUnityPlugin)this).Logger.LogInfo((object)"Removed a stale Scoutmaster from the previous scene.");
				return true;
			}
			catch (Exception arg)
			{
				((BaseUnityPlugin)this).Logger.LogError((object)$"Failed to remove stale Scoutmaster: {arg}");
				return false;
			}
		}

		private void OnDestroy()
		{
			SceneManager.sceneLoaded -= OnSceneLoaded;
			Harmony harmony = _harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
		}
	}
	internal sealed class LobbyPlayerAcceptableValues : AcceptableValueList<string>
	{
		private const string JoinLobbyChoice = "Join a lobby to choose a player";

		private const string PlayerIdPrefix = " (ID ";

		private readonly Func<string> _selectedChoice;

		public override string[] AcceptableValues
		{
			get
			{
				Player[] array = (PhotonNetwork.InRoom ? PhotonNetwork.PlayerList : Array.Empty<Player>());
				List<Player> list = new List<Player>(array.Length);
				foreach (Player val in array)
				{
					if (val != null && !string.IsNullOrEmpty(val.UserId))
					{
						list.Add(val);
					}
				}
				list.Sort((Player left, Player right) => string.Compare(left.NickName, right.NickName, StringComparison.OrdinalIgnoreCase));
				List<string> list2 = new List<string>(list.Count + 2) { "Join a lobby to choose a player" };
				string text = _selectedChoice();
				bool flag = false;
				for (int num = 0; num < list.Count; num++)
				{
					Player val2 = list[num];
					string item = val2.NickName + " (ID " + val2.UserId + ")";
					list2.Add(item);
					if (TryGetUserId(text, out var userId) && string.Equals(val2.UserId, userId, StringComparison.OrdinalIgnoreCase))
					{
						flag = true;
					}
				}
				if (!string.IsNullOrEmpty(text) && !flag && !list2.Contains(text) && TryGetUserId(text, out var _))
				{
					list2.Add("(Not in lobby) " + text);
				}
				return list2.ToArray();
			}
		}

		internal LobbyPlayerAcceptableValues(Func<string> selectedChoice)
			: base(new string[1] { "Join a lobby to choose a player" })
		{
			_selectedChoice = selectedChoice;
		}

		public override bool IsValid(object value)
		{
			return value is string;
		}

		internal static bool TryGetUserId(string choice, out string userId)
		{
			int num = choice.LastIndexOf(" (ID ", StringComparison.Ordinal);
			if (num >= 0 && choice.EndsWith(")", StringComparison.Ordinal))
			{
				userId = choice.Substring(num + " (ID ".Length, choice.Length - num - " (ID ".Length - 1);
				return userId.Length > 0;
			}
			userId = choice;
			return !string.IsNullOrWhiteSpace(userId);
		}
	}
	internal static class ScoutmasterSceneLookup
	{
		private const float LookupRefreshInterval = 1f;

		private static Scoutmaster _cachedAnyScoutmaster;

		private static int _cachedAnySceneHandle = int.MinValue;

		private static float _nextAnyRefresh;

		private static Scoutmaster _cachedOwnedScoutmaster;

		private static int _cachedOwnedSceneHandle = int.MinValue;

		private static float _nextOwnedRefresh;

		internal static Scoutmaster FindRoomScoutmasterInScene(Scene scene, bool requireMine)
		{
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: 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_0091: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_007c: Unknown result type (might be due to invalid IL or missing references)
			Scoutmaster val = (requireMine ? _cachedOwnedScoutmaster : _cachedAnyScoutmaster);
			int num = (requireMine ? _cachedOwnedSceneHandle : _cachedAnySceneHandle);
			float num2 = (requireMine ? _nextOwnedRefresh : _nextAnyRefresh);
			if (SceneHandle.op_Implicit(num) == ((Scene)(ref scene)).handle && Time.time < num2 && ((Object)(object)val == (Object)null || IsUsable(val, scene, requireMine)))
			{
				return val;
			}
			Scoutmaster[] array = Object.FindObjectsByType<Scoutmaster>((FindObjectsSortMode)0);
			foreach (Scoutmaster val2 in array)
			{
				if (IsUsable(val2, scene, requireMine))
				{
					Cache(val2, scene, requireMine);
					return val2;
				}
			}
			Cache(null, scene, requireMine);
			return null;
		}

		internal static void Invalidate()
		{
			_cachedAnyScoutmaster = null;
			_cachedAnySceneHandle = int.MinValue;
			_nextAnyRefresh = 0f;
			_cachedOwnedScoutmaster = null;
			_cachedOwnedSceneHandle = int.MinValue;
			_nextOwnedRefresh = 0f;
		}

		private static bool IsUsable(Scoutmaster scoutmaster, Scene scene, bool requireMine)
		{
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)scoutmaster == (Object)null || !((Behaviour)scoutmaster).isActiveAndEnabled || !((Component)scoutmaster).gameObject.activeInHierarchy || ((Component)scoutmaster).gameObject.scene != scene)
			{
				return false;
			}
			PhotonView component = ((Component)scoutmaster).GetComponent<PhotonView>();
			if ((Object)(object)component != (Object)null && component.IsRoomView)
			{
				if (requireMine)
				{
					return component.IsMine;
				}
				return true;
			}
			return false;
		}

		private static void Cache(Scoutmaster scoutmaster, Scene scene, bool requireMine)
		{
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			float num = Time.time + 1f;
			if (requireMine)
			{
				_cachedOwnedScoutmaster = scoutmaster;
				_cachedOwnedSceneHandle = SceneHandle.op_Implicit(((Scene)(ref scene)).handle);
				_nextOwnedRefresh = num;
			}
			else
			{
				_cachedAnyScoutmaster = scoutmaster;
				_cachedAnySceneHandle = SceneHandle.op_Implicit(((Scene)(ref scene)).handle);
				_nextAnyRefresh = num;
			}
		}

		internal static bool IsPrimaryInPlayerScene(Scoutmaster scoutmaster)
		{
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			Character localCharacter = Character.localCharacter;
			if ((Object)(object)localCharacter != (Object)null && (Object)(object)scoutmaster != (Object)null)
			{
				return (Object)(object)FindRoomScoutmasterInScene(((Component)localCharacter).gameObject.scene, requireMine: true) == (Object)(object)scoutmaster;
			}
			return false;
		}

		internal static bool IsInPlayerScene(Scoutmaster scoutmaster)
		{
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			Character localCharacter = Character.localCharacter;
			if ((Object)(object)localCharacter != (Object)null && (Object)(object)scoutmaster != (Object)null)
			{
				return (Object)(object)FindRoomScoutmasterInScene(((Component)localCharacter).gameObject.scene, requireMine: false) == (Object)(object)scoutmaster;
			}
			return false;
		}
	}
	internal static class ScoutmasterSpawnPosition
	{
		private const float GroundSearchUp = 3f;

		private const float GroundSearchDown = 14f;

		private const float MaxGroundHeightDifference = 3.5f;

		internal static bool TryGetGroundedCenter(Character reference, Vector3 horizontalOffset, out Vector3 target)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: 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_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: 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_0070: Unknown result type (might be due to invalid IL or missing references)
			//IL_007c: Unknown result type (might be due to invalid IL or missing references)
			//IL_008b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0090: Unknown result type (might be due to invalid IL or missing references)
			//IL_009c: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
			target = Vector3.zero;
			if ((Object)(object)reference == (Object)null)
			{
				return false;
			}
			horizontalOffset.y = 0f;
			if (!TryFindGround(reference.Center, out var closestHit))
			{
				return false;
			}
			if (!TryFindGround(reference.Center + horizontalOffset, out var closestHit2) || Mathf.Abs(((RaycastHit)(ref closestHit2)).point.y - ((RaycastHit)(ref closestHit)).point.y) > 3.5f)
			{
				return false;
			}
			float num = reference.Center.y - ((RaycastHit)(ref closestHit)).point.y;
			target = ((RaycastHit)(ref closestHit2)).point + Vector3.up * (num + 0.2f);
			return true;
		}

		private static bool TryFindGround(Vector3 position, out RaycastHit closestHit)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_0064: 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)
			RaycastHit[] array = Physics.RaycastAll(position + Vector3.up * 3f, Vector3.down, 17f, -5, (QueryTriggerInteraction)1);
			closestHit = default(RaycastHit);
			float num = float.MaxValue;
			for (int i = 0; i < array.Length; i++)
			{
				Collider collider = ((RaycastHit)(ref array[i])).collider;
				if (!((Object)(object)collider == (Object)null) && !((Object)(object)((Component)collider).GetComponentInParent<Character>() != (Object)null) && !(((RaycastHit)(ref array[i])).normal.y < 0.5f) && !(((RaycastHit)(ref array[i])).distance >= num))
				{
					closestHit = array[i];
					num = ((RaycastHit)(ref array[i])).distance;
				}
			}
			return num < float.MaxValue;
		}
	}
	[HarmonyPatch(typeof(Scoutmaster), "Update")]
	internal static class ScoutmasterUpdatePatch
	{
		private const float FollowDistance = 8f;

		private const float SprintDistance = 10f;

		private static Action<Scoutmaster, Vector3, float> _walkTowards;

		private static Action<Scoutmaster, Vector3, float> _climbTowards;

		private static Action<Scoutmaster, Vector3> _lookAt;

		private static Action<Scoutmaster> _resetInput;

		private static ManualLogSource _logger;

		private static ConfigEntry<string> _followMode;

		private static ConfigEntry<string> _specificPlayerUserId;

		private static float _nextStatusLog;

		private static float _nextMissingTargetLog;

		internal static void Bind(ManualLogSource logger, ConfigEntry<string> followMode, ConfigEntry<string> specificPlayerUserId)
		{
			_logger = logger;
			_followMode = followMode;
			_specificPlayerUserId = specificPlayerUserId;
			Type? typeFromHandle = typeof(Scoutmaster);
			_walkTowards = AccessTools.MethodDelegate<Action<Scoutmaster, Vector3, float>>(AccessTools.Method(typeFromHandle, "WalkTowards", (Type[])null, (Type[])null), (object)null, true);
			_climbTowards = AccessTools.MethodDelegate<Action<Scoutmaster, Vector3, float>>(AccessTools.Method(typeFromHandle, "ClimbTowards", (Type[])null, (Type[])null), (object)null, true);
			_lookAt = AccessTools.MethodDelegate<Action<Scoutmaster, Vector3>>(AccessTools.Method(typeFromHandle, "LookAt", (Type[])null, (Type[])null), (object)null, true);
			_resetInput = AccessTools.MethodDelegate<Action<Scoutmaster>>(AccessTools.Method(typeFromHandle, "ResetInput", (Type[])null, (Type[])null), (object)null, true);
		}

		[HarmonyPostfix]
		private static void Postfix(Scoutmaster __instance)
		{
			//IL_00b1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c9: 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)
			//IL_0142: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e2: Unknown result type (might be due to invalid IL or missing references)
			//IL_01cb: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)Player.localPlayer == (Object)null)
			{
				return;
			}
			PhotonView component = ((Component)__instance).GetComponent<PhotonView>();
			if ((Object)(object)component == (Object)null || !component.IsRoomView || !component.IsMine || !ScoutmasterSceneLookup.IsPrimaryInPlayerScene(__instance))
			{
				return;
			}
			Character component2 = ((Component)__instance).GetComponent<Character>();
			if ((Object)(object)component2 == (Object)null || (Object)(object)component2.data == (Object)null)
			{
				return;
			}
			ScoutmasterRagdollDamping.ApplyOnce(component2, _logger);
			_resetInput(__instance);
			component2.input.useSecondaryIsPressed = false;
			Character obj = ScoutmasterAggression.GetTarget(__instance);
			if ((Object)(object)obj == (Object)null)
			{
				ClearAggroTarget(__instance, component);
			}
			if (obj == null)
			{
				obj = FindTarget(component2);
			}
			Character val = obj;
			if ((Object)(object)val == (Object)null)
			{
				component2.input.sprintIsPressed = false;
				return;
			}
			float num = Vector3.Distance(component2.Center, val.Center);
			_lookAt(__instance, val.Head);
			component2.input.sprintIsPressed = num > 10f;
			if (component2.input.sprintIsPressed)
			{
				component2.data.currentStamina = 1f;
			}
			if (Time.time >= _nextStatusLog)
			{
				_nextStatusLog = Time.time + 5f;
				ManualLogSource logger = _logger;
				string[] obj2 = new string[5] { "Controlling run Scoutmaster in ", null, null, null, null };
				Scene scene = ((Component)__instance).gameObject.scene;
				obj2[1] = ((Scene)(ref scene)).name;
				obj2[2] = ": ";
				obj2[3] = $"distance={num:F1}m, sprintInput={component2.input.sprintIsPressed}, ";
				obj2[4] = $"sprinting={component2.data.isSprinting}, climbing={component2.data.isClimbing}.";
				logger.LogInfo((object)string.Concat(obj2));
			}
			if (!(num <= 8f))
			{
				if (component2.data.isClimbing)
				{
					_climbTowards(__instance, val.Head, 1f);
				}
				else
				{
					_walkTowards(__instance, val.Head, 1f);
				}
			}
		}

		private static Character FindTarget(Character body)
		{
			List<Character> allPlayerCharacters = PlayerHandler.GetAllPlayerCharacters();
			if (_followMode.Value == "SpecificUserId")
			{
				Character val = FindSpecificPlayer(allPlayerCharacters);
				if ((Object)(object)val != (Object)null)
				{
					return val;
				}
				return FindNearestLivingPlayer(body, allPlayerCharacters);
			}
			return FindNearestLivingPlayer(body, allPlayerCharacters);
		}

		private static Character FindNearestLivingPlayer(Character body, List<Character> players)
		{
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			Character result = null;
			float num = float.MaxValue;
			for (int i = 0; i < players.Count; i++)
			{
				Character val = players[i];
				if (!((Object)(object)val == (Object)null) && !((Object)(object)val.data == (Object)null) && !val.data.dead && !val.data.fullyPassedOut)
				{
					float num2 = Vector3.Distance(body.Center, val.Center);
					if (num2 < num)
					{
						result = val;
						num = num2;
					}
				}
			}
			return result;
		}

		private static Character FindSpecificPlayer(List<Character> players)
		{
			string text = _specificPlayerUserId.Value.Trim();
			if (!LobbyPlayerAcceptableValues.TryGetUserId(text, out var userId) || text == "Join a lobby to choose a player" || text.StartsWith("(Not in lobby) ", StringComparison.Ordinal))
			{
				LogMissingTarget("The configured specific player is not available in this lobby; falling back to the nearest living player.");
				return null;
			}
			for (int i = 0; i < players.Count; i++)
			{
				Character val = players[i];
				if (!((Object)(object)val == (Object)null) && !((Object)(object)val.data == (Object)null) && !val.data.dead && !((Object)(object)((MonoBehaviourPun)val).photonView == (Object)null) && ((MonoBehaviourPun)val).photonView.Owner != null && string.Equals(((MonoBehaviourPun)val).photonView.Owner.UserId, userId, StringComparison.OrdinalIgnoreCase))
				{
					return val;
				}
			}
			LogMissingTarget("Configured follow target '" + userId + "' is absent or dead; falling back to the nearest living player.");
			return null;
		}

		private static void LogMissingTarget(string message)
		{
			if (!(Time.time < _nextMissingTargetLog))
			{
				_nextMissingTargetLog = Time.time + 10f;
				_logger.LogWarning((object)message);
			}
		}

		private static void ClearAggroTarget(Scoutmaster scoutmaster, PhotonView view)
		{
			if ((Object)(object)scoutmaster.currentTarget != (Object)null)
			{
				view.RPC("RPCA_SetCurrentTarget", (RpcTarget)0, new object[2] { -1, 0f });
			}
		}
	}
	internal static class ScoutmasterSfxVolume
	{
		private const float UnattachedSoundRadius = 4f;

		private static ConfigEntry<float> _volume;

		internal static void Bind(ConfigEntry<float> volume)
		{
			_volume = volume;
		}

		internal static void Apply(Vector3 position, Transform followTransform, ref float volumeMultiplier)
		{
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			Scoutmaster val = (((Object)(object)followTransform != (Object)null) ? ((Component)followTransform).GetComponentInParent<Scoutmaster>() : null);
			if ((Object)(object)val != (Object)null && !IsRoomScoutmasterInLocalScene(val))
			{
				val = null;
			}
			if ((Object)(object)val == (Object)null)
			{
				val = FindRoomScoutmasterNear(position);
			}
			if ((Object)(object)val != (Object)null)
			{
				volumeMultiplier *= Mathf.Clamp01(_volume.Value);
			}
		}

		private static bool IsRoomScoutmasterInLocalScene(Scoutmaster scoutmaster)
		{
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			Character localCharacter = Character.localCharacter;
			if ((Object)(object)localCharacter == (Object)null || ((Component)scoutmaster).gameObject.scene != ((Component)localCharacter).gameObject.scene)
			{
				return false;
			}
			PhotonView component = ((Component)scoutmaster).GetComponent<PhotonView>();
			if ((Object)(object)component != (Object)null)
			{
				return component.IsRoomView;
			}
			return false;
		}

		private static Scoutmaster FindRoomScoutmasterNear(Vector3 position)
		{
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: 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_0052: 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)
			Character localCharacter = Character.localCharacter;
			if ((Object)(object)localCharacter == (Object)null)
			{
				return null;
			}
			Scoutmaster val = ScoutmasterSceneLookup.FindRoomScoutmasterInScene(((Component)localCharacter).gameObject.scene, requireMine: false);
			if ((Object)(object)val == (Object)null)
			{
				return null;
			}
			Character component = ((Component)val).GetComponent<Character>();
			Vector3 val2 = (((Object)(object)component != (Object)null) ? component.Center : ((Component)val).transform.position);
			if (!(Vector3.Distance(position, val2) < 4f))
			{
				return null;
			}
			return val;
		}
	}
	[HarmonyPatch(typeof(SFX_Player), "PlaySFX")]
	internal static class ScoutmasterSfxVolumePatch
	{
		[HarmonyPrefix]
		private static void ApplyScoutmasterVolume(Vector3 __1, Transform __2, ref float __4)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			ScoutmasterSfxVolume.Apply(__1, __2, ref __4);
		}
	}
	[HarmonyPatch(typeof(Scoutmaster), "Start")]
	internal static class ScoutmasterSpawnPosePatch
	{
		private static readonly HashSet<int> ScheduledScoutmasters = new HashSet<int>();

		private static ManualLogSource _logger;

		internal static void Bind(ManualLogSource logger)
		{
			_logger = logger;
		}

		[HarmonyPostfix]
		private static void AfterScoutmasterStart(Scoutmaster __instance)
		{
			PhotonView component = ((Component)__instance).GetComponent<PhotonView>();
			if (!((Object)(object)component == (Object)null) && component.IsRoomView && component.IsMine && ScheduledScoutmasters.Add(((Object)__instance).GetInstanceID()))
			{
				((MonoBehaviour)__instance).StartCoroutine(SnapAfterRagdollInitialization(__instance));
			}
		}

		private static IEnumerator SnapAfterRagdollInitialization(Scoutmaster scoutmaster)
		{
			yield return (object)new WaitForFixedUpdate();
			yield return (object)new WaitForFixedUpdate();
			if (!((Object)(object)scoutmaster == (Object)null))
			{
				CharacterRagdoll componentInChildren = ((Component)scoutmaster).GetComponentInChildren<CharacterRagdoll>();
				if ((Object)(object)componentInChildren == (Object)null || componentInChildren.partList == null || componentInChildren.partList.Count == 0)
				{
					_logger.LogWarning((object)"Could not snap newly spawned Scoutmaster to animation: its ragdoll is not initialized.");
					yield break;
				}
				componentInChildren.SnapToAnimation();
				_logger.LogInfo((object)"Snapped newly spawned Scoutmaster to its initialized animation pose.");
			}
		}
	}
	internal static class ScoutmasterAggression
	{
		private static Scoutmaster _scoutmaster;

		private static Character _target;

		private static float _forcedUntil;

		private static ManualLogSource _logger;

		internal static void Bind(ManualLogSource logger)
		{
			_logger = logger;
		}

		internal static bool TryRedirectForcedTarget(Scoutmaster source, Character target, float forceForTime)
		{
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)target == (Object)null || forceForTime <= 0f)
			{
				return false;
			}
			Character localCharacter = Character.localCharacter;
			if ((Object)(object)localCharacter == (Object)null)
			{
				return false;
			}
			Scoutmaster val = ScoutmasterSceneLookup.FindRoomScoutmasterInScene(((Component)localCharacter).gameObject.scene, requireMine: false);
			if ((Object)(object)val == (Object)null || (Object)(object)val == (Object)(object)source)
			{
				return false;
			}
			PhotonView component = ((Component)val).GetComponent<PhotonView>();
			PhotonView photonView = ((MonoBehaviourPun)target).photonView;
			if ((Object)(object)component == (Object)null || !component.IsRoomView || (Object)(object)photonView == (Object)null)
			{
				return false;
			}
			component.RPC("RPCA_SetCurrentTarget", (RpcTarget)0, new object[2] { photonView.ViewID, forceForTime });
			_logger.LogInfo((object)$"Redirected forced Scoutmaster aggression to {target.characterName} for {forceForTime:F0}s.");
			return true;
		}

		internal static bool RegisterForcedTarget(Scoutmaster scoutmaster, int targetViewId, float forceForTime)
		{
			if (forceForTime <= 0f || targetViewId < 0 || !IsManagedRoomScoutmaster(scoutmaster))
			{
				return false;
			}
			PhotonView val = PhotonView.Find(targetViewId);
			Character val2 = (((Object)(object)val != (Object)null) ? ((Component)val).GetComponent<Character>() : null);
			if ((Object)(object)val2 == (Object)null)
			{
				return false;
			}
			_scoutmaster = scoutmaster;
			_target = val2;
			_forcedUntil = Time.time + forceForTime;
			return true;
		}

		internal static Character GetTarget(Scoutmaster scoutmaster)
		{
			if ((Object)(object)_scoutmaster != (Object)(object)scoutmaster || (Object)(object)_target == (Object)null || (Object)(object)_target.data == (Object)null || _target.data.dead || Time.time >= _forcedUntil)
			{
				if ((Object)(object)_scoutmaster == (Object)(object)scoutmaster)
				{
					Clear(scoutmaster);
				}
				return null;
			}
			return _target;
		}

		internal static bool IsActive(Scoutmaster scoutmaster)
		{
			return (Object)(object)GetTarget(scoutmaster) != (Object)null;
		}

		private static bool IsManagedRoomScoutmaster(Scoutmaster scoutmaster)
		{
			if ((Object)(object)scoutmaster == (Object)null)
			{
				return false;
			}
			PhotonView component = ((Component)scoutmaster).GetComponent<PhotonView>();
			if ((Object)(object)component != (Object)null && component.IsRoomView)
			{
				return ScoutmasterSceneLookup.IsInPlayerScene(scoutmaster);
			}
			return false;
		}

		private static void Clear(Scoutmaster scoutmaster)
		{
			if (!((Object)(object)_scoutmaster != (Object)(object)scoutmaster))
			{
				_scoutmaster = null;
				_target = null;
				_forcedUntil = 0f;
			}
		}
	}
	[HarmonyPatch(typeof(Scoutmaster), "SetCurrentTarget")]
	internal static class ScoutmasterForcedTargetRedirectPatch
	{
		[HarmonyPrefix]
		private static bool RedirectForcedTarget(Scoutmaster __instance, Character __0, float __1)
		{
			return !ScoutmasterAggression.TryRedirectForcedTarget(__instance, __0, __1);
		}
	}
	internal static class ScoutmasterRagdollDamping
	{
		private const float AddedLinearDamping = 0.1f;

		private const float AddedAngularDamping = 0.1f;

		private static readonly HashSet<int> DampenedCharacters = new HashSet<int>();

		internal static void ApplyOnce(Character character, ManualLogSource logger)
		{
			int instanceID = ((Object)character).GetInstanceID();
			if (DampenedCharacters.Contains(instanceID))
			{
				return;
			}
			CharacterRagdoll componentInChildren = ((Component)character).GetComponentInChildren<CharacterRagdoll>();
			if ((Object)(object)componentInChildren == (Object)null || componentInChildren.partList == null || componentInChildren.partList.Count == 0)
			{
				return;
			}
			int num = 0;
			for (int i = 0; i < componentInChildren.partList.Count; i++)
			{
				Bodypart val = componentInChildren.partList[i];
				if (!((Object)(object)val == (Object)null) && !((Object)(object)val.Rig == (Object)null))
				{
					Rigidbody rig = val.Rig;
					rig.linearDamping += 0.1f;
					Rigidbody rig2 = val.Rig;
					rig2.angularDamping += 0.1f;
					num++;
				}
			}
			if (num != 0)
			{
				DampenedCharacters.Add(instanceID);
				logger.LogInfo((object)$"Added light ragdoll damping to {num} Scoutmaster body parts.");
			}
		}
	}
	[HarmonyPatch(typeof(Character), "CheckSprint")]
	internal static class ScoutmasterSprintPatch
	{
		[HarmonyPrefix]
		private static bool AllowScoutmasterSprint(Character __instance, ref bool __result)
		{
			Scoutmaster component = ((Component)__instance).GetComponent<Scoutmaster>();
			CharacterData data = __instance.data;
			CharacterInput input = __instance.input;
			if ((Object)(object)component == (Object)null || (Object)(object)data == (Object)null || (Object)(object)input == (Object)null || !input.sprintIsPressed || input.movementInput.y <= 0.01f || data.isClimbing || data.isRopeClimbing || data.isVineClimbing || !ScoutmasterSceneLookup.IsPrimaryInPlayerScene(component))
			{
				return true;
			}
			PhotonView component2 = ((Component)component).GetComponent<PhotonView>();
			if ((Object)(object)component2 == (Object)null || !component2.IsMine)
			{
				return true;
			}
			__result = true;
			return false;
		}
	}
	[HarmonyPatch(typeof(Scoutmaster), "TeleportFarAway")]
	internal static class ScoutmasterTeleportFarAwayPatch
	{
		[HarmonyPrefix]
		private static bool PreventTeleportToVoid(Scoutmaster __instance)
		{
			PhotonView component = ((Component)__instance).GetComponent<PhotonView>();
			if (!((Object)(object)component == (Object)null) && component.IsRoomView)
			{
				return !component.IsMine;
			}
			return true;
		}
	}
	[HarmonyPatch(typeof(Character), "WarpPlayerRPC")]
	internal static class ScoutmasterVoidWarpPatch
	{
		private static readonly HashSet<int> LoggedScoutmasters = new HashSet<int>();

		private static ManualLogSource _logger;

		internal static void Bind(ManualLogSource logger)
		{
			_logger = logger;
		}

		[HarmonyPrefix]
		private static bool PreventVoidWarp(Character __instance, Vector3 __0)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0061: Unknown result type (might be due to invalid IL or missing references)
			if (__0.y < 1000f)
			{
				return true;
			}
			Scoutmaster component = ((Component)__instance).GetComponent<Scoutmaster>();
			PhotonView val = (((Object)(object)component != (Object)null) ? ((Component)component).GetComponent<PhotonView>() : null);
			if ((Object)(object)component == (Object)null || (Object)(object)val == (Object)null || !val.IsRoomView)
			{
				return true;
			}
			if (LoggedScoutmasters.Add(((Object)component).GetInstanceID()))
			{
				_logger.LogInfo((object)$"Prevented out-of-world warp for room Scoutmaster (target={__0}).");
			}
			return false;
		}
	}
	[HarmonyPatch(typeof(Scoutmaster), "HoldPlayer")]
	internal static class ScoutmasterHoldPatch
	{
		[HarmonyPrefix]
		private static bool AllowForcedAttack(Scoutmaster __instance)
		{
			return ScoutmasterAggression.IsActive(__instance);
		}
	}
	[HarmonyPatch(typeof(Scoutmaster), "RPCA_SetCurrentTarget")]
	internal static class ScoutmasterTargetPatch
	{
		[HarmonyPrefix]
		private static bool PreserveForcedTarget(Scoutmaster __instance, ref int __0, float __1)
		{
			if (__0 >= 0 && __1 > 0f && ScoutmasterAggression.RegisterForcedTarget(__instance, __0, __1))
			{
				return true;
			}
			if (__0 == -1 && ScoutmasterAggression.IsActive(__instance))
			{
				return false;
			}
			__0 = -1;
			return true;
		}
	}
}