Decompiled source of TheShyGuy v1.0.2

TheShyGuy.dll

Decompiled a day ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text;
using Il2CppInterop.Runtime;
using Il2CppInterop.Runtime.InteropTypes;
using Il2CppInterop.Runtime.InteropTypes.Arrays;
using Il2CppSystem;
using MelonLoader;
using MelonLoader.Preferences;
using MelonLoader.Utils;
using Microsoft.CodeAnalysis;
using TheShyGuy;
using UnityEngine;
using UnityEngine.AI;
using UnityEngine.SceneManagement;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyTitle("TheShyGuy")]
[assembly: AssemblyDescription("Adds a custom SCP-096 style enemy to BONELAB that rages when a player looks at its face.")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("TheShyGuy")]
[assembly: AssemblyFileVersion("1.0.0")]
[assembly: MelonInfo(typeof(TheShyGuyMod), "TheShyGuy", "1.0.0", "ShyGuyMod", null)]
[assembly: MelonGame("Stress Level Zero", "BONELAB")]
[assembly: MelonOptionalDependencies(new string[] { "BoneLib", "LabFusion" })]
[assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")]
[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 TheShyGuy
{
	internal sealed class ShyGuyRig
	{
		private readonly List<string> _generated = new List<string>();

		internal Transform Root { get; private set; }

		internal Transform FaceDetectionPoint { get; private set; }

		internal Transform HeadBone { get; private set; }

		internal Animator Animator { get; private set; }

		internal Animation LegacyAnimation { get; private set; }

		internal NavMeshAgent Agent { get; private set; }

		internal Collider MainCollider { get; private set; }

		internal Collider TriggerCollider { get; private set; }

		internal AudioSource TriggerAudio { get; private set; }

		internal AudioSource ChaseAudio { get; private set; }

		internal AudioSource AttackAudio { get; private set; }

		internal AudioSource AmbientAudio { get; private set; }

		internal bool FacePointWasGenerated { get; private set; }

		internal static ShyGuyRig Resolve(GameObject instance, ShyGuyPrefabConfig config)
		{
			ShyGuyRig rig = new ShyGuyRig();
			if ((Object)(object)instance == (Object)null)
			{
				ShyGuyLog.Error("ShyGuyRig.Resolve was handed a null GameObject; nothing to wire up.");
				return rig;
			}
			if (config == null)
			{
				config = new ShyGuyPrefabConfig();
			}
			rig.Root = instance.transform;
			ShyGuyLog.Guard("rig resolve", delegate
			{
				rig.ResolveAnimation(config);
				rig.ResolveHeadAndFace(config);
				rig.ResolveColliders(config);
				rig.ResolveAgent(config);
				rig.ResolveAudio(config);
				rig.ApplyScale(config);
			});
			rig.ReportGenerated();
			return rig;
		}

		private void ResolveAnimation(ShyGuyPrefabConfig config)
		{
			Animator = ShyGuyHierarchy.ResolveComponent<Animator>(Root, config.animatorPath, (IEnumerable<string>)new string[4] { "Visuals", "CharacterModel", "Model", "Armature" });
			LegacyAnimation = ShyGuyHierarchy.ResolveComponent<Animation>(Root, config.animatorPath, (IEnumerable<string>)new string[4] { "Visuals", "CharacterModel", "Model", "Armature" });
			if ((Object)(object)Animator == (Object)null && (Object)(object)LegacyAnimation == (Object)null)
			{
				ShyGuyLog.Warn("Neither an Animator nor a legacy Animation component was found anywhere under the creature. It will move and kill but never animate. Add an Animator to the CharacterModel object (see the prefab setup instructions).");
				return;
			}
			if (config.useLegacyAnimation && (Object)(object)LegacyAnimation == (Object)null)
			{
				ShyGuyLog.Warn("The prefab config asks for legacy animation but no Animation component exists; using the Animator instead.");
			}
			if ((Object)(object)Animator != (Object)null)
			{
				try
				{
					Animator.cullingMode = (AnimatorCullingMode)0;
				}
				catch (Exception ex)
				{
					ShyGuyLog.Debug("Setting the Animator culling mode threw: " + ex.Message);
				}
			}
		}

		private void ResolveHeadAndFace(ShyGuyPrefabConfig config)
		{
			//IL_00d0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d6: Expected O, but got Unknown
			//IL_0124: Unknown result type (might be due to invalid IL or missing references)
			//IL_012f: Unknown result type (might be due to invalid IL or missing references)
			//IL_013f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f7: Unknown result type (might be due to invalid IL or missing references)
			//IL_0102: Unknown result type (might be due to invalid IL or missing references)
			//IL_0112: Unknown result type (might be due to invalid IL or missing references)
			HeadBone = ShyGuyHierarchy.FindByPath(Root, config.headBonePath) ?? ShyGuyHierarchy.FindFirstByNames(Root, ShyGuyPrefabConfig.HeadBoneNames);
			if ((Object)(object)HeadBone != (Object)null)
			{
				ShyGuyLog.Debug("Head bone: " + ShyGuyHierarchy.GetPath(Root, HeadBone));
			}
			FaceDetectionPoint = ShyGuyHierarchy.FindByPath(Root, config.faceDetectionPointPath) ?? ShyGuyHierarchy.FindFirstByNames(Root, ShyGuyPrefabConfig.FacePointNames);
			if ((Object)(object)FaceDetectionPoint != (Object)null)
			{
				ShyGuyLog.Msg("Face detection point: " + ShyGuyHierarchy.GetPath(Root, FaceDetectionPoint));
				return;
			}
			FacePointWasGenerated = true;
			Transform val = HeadBone ?? Root;
			GameObject val2 = new GameObject("FaceDetectionPoint");
			val2.transform.SetParent(val, false);
			if ((Object)(object)HeadBone != (Object)null)
			{
				val2.transform.localPosition = Vector3.forward * config.fallbackFaceForward;
				val2.transform.localRotation = Quaternion.identity;
			}
			else
			{
				val2.transform.localPosition = Vector3.up * config.fallbackFaceHeight;
				val2.transform.localRotation = Quaternion.identity;
			}
			FaceDetectionPoint = val2.transform;
			_generated.Add("FaceDetectionPoint (parented to " + ((Object)val).name + ")");
			ShyGuyLog.Warn("No FaceDetectionPoint was found on the prefab, so one was generated " + (((Object)(object)HeadBone != (Object)null) ? ("in front of the '" + ((Object)HeadBone).name + "' bone.") : ("at " + config.fallbackFaceHeight.ToString("F2") + "m above the root.")) + " Gaze detection will work but the position is a guess - add a real FaceDetectionPoint to the prefab for accurate results.");
		}

		private void ResolveColliders(ShyGuyPrefabConfig config)
		{
			//IL_009f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f4: Unknown result type (might be due to invalid IL or missing references)
			MainCollider = ShyGuyHierarchy.ResolveComponent<Collider>(Root, config.mainColliderPath, (IEnumerable<string>)new string[3] { "MainCollider", "BodyCollider", "Capsule" });
			Transform val = ShyGuyHierarchy.FindByPath(Root, config.triggerColliderPath) ?? ShyGuyHierarchy.FindByName(Root, "TriggerCollider");
			if ((Object)(object)val != (Object)null)
			{
				Collider component = ((Component)val).GetComponent<Collider>();
				if ((Object)(object)component != (Object)null && (Object)(object)component != (Object)(object)MainCollider)
				{
					TriggerCollider = component;
				}
			}
			if ((Object)(object)MainCollider == (Object)null)
			{
				GameObject val2 = new GameObject("MainCollider");
				val2.transform.SetParent(Root, false);
				CapsuleCollider val3 = val2.AddComponent<CapsuleCollider>();
				val3.height = Mathf.Max(1f, config.height);
				val3.radius = 0.35f;
				val3.center = new Vector3(0f, config.height * 0.5f, 0f);
				((Collider)val3).isTrigger = false;
				MainCollider = (Collider)(object)val3;
				_generated.Add("MainCollider (capsule, " + config.height.ToString("F1") + "m tall)");
				ShyGuyLog.Warn("The prefab had no collider, so a capsule was generated. Without a collider the gaze raycast has nothing to hit and the creature can never be seen, so this is important to fix properly in the prefab.");
			}
			if ((Object)(object)TriggerCollider == (Object)null)
			{
				ShyGuyLog.Debug("No separate TriggerCollider found. Attack range is measured by distance, which does not need one, so this is optional.");
			}
			if (!((Object)(object)MainCollider != (Object)null))
			{
				return;
			}
			try
			{
				Rigidbody component2 = ((Component)Root).GetComponent<Rigidbody>();
				if ((Object)(object)component2 != (Object)null)
				{
					component2.isKinematic = true;
					component2.useGravity = false;
					ShyGuyLog.Debug("Set the creature's Rigidbody to kinematic so physics cannot fight movement.");
				}
			}
			catch (Exception ex)
			{
				ShyGuyLog.Debug("Configuring the Rigidbody threw: " + ex.Message);
			}
		}

		private void ResolveAgent(ShyGuyPrefabConfig config)
		{
			Agent = ((Component)Root).GetComponent<NavMeshAgent>();
			if ((Object)(object)Agent == (Object)null)
			{
				Agent = ShyGuyHierarchy.ResolveComponent<NavMeshAgent>(Root, "NavMeshAgent", (IEnumerable<string>)new string[1] { "NavMeshAgent" });
			}
			if ((Object)(object)Agent == (Object)null)
			{
				try
				{
					Agent = ((Component)Root).gameObject.AddComponent<NavMeshAgent>();
					((Behaviour)Agent).enabled = false;
					_generated.Add("NavMeshAgent");
					ShyGuyLog.Debug("Added a NavMeshAgent to the creature root.");
					return;
				}
				catch (Exception ex)
				{
					ShyGuyLog.Warn("Could not add a NavMeshAgent (" + ex.Message + "); the creature will rely on direct movement.");
					return;
				}
			}
			if ((Object)(object)((Component)Agent).transform != (Object)(object)Root)
			{
				ShyGuyLog.Warn("The NavMeshAgent is on '" + ((Object)Agent).name + "' rather than the prefab root. The agent moves the object it is attached to, so it must be on the root to move the creature. It is being ignored; direct movement will be used instead.");
				Agent = null;
			}
		}

		private void ResolveAudio(ShyGuyPrefabConfig config)
		{
			TriggerAudio = ResolveAudioSource(config.triggerAudioSourcePath, "TriggerAudioSource");
			ChaseAudio = ResolveAudioSource(config.chaseAudioSourcePath, "ChaseAudioSource");
			AttackAudio = ResolveAudioSource(config.attackAudioSourcePath, "AttackAudioSource");
			AmbientAudio = ResolveAudioSource(config.ambientAudioSourcePath, "AmbientAudioSource");
		}

		private AudioSource ResolveAudioSource(string path, string conventionalName)
		{
			//IL_00b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c3: 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)
			Transform val = ShyGuyHierarchy.FindByPath(Root, path) ?? ShyGuyHierarchy.FindByName(Root, conventionalName);
			if ((Object)(object)val != (Object)null)
			{
				AudioSource component = ((Component)val).GetComponent<AudioSource>();
				if ((Object)(object)component != (Object)null)
				{
					return component;
				}
				try
				{
					AudioSource result = ((Component)val).gameObject.AddComponent<AudioSource>();
					_generated.Add(conventionalName + " (AudioSource component on the existing object)");
					return result;
				}
				catch (Exception ex)
				{
					ShyGuyLog.Debug("Adding an AudioSource to '" + ((Object)val).name + "' threw: " + ex.Message);
					return null;
				}
			}
			try
			{
				Transform val2 = ShyGuyHierarchy.FindFirstByNames(Root, ShyGuyPrefabConfig.AudioParentNames) ?? ShyGuyHierarchy.EnsureChild(Root, "Audio");
				GameObject val3 = new GameObject(conventionalName);
				val3.transform.SetParent(val2, false);
				val3.transform.localPosition = Vector3.zero;
				AudioSource result2 = val3.AddComponent<AudioSource>();
				_generated.Add(conventionalName);
				return result2;
			}
			catch (Exception ex2)
			{
				ShyGuyLog.Warn("Could not create the audio source " + conventionalName + ": " + ex2.Message);
				return null;
			}
		}

		private void ApplyScale(ShyGuyPrefabConfig config)
		{
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			if (Mathf.Approximately(config.modelScale, 1f))
			{
				return;
			}
			try
			{
				Root.localScale = Vector3.one * config.modelScale;
				ShyGuyLog.Debug("Applied a model scale of " + config.modelScale.ToString("F3") + ".");
			}
			catch (Exception ex)
			{
				ShyGuyLog.Debug("Applying the model scale threw: " + ex.Message);
			}
		}

		private void ReportGenerated()
		{
			if (_generated.Count == 0)
			{
				ShyGuyLog.Msg("Prefab wiring complete; every reference was found on the prefab.");
				return;
			}
			ShyGuyLog.Msg("Prefab wiring complete. " + _generated.Count + " item(s) were missing and had to be generated at runtime: " + string.Join("; ", _generated) + ". Add them to TheShyGuy.prefab for full control over their placement.");
		}

		internal string DescribeState()
		{
			return "Rig references:\n  Root:        " + (((Object)(object)Root != (Object)null) ? ((Object)Root).name : "MISSING") + "\n  FacePoint:   " + (((Object)(object)FaceDetectionPoint != (Object)null) ? (ShyGuyHierarchy.GetPath(Root, FaceDetectionPoint) + (FacePointWasGenerated ? " (generated)" : "")) : "MISSING") + "\n  HeadBone:    " + (((Object)(object)HeadBone != (Object)null) ? ((Object)HeadBone).name : "not identified") + "\n  Animator:    " + (((Object)(object)Animator != (Object)null) ? "yes" : "no") + "\n  LegacyAnim:  " + (((Object)(object)LegacyAnimation != (Object)null) ? "yes" : "no") + "\n  NavMeshAgent:" + (((Object)(object)Agent != (Object)null) ? "yes" : "no") + "\n  MainCollider:" + (((Object)(object)MainCollider != (Object)null) ? ((Object)MainCollider).name : "MISSING") + "\n  TriggerCol:  " + (((Object)(object)TriggerCollider != (Object)null) ? ((Object)TriggerCollider).name : "none") + "\n  Generated:   " + ((_generated.Count == 0) ? "nothing" : string.Join("; ", _generated));
		}
	}
	[RegisterTypeInIl2Cpp]
	public sealed class TheShyGuyBehaviour : MonoBehaviour
	{
		private static readonly List<TheShyGuyBehaviour> Instances = new List<TheShyGuyBehaviour>(4);

		private ShyGuyRig _rig;

		private ShyGuyPrefabConfig _prefabConfig;

		private ShyGuyFaceDetector _detector;

		private ShyGuyNavigation _navigation;

		private ShyGuyAnimationController _animation;

		private ShyGuyAudioController _audio;

		private ShyGuyState _state;

		private ShyGuyTarget _target;

		private readonly HashSet<string> _triggeredKeys = new HashSet<string>(StringComparer.Ordinal);

		private bool _triggerPlayedThisCycle;

		private float _triggerEndsAt;

		private float _introEndsAt;

		private float _resetEndsAt;

		private float _lastAttackTime = -999f;

		private float _attackLandsAt;

		private ShyGuyTarget _attackTarget;

		private float _lastDoorReaction = -999f;

		private bool _shuttingDown;

		private bool _ready;

		private Action<ShyGuyTarget, string> _targetLostHandler;

		private Action<ShyGuyTarget> _targetRespawnedHandler;

		private Action<Collider> _doorBlockedHandler;

		internal static IReadOnlyList<TheShyGuyBehaviour> All => Instances;

		internal ShyGuyState State => _state;

		internal ShyGuyTarget CurrentTarget => _target;

		public TheShyGuyBehaviour(IntPtr pointer)
			: base(pointer)
		{
		}

		internal void Initialise(ShyGuyPrefabConfig prefabConfig)
		{
			//IL_018a: Unknown result type (might be due to invalid IL or missing references)
			//IL_018f: Unknown result type (might be due to invalid IL or missing references)
			if (_ready)
			{
				return;
			}
			_prefabConfig = prefabConfig ?? ShyGuyAssetLoader.GetPrefabConfig();
			_rig = ShyGuyRig.Resolve(((Component)this).gameObject, _prefabConfig);
			if ((Object)(object)_rig.Root == (Object)null)
			{
				ShyGuyLog.Error("Could not resolve the creature's own transform; disabling this instance.");
				((Behaviour)this).enabled = false;
				return;
			}
			_detector = new ShyGuyFaceDetector(((Component)this).transform);
			_navigation = new ShyGuyNavigation(((Component)this).transform, _rig.Agent, _prefabConfig.height);
			_doorBlockedHandler = OnDoorBlocked;
			_navigation.DoorBlocked += _doorBlockedHandler;
			_animation = new ShyGuyAnimationController(_rig.Animator, _rig.LegacyAnimation, _prefabConfig);
			_audio = new ShyGuyAudioController(((Component)this).transform, _prefabConfig, _rig);
			ShyGuyMaterialFixer.FixHierarchy(((Component)this).transform);
			ShyGuyPlayerFinder playerFinder = TheShyGuyMod.PlayerFinder;
			if (playerFinder != null)
			{
				_targetLostHandler = OnPlayerFinderTargetLost;
				_targetRespawnedHandler = OnPlayerFinderTargetRespawned;
				playerFinder.TargetLost += _targetLostHandler;
				playerFinder.TargetRespawned += _targetRespawnedHandler;
			}
			if (!Instances.Contains(this))
			{
				Instances.Add(this);
			}
			_navigation.EvaluateNavMeshAvailability();
			EnterSpawning();
			_ready = true;
			string[] obj = new string[7] { "The Shy Guy is active at ", null, null, null, null, null, null };
			Vector3 position = ((Component)this).transform.position;
			obj[1] = ((Vector3)(ref position)).ToString("F1");
			obj[2] = ". Animation backend: ";
			obj[3] = _animation.Backend;
			obj[4] = ". Movement: ";
			obj[5] = (_navigation.UsingNavMesh ? "NavMesh" : "direct");
			obj[6] = ".";
			ShyGuyLog.Msg(string.Concat(obj));
		}

		private void Update()
		{
			if (!_shuttingDown && _ready)
			{
				ShyGuyLog.Guard("behaviour tick", Tick);
			}
		}

		private void OnDestroy()
		{
			_shuttingDown = true;
			try
			{
				_audio?.StopAll();
			}
			catch
			{
			}
			try
			{
				_animation?.StopAll();
			}
			catch
			{
			}
			if (_navigation != null)
			{
				if (_doorBlockedHandler != null)
				{
					_navigation.DoorBlocked -= _doorBlockedHandler;
				}
				_navigation.Dispose();
			}
			ShyGuyPlayerFinder playerFinder = TheShyGuyMod.PlayerFinder;
			if (playerFinder != null)
			{
				if (_targetLostHandler != null)
				{
					playerFinder.TargetLost -= _targetLostHandler;
				}
				if (_targetRespawnedHandler != null)
				{
					playerFinder.TargetRespawned -= _targetRespawnedHandler;
				}
			}
			_targetLostHandler = null;
			_targetRespawnedHandler = null;
			_doorBlockedHandler = null;
			_target = null;
			_attackTarget = null;
			_triggeredKeys.Clear();
			Instances.Remove(this);
			ShyGuyLog.Debug("A Shy Guy instance was destroyed and cleaned up. " + Instances.Count + " remain.");
		}

		private void Tick()
		{
			float deltaTime = Time.deltaTime;
			if (deltaTime <= 0f)
			{
				return;
			}
			if (!ShyGuyConfig.Enabled)
			{
				if (_state != ShyGuyState.Passive)
				{
					EnterPassive();
				}
				_navigation.Stop();
				return;
			}
			ShyGuyPlayerFinder playerFinder = TheShyGuyMod.PlayerFinder;
			if (playerFinder == null)
			{
				ShyGuyLog.DebugThrottled("tick:nofinder", "No player finder is available this frame.", 10f);
				return;
			}
			List<ShyGuyTarget> livingPlayers = playerFinder.GetLivingPlayers();
			PruneTriggeredKeys(playerFinder);
			switch (_state)
			{
			case ShyGuyState.Spawning:
				TickSpawning(deltaTime);
				break;
			case ShyGuyState.Passive:
				TickPassive(livingPlayers, deltaTime);
				break;
			case ShyGuyState.Triggering:
				TickTriggering(deltaTime);
				break;
			case ShyGuyState.Chasing:
				TickChasing(deltaTime);
				break;
			case ShyGuyState.Attacking:
				TickAttacking(deltaTime);
				break;
			case ShyGuyState.Resetting:
				TickResetting(deltaTime);
				break;
			default:
				ShyGuyLog.Warn("Unknown state " + _state.ToString() + "; returning to passive.");
				EnterPassive();
				break;
			}
		}

		private void EnterSpawning()
		{
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			_state = ShyGuyState.Spawning;
			_target = null;
			_attackTarget = null;
			_attackLandsAt = 0f;
			_navigation.Freeze();
			_navigation.ResetStuckTracking();
			_navigation.SetHome(((Component)this).transform.position);
			_audio.StopChase();
			_audio.StopTrigger();
			_audio.StartAmbient();
			_animation.SetSpeedMultiplier(1f);
			bool num = _animation.Play("intro", restart: true);
			float introHoldDuration = _animation.GetIntroHoldDuration();
			if (!num)
			{
				ShyGuyLog.Msg("No Intro animation found; entering passive immediately.");
				EnterPassive(announce: false);
			}
			else
			{
				_introEndsAt = Time.time + introHoldDuration;
				ShyGuyLog.Msg("Playing Intro (" + introHoldDuration.ToString("F1") + "s). Cannot be triggered until it finishes.");
			}
		}

		private void TickSpawning(float deltaTime)
		{
			_navigation.Freeze();
			_animation.Play("intro");
			if (!(Time.time < _introEndsAt))
			{
				ShyGuyLog.Debug("Intro finished; entering passive wander.");
				EnterPassive();
			}
		}

		private void TickPassive(List<ShyGuyTarget> living, float deltaTime)
		{
			bool flag = _navigation.TickPassive(deltaTime);
			if (ShyGuyConfig.PassiveBehaviour == PassiveMode.Stationary)
			{
				_animation.Play("idle");
				_animation.SetSpeedMultiplier(0.01f);
			}
			else
			{
				_animation.SetSpeedMultiplier(1f);
				_animation.Play(flag ? "walk" : "idle");
			}
			_audio.StartAmbient();
			ShyGuyTarget shyGuyTarget = _detector.Evaluate(living, _rig.FaceDetectionPoint, deltaTime);
			if (shyGuyTarget != null)
			{
				BeginTrigger(shyGuyTarget);
			}
		}

		private void BeginTrigger(ShyGuyTarget target)
		{
			if (_state != ShyGuyState.Passive)
			{
				ShyGuyLog.Debug("Ignoring a face detection: already in state " + _state.ToString() + ".");
				return;
			}
			if (_triggerPlayedThisCycle)
			{
				ShyGuyLog.Debug("Trigger already played this rage cycle; going straight to the chase.");
				_target = target;
				EnterChasing();
				return;
			}
			if (target == null || !target.IsAlive)
			{
				ShyGuyLog.Debug("Trigger requested for an invalid target; ignoring.");
				return;
			}
			_triggeredKeys.Clear();
			_triggeredKeys.Add(target.Key);
			target.HasTriggered = true;
			_target = target;
			_triggerPlayedThisCycle = true;
			_state = ShyGuyState.Triggering;
			_animation.SetSpeedMultiplier(1f);
			_navigation.Freeze();
			_audio.StopAmbient();
			_audio.StopChase();
			_audio.PlayTrigger();
			_animation.Play("trigger", restart: true);
			float triggerHoldDuration = _animation.GetTriggerHoldDuration();
			_triggerEndsAt = Time.time + triggerHoldDuration;
			ShyGuyLog.Msg("The Shy Guy has been triggered by " + target.DisplayName + ". Raging for " + triggerHoldDuration.ToString("F1") + "s before the chase begins.");
		}

		private void TickTriggering(float deltaTime)
		{
			_navigation.Freeze();
			_animation.Play("trigger");
			if (!(Time.time < _triggerEndsAt))
			{
				ShyGuyLog.Debug("Trigger animation finished.");
				if (_target == null || !_target.IsAlive)
				{
					ShyGuyLog.Debug("The triggering player is no longer valid after the trigger animation.");
					HandleTargetLost("became invalid during the trigger animation");
				}
				else
				{
					EnterChasing();
				}
			}
		}

		private void EnterChasing()
		{
			_state = ShyGuyState.Chasing;
			_navigation.ResetStuckTracking();
			_navigation.ResumeChase();
			_navigation.EvaluateNavMeshAvailability();
			_audio.StopAmbient();
			_audio.StopTrigger();
			_audio.StartChase();
			_animation.Play("run");
			ShyGuyLog.Msg("The Shy Guy is now chasing " + ((_target != null) ? _target.DisplayName : "nobody") + ".");
		}

		private void TickChasing(float deltaTime)
		{
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: 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_005c: Unknown result type (might be due to invalid IL or missing references)
			if (ValidateTarget())
			{
				_target.RefreshLiveHead();
				_audio.StartChase();
				_animation.Play("run");
				Vector3 navigationPosition = _target.NavigationPosition;
				float num = _navigation.Chase(navigationPosition, deltaTime);
				if (Vector3.Distance(((Component)this).transform.position, _target.EyePosition) <= ShyGuyConfig.AttackDistance || num <= ShyGuyConfig.AttackDistance)
				{
					EnterAttacking();
				}
			}
		}

		private void EnterAttacking()
		{
			_state = ShyGuyState.Attacking;
			_navigation.Stop();
			ShyGuyLog.Debug("In attack range of " + ((_target != null) ? _target.DisplayName : "nobody") + ".");
		}

		private void TickAttacking(float deltaTime)
		{
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: 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)
			//IL_0074: Unknown result type (might be due to invalid IL or missing references)
			//IL_0079: Unknown result type (might be due to invalid IL or missing references)
			if (!ValidateTarget())
			{
				return;
			}
			_target.RefreshLiveHead();
			if (Vector3.Distance(((Component)this).transform.position, _target.EyePosition) > ShyGuyConfig.AttackDistance * 1.4f)
			{
				ShyGuyLog.Debug(_target.DisplayName + " escaped attack range; resuming the chase.");
				EnterChasing();
				return;
			}
			_navigation.RotateTowards(_target.EyePosition - ((Component)this).transform.position, deltaTime);
			if (_attackLandsAt > 0f)
			{
				if (Time.time >= _attackLandsAt)
				{
					LandAttack();
				}
			}
			else if (Time.time - _lastAttackTime < ShyGuyConfig.AttackCooldown)
			{
				_animation.Play("run");
			}
			else if (ShyGuyConfig.AttackRequiresLineOfSight && !HasLineOfSightTo(_target))
			{
				ShyGuyLog.DebugThrottled("attack:nolos", "In range of " + _target.DisplayName + " but there is no clear line to them; not attacking through geometry.");
				EnterChasing();
			}
			else
			{
				BeginAttackSwing();
			}
		}

		private void BeginAttackSwing()
		{
			_attackTarget = _target;
			_attackLandsAt = Time.time + ShyGuyConfig.AttackWindup;
			_audio.StopChase();
			_audio.PlayAttack();
			_animation.Play("attack", restart: true);
			ShyGuyLog.Debug("Attack swing started against " + _attackTarget.DisplayName + ".");
		}

		private void LandAttack()
		{
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			_attackLandsAt = 0f;
			ShyGuyTarget attackTarget = _attackTarget;
			_attackTarget = null;
			if (attackTarget == null || !attackTarget.IsAlive)
			{
				ShyGuyLog.Debug("The attack landed on nobody: the target became invalid mid-swing.");
				_lastAttackTime = Time.time;
				return;
			}
			float num = Vector3.Distance(((Component)this).transform.position, attackTarget.EyePosition);
			if (num > ShyGuyConfig.AttackDistance * 1.6f)
			{
				ShyGuyLog.Debug(attackTarget.DisplayName + " dodged the attack by moving " + num.ToString("F1") + "m away.");
				_lastAttackTime = Time.time;
				EnterChasing();
				return;
			}
			bool num2 = ShyGuyDamage.Attack(attackTarget);
			_lastAttackTime = Time.time;
			if (!num2)
			{
				EnterChasing();
				return;
			}
			ShyGuyLog.Msg(attackTarget.DisplayName + " was killed by The Shy Guy; returning to idle.");
			HandleTargetLost("killed by The Shy Guy");
		}

		private bool HasLineOfSightTo(ShyGuyTarget target)
		{
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: 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_003f: 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_0058: Unknown result type (might be due to invalid IL or missing references)
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0063: Unknown result type (might be due to invalid IL or missing references)
			//IL_0082: Unknown result type (might be due to invalid IL or missing references)
			//IL_0083: Unknown result type (might be due to invalid IL or missing references)
			//IL_0085: Unknown result type (might be due to invalid IL or missing references)
			if (target == null)
			{
				return false;
			}
			Vector3 val = (((Object)(object)_rig.FaceDetectionPoint != (Object)null) ? _rig.FaceDetectionPoint.position : (((Component)this).transform.position + Vector3.up * 1.5f));
			Vector3 val2 = target.EyePosition - val;
			float magnitude = ((Vector3)(ref val2)).magnitude;
			if (magnitude < 0.05f)
			{
				return true;
			}
			GameObject victimRig = target.RigRoot;
			if (!ShyGuyPhysics.Raycast(val, val2 / magnitude, magnitude, _detector.GetOcclusionMask(), out var nearest, (Collider collider) => (Object)(object)collider == (Object)null || ShyGuyHierarchy.IsPartOf(((Component)this).transform, ((Component)collider).transform) || ((Object)(object)victimRig != (Object)null && ShyGuyHierarchy.IsPartOf(victimRig.transform, ((Component)collider).transform))))
			{
				return true;
			}
			ShyGuyLog.DebugThrottled("attack:blocked", "Attack line of sight to " + target.DisplayName + " is blocked by '" + (((Object)(object)((RaycastHit)(ref nearest)).collider != (Object)null) ? ((Object)((RaycastHit)(ref nearest)).collider).name : "unknown") + "'.");
			return false;
		}

		private void EnterResetting()
		{
			ShyGuyLog.Msg("Rage cycle ended; returning to passive (no calm-down pose).");
			EnterPassive(announce: false);
		}

		private void TickResetting(float deltaTime)
		{
			EnterPassive(announce: false);
		}

		private void EnterPassive(bool announce = true)
		{
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			_state = ShyGuyState.Passive;
			_target = null;
			_attackTarget = null;
			_attackLandsAt = 0f;
			_triggerPlayedThisCycle = false;
			ClearAllLookState();
			_triggeredKeys.Clear();
			_navigation.Stop();
			_navigation.ResetStuckTracking();
			_navigation.SetHome(((Component)this).transform.position);
			_audio.StopChase();
			_audio.StopTrigger();
			_audio.StartAmbient();
			_animation.SetSpeedMultiplier(1f);
			_animation.StopAll();
			_animation.Play("idle", restart: true);
			if (announce)
			{
				ShyGuyLog.Msg("The Shy Guy is passive again. Do not look at its face.");
			}
		}

		private void ClearAllLookState()
		{
			ShyGuyPlayerFinder playerFinder = TheShyGuyMod.PlayerFinder;
			if (playerFinder == null)
			{
				return;
			}
			List<ShyGuyTarget> livingPlayers = playerFinder.GetLivingPlayers();
			for (int i = 0; i < livingPlayers.Count; i++)
			{
				ShyGuyTarget shyGuyTarget = livingPlayers[i];
				if (shyGuyTarget != null)
				{
					shyGuyTarget.ClearLookState();
					shyGuyTarget.HasTriggered = false;
				}
			}
		}

		private bool ValidateTarget()
		{
			if (_target == null)
			{
				HandleTargetLost("no target");
				return false;
			}
			if (!_target.IsValid)
			{
				HandleTargetLost(_target.InvalidReason ?? "rig became invalid");
				return false;
			}
			if (!_target.IsAlive)
			{
				HandleTargetLost("died");
				return false;
			}
			if (!_target.IsLocal && _target.FusionId >= 0 && !ShyGuyFusionBridge.IsStillConnected(_target.FusionId))
			{
				HandleTargetLost("left the Fusion session");
				return false;
			}
			return true;
		}

		private void HandleTargetLost(string reason)
		{
			string text = ((_target != null) ? _target.DisplayName : "the target");
			if (_target != null)
			{
				_triggeredKeys.Remove(_target.Key);
				_target.HasTriggered = false;
				_target.ClearLookState();
			}
			_triggeredKeys.Clear();
			_target = null;
			_attackTarget = null;
			_attackLandsAt = 0f;
			_audio.StopChase();
			ShyGuyLog.Msg("Lost " + text + " (" + reason + "). Returning to passive; another player may trigger.");
			EnterResetting();
		}

		private void PruneTriggeredKeys(ShyGuyPlayerFinder finder)
		{
			if (_triggeredKeys.Count == 0)
			{
				return;
			}
			List<string> list = null;
			foreach (string triggeredKey in _triggeredKeys)
			{
				ShyGuyTarget shyGuyTarget = finder.Find(triggeredKey);
				if (shyGuyTarget == null || !shyGuyTarget.IsAlive)
				{
					(list ?? (list = new List<string>())).Add(triggeredKey);
				}
			}
			if (list != null)
			{
				for (int i = 0; i < list.Count; i++)
				{
					_triggeredKeys.Remove(list[i]);
					ShyGuyLog.Debug("Removed '" + list[i] + "' from the triggered list: no longer a living player.");
				}
			}
		}

		private void OnPlayerFinderTargetLost(ShyGuyTarget lost, string reason)
		{
			if (_shuttingDown || lost == null)
			{
				return;
			}
			_triggeredKeys.Remove(lost.Key);
			if (_target != null && string.Equals(_target.Key, lost.Key, StringComparison.Ordinal))
			{
				ShyGuyLog.Guard("target lost event", delegate
				{
					HandleTargetLost(reason ?? "disconnected");
				});
			}
		}

		private void OnPlayerFinderTargetRespawned(ShyGuyTarget respawned)
		{
			if (_shuttingDown || respawned == null)
			{
				return;
			}
			_triggeredKeys.Remove(respawned.Key);
			respawned.HasTriggered = false;
			respawned.ClearLookState();
			if (_target != null && string.Equals(_target.Key, respawned.Key, StringComparison.Ordinal))
			{
				ShyGuyLog.Guard("target respawn event", delegate
				{
					HandleTargetLost("respawned");
				});
			}
		}

		private void OnDoorBlocked(Collider door)
		{
			if (!_shuttingDown && _state == ShyGuyState.Chasing && !(Time.time - _lastDoorReaction < 2.5f))
			{
				_lastDoorReaction = Time.time;
				ShyGuyLog.Debug("Forcing past the door '" + (((Object)(object)door != (Object)null) ? ((Object)door).name : "unknown") + "'.");
				_audio.PlayDoor();
				_animation.Play("door", restart: true);
			}
		}

		internal void ForceTrigger(ShyGuyTarget target)
		{
			if (_ready)
			{
				if (_state != ShyGuyState.Passive)
				{
					ShyGuyLog.Msg("Cannot force a trigger: already in state " + _state.ToString() + ".");
					return;
				}
				if (target == null || !target.IsAlive)
				{
					ShyGuyLog.Warn("Cannot force a trigger: no valid living player was supplied.");
					return;
				}
				ShyGuyLog.Msg("Forcing a trigger against " + target.DisplayName + ".");
				BeginTrigger(target);
			}
		}

		internal void ForceCalm()
		{
			if (_ready)
			{
				ShyGuyLog.Msg("Forcing The Shy Guy to calm down.");
				EnterResetting();
			}
		}

		internal string Describe()
		{
			//IL_0012: 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)
			List<string> list = new List<string>();
			Vector3 position = ((Component)this).transform.position;
			list.Add("=== The Shy Guy @ " + ((Vector3)(ref position)).ToString("F1") + " ===");
			list.Add("State: " + _state.ToString() + " | target: " + ((_target != null) ? _target.ToString() : "none") + " | triggerPlayedThisCycle: " + _triggerPlayedThisCycle);
			list.Add("Triggered players (" + _triggeredKeys.Count + "): " + ((_triggeredKeys.Count == 0) ? "none" : string.Join(", ", _triggeredKeys)));
			list.Add((_rig != null) ? _rig.DescribeState() : "Rig: not resolved");
			list.Add((_navigation != null) ? _navigation.DescribeState() : "Navigation: not resolved");
			list.Add((_animation != null) ? _animation.DescribeState() : "Animation: not resolved");
			list.Add((_audio != null) ? _audio.DescribeState() : "Audio: not resolved");
			List<string> list2 = list;
			ShyGuyPlayerFinder playerFinder = TheShyGuyMod.PlayerFinder;
			if (playerFinder != null && _detector != null && _rig != null)
			{
				list2.Add("Gaze evaluation:");
				list2.Add(_detector.DescribeGaze(playerFinder.GetLivingPlayers(), _rig.FaceDetectionPoint));
			}
			return string.Join("\n", list2);
		}

		internal static void DestroyAll()
		{
			List<TheShyGuyBehaviour> list = new List<TheShyGuyBehaviour>(Instances);
			Instances.Clear();
			foreach (TheShyGuyBehaviour item in list)
			{
				if ((Object)(object)item == (Object)null)
				{
					continue;
				}
				try
				{
					GameObject gameObject = ((Component)item).gameObject;
					item._shuttingDown = true;
					if ((Object)(object)gameObject != (Object)null)
					{
						Object.Destroy((Object)(object)gameObject);
					}
				}
				catch (Exception ex)
				{
					ShyGuyLog.Debug("Destroying a Shy Guy instance threw: " + ex.Message);
				}
			}
			if (list.Count > 0)
			{
				ShyGuyLog.Msg("Destroyed " + list.Count + " Shy Guy instance(s).");
			}
		}

		internal static int PurgeDestroyedInstances()
		{
			int num = 0;
			for (int num2 = Instances.Count - 1; num2 >= 0; num2--)
			{
				TheShyGuyBehaviour theShyGuyBehaviour = Instances[num2];
				if ((Object)(object)theShyGuyBehaviour == (Object)null || (Object)(object)((Component)theShyGuyBehaviour).gameObject == (Object)null)
				{
					Instances.RemoveAt(num2);
					num++;
				}
			}
			if (num > 0)
			{
				ShyGuyLog.Debug("Purged " + num + " destroyed Shy Guy reference(s).");
			}
			return num;
		}
	}
	internal enum PassiveMode
	{
		Stationary,
		IdleAnimation,
		Wander
	}
	internal enum TargetLostAction
	{
		ReturnToPassive,
		ChaseAnotherTriggeredPlayer,
		PreferAnotherThenPassive
	}
	internal static class ShyGuyConfig
	{
		internal const string CategoryId = "TheShyGuy";

		internal const string CategoryDisplay = "The Shy Guy";

		private static MelonPreferences_Category _category;

		private static bool _created;

		private static MelonPreferences_Entry<bool> _enabled;

		private static MelonPreferences_Entry<float> _detectionDistance;

		private static MelonPreferences_Entry<float> _detectionAngle;

		private static MelonPreferences_Entry<bool> _faceVisibilityRaycast;

		private static MelonPreferences_Entry<float> _requiredLookDuration;

		private static MelonPreferences_Entry<float> _triggerAnimationDurationFallback;

		private static MelonPreferences_Entry<float> _chaseSpeed;

		private static MelonPreferences_Entry<float> _chaseAcceleration;

		private static MelonPreferences_Entry<float> _turningSpeed;

		private static MelonPreferences_Entry<float> _navMeshRepathInterval;

		private static MelonPreferences_Entry<float> _attackDistance;

		private static MelonPreferences_Entry<float> _attackCooldown;

		private static MelonPreferences_Entry<float> _damageAmount;

		private static MelonPreferences_Entry<bool> _instantKillEnabled;

		private static MelonPreferences_Entry<bool> _returnToPassiveAfterTargetDeath;

		private static MelonPreferences_Entry<bool> _chaseAnotherTriggeredPlayer;

		private static MelonPreferences_Entry<bool> _directMovementFallback;

		private static MelonPreferences_Entry<float> _stuckDetectionTimeout;

		private static MelonPreferences_Entry<bool> _debugLogging;

		private static MelonPreferences_Entry<bool> _fusionSupportEnabled;

		private static MelonPreferences_Entry<float> _faceFacingAngle;

		private static MelonPreferences_Entry<float> _faceHitRadius;

		private static MelonPreferences_Entry<float> _minimumDetectionDistance;

		private static MelonPreferences_Entry<float> _lookMemoryDecay;

		private static MelonPreferences_Entry<string> _occlusionIgnoreLayers;

		private static MelonPreferences_Entry<int> _passiveMode;

		private static MelonPreferences_Entry<float> _passiveWanderRadius;

		private static MelonPreferences_Entry<float> _passiveWanderSpeed;

		private static MelonPreferences_Entry<float> _passiveWanderInterval;

		private static MelonPreferences_Entry<bool> _passiveAmbientAudio;

		private static MelonPreferences_Entry<float> _maxTriggerDuration;

		private static MelonPreferences_Entry<bool> _useClipLengthForTrigger;

		private static MelonPreferences_Entry<float> _stoppingDistance;

		private static MelonPreferences_Entry<int> _obstacleAvoidanceQuality;

		private static MelonPreferences_Entry<float> _obstacleAvoidanceRadius;

		private static MelonPreferences_Entry<float> _stuckMoveThreshold;

		private static MelonPreferences_Entry<float> _stuckAccelerationBoost;

		private static MelonPreferences_Entry<bool> _stuckTeleportRecovery;

		private static MelonPreferences_Entry<float> _stuckTeleportDistance;

		private static MelonPreferences_Entry<float> _directFallbackGravity;

		private static MelonPreferences_Entry<float> _directFallbackStepHeight;

		private static MelonPreferences_Entry<bool> _breakDoors;

		private static MelonPreferences_Entry<bool> _attackRequiresLineOfSight;

		private static MelonPreferences_Entry<float> _attackWindup;

		private static MelonPreferences_Entry<float> _resetStateDuration;

		private static MelonPreferences_Entry<string> _spawnKey;

		private static MelonPreferences_Entry<string> _despawnKey;

		private static MelonPreferences_Entry<float> _spawnDistance;

		private static MelonPreferences_Entry<int> _maxInstances;

		private static MelonPreferences_Entry<bool> _autoFindPlacedPrefab;

		private static MelonPreferences_Entry<bool> _autoSpawnOnLevelLoad;

		private static MelonPreferences_Entry<string> _bundleFileName;

		private static MelonPreferences_Entry<string> _prefabAssetName;

		private static MelonPreferences_Entry<bool> _fixMaterialsAtRuntime;

		internal static bool Enabled => _enabled?.Value ?? true;

		internal static float DetectionDistance => Mathf.Clamp(_detectionDistance?.Value ?? 45f, 1f, 500f);

		internal static float DetectionAngle => Mathf.Clamp(_detectionAngle?.Value ?? 60f, 1f, 179f);

		internal static bool FaceVisibilityRaycast => _faceVisibilityRaycast?.Value ?? true;

		internal static float RequiredLookDuration => Mathf.Max(0f, _requiredLookDuration?.Value ?? 0.35f);

		internal static float TriggerAnimationDurationFallback => Mathf.Clamp(_triggerAnimationDurationFallback?.Value ?? 30f, 0.1f, 120f);

		internal static float ChaseSpeed => Mathf.Clamp(_chaseSpeed?.Value ?? 14f, 0.5f, 100f);

		internal static float ChaseAcceleration => Mathf.Clamp(_chaseAcceleration?.Value ?? 22f, 0.5f, 500f);

		internal static float TurningSpeed => Mathf.Clamp(_turningSpeed?.Value ?? 540f, 15f, 5000f);

		internal static float NavMeshRepathInterval => Mathf.Clamp(_navMeshRepathInterval?.Value ?? 0.15f, 0.02f, 5f);

		internal static float AttackDistance => Mathf.Clamp(_attackDistance?.Value ?? 1.9f, 0.2f, 20f);

		internal static float AttackCooldown => Mathf.Clamp(_attackCooldown?.Value ?? 2.5f, 0.05f, 60f);

		internal static float DamageAmount => Mathf.Clamp(_damageAmount?.Value ?? 200f, 0f, 100000f);

		internal static bool InstantKillEnabled => _instantKillEnabled?.Value ?? true;

		internal static bool ReturnToPassiveAfterTargetDeath => _returnToPassiveAfterTargetDeath?.Value ?? true;

		internal static bool ChaseAnotherTriggeredPlayer => _chaseAnotherTriggeredPlayer?.Value ?? false;

		internal static bool DirectMovementFallback => _directMovementFallback?.Value ?? true;

		internal static float StuckDetectionTimeout => Mathf.Clamp(_stuckDetectionTimeout?.Value ?? 1.25f, 0.2f, 30f);

		internal static bool DebugLogging => _debugLogging?.Value ?? false;

		internal static bool FusionSupportEnabled => _fusionSupportEnabled?.Value ?? true;

		internal static float FaceFacingAngle => Mathf.Clamp(_faceFacingAngle?.Value ?? 100f, 5f, 360f);

		internal static float FaceHitRadius => Mathf.Clamp(_faceHitRadius?.Value ?? 0.55f, 0.05f, 10f);

		internal static float MinimumDetectionDistance => Mathf.Clamp(_minimumDetectionDistance?.Value ?? 0.35f, 0f, 10f);

		internal static float LookMemoryDecay => Mathf.Clamp(_lookMemoryDecay?.Value ?? 2f, 0f, 50f);

		internal static string OcclusionIgnoreLayers => _occlusionIgnoreLayers?.Value ?? string.Empty;

		internal static PassiveMode PassiveBehaviour
		{
			get
			{
				if ((_passiveMode?.Value ?? 2) == 0)
				{
					return PassiveMode.Stationary;
				}
				return PassiveMode.Wander;
			}
		}

		internal static float PassiveWanderRadius => Mathf.Clamp(_passiveWanderRadius?.Value ?? 6f, 0.5f, 200f);

		internal static float PassiveWanderSpeed => Mathf.Clamp(_passiveWanderSpeed?.Value ?? 1.1f, 0.05f, 20f);

		internal static float PassiveWanderInterval => Mathf.Clamp(_passiveWanderInterval?.Value ?? 6f, 0.5f, 120f);

		internal static bool PassiveAmbientAudio => _passiveAmbientAudio?.Value ?? true;

		internal static float MaxTriggerDuration => Mathf.Max(0f, _maxTriggerDuration?.Value ?? 35f);

		internal static bool UseClipLengthForTrigger => _useClipLengthForTrigger?.Value ?? true;

		internal static float StoppingDistance => Mathf.Clamp(_stoppingDistance?.Value ?? 1.2f, 0f, 20f);

		internal static int ObstacleAvoidanceQuality => Mathf.Clamp(_obstacleAvoidanceQuality?.Value ?? 2, 0, 4);

		internal static float ObstacleAvoidanceRadius => Mathf.Clamp(_obstacleAvoidanceRadius?.Value ?? 0.45f, 0.05f, 5f);

		internal static float StuckMoveThreshold => Mathf.Clamp(_stuckMoveThreshold?.Value ?? 0.35f, 0.01f, 10f);

		internal static float StuckAccelerationBoost => Mathf.Clamp(_stuckAccelerationBoost?.Value ?? 2.5f, 1f, 20f);

		internal static bool StuckTeleportRecovery => _stuckTeleportRecovery?.Value ?? false;

		internal static float StuckTeleportDistance => Mathf.Clamp(_stuckTeleportDistance?.Value ?? 0.4f, 0.01f, 2f);

		internal static float DirectFallbackGravity => Mathf.Clamp(_directFallbackGravity?.Value ?? 22f, 0f, 200f);

		internal static float DirectFallbackStepHeight => Mathf.Clamp(_directFallbackStepHeight?.Value ?? 0.95f, 0f, 5f);

		internal static bool BreakDoors => _breakDoors?.Value ?? true;

		internal static bool AttackRequiresLineOfSight => _attackRequiresLineOfSight?.Value ?? true;

		internal static float AttackWindup => Mathf.Clamp(_attackWindup?.Value ?? 0.25f, 0f, 10f);

		internal static float ResetStateDuration => Mathf.Clamp(_resetStateDuration?.Value ?? 5.5f, 0f, 60f);

		internal static string SpawnKeyName => _spawnKey?.Value ?? "F8";

		internal static string DespawnKeyName => _despawnKey?.Value ?? "F9";

		internal static float SpawnDistance => Mathf.Clamp(_spawnDistance?.Value ?? 6f, 0.5f, 50f);

		internal static int MaxInstances => Mathf.Clamp(_maxInstances?.Value ?? 1, 1, 16);

		internal static bool AutoFindPlacedPrefab => _autoFindPlacedPrefab?.Value ?? true;

		internal static bool AutoSpawnOnLevelLoad => _autoSpawnOnLevelLoad?.Value ?? false;

		internal static string BundleFileName
		{
			get
			{
				if (!string.IsNullOrWhiteSpace(_bundleFileName?.Value))
				{
					return _bundleFileName.Value.Trim();
				}
				return "theshyguy.bundle";
			}
		}

		internal static string PrefabAssetName
		{
			get
			{
				if (!string.IsNullOrWhiteSpace(_prefabAssetName?.Value))
				{
					return _prefabAssetName.Value.Trim();
				}
				return "TheShyGuy";
			}
		}

		internal static bool FixMaterialsAtRuntime => _fixMaterialsAtRuntime?.Value ?? true;

		internal static TargetLostAction TargetLost
		{
			get
			{
				bool chaseAnotherTriggeredPlayer = ChaseAnotherTriggeredPlayer;
				bool returnToPassiveAfterTargetDeath = ReturnToPassiveAfterTargetDeath;
				if (chaseAnotherTriggeredPlayer && returnToPassiveAfterTargetDeath)
				{
					return TargetLostAction.PreferAnotherThenPassive;
				}
				if (chaseAnotherTriggeredPlayer)
				{
					return TargetLostAction.ChaseAnotherTriggeredPlayer;
				}
				return TargetLostAction.ReturnToPassive;
			}
		}

		internal static KeyCode SpawnKey => ParseKey(SpawnKeyName, "SpawnKey");

		internal static KeyCode DespawnKey => ParseKey(DespawnKeyName, "DespawnKey");

		internal static void Initialise()
		{
			if (!_created)
			{
				_created = true;
				_category = MelonPreferences.CreateCategory("TheShyGuy", "The Shy Guy");
				_category.SetFilePath(Path.Combine(MelonEnvironment.UserDataDirectory, "TheShyGuy.cfg"), true, false);
				_enabled = Make("Enabled", value: true, "Master switch. When false the creature stays passive and never triggers.");
				_detectionDistance = Make("DetectionDistance", 45f, "Metres. Beyond this range a player looking at the face is ignored.");
				_detectionAngle = Make("DetectionAngle", 60f, "Full cone angle in degrees, measured from the player's view direction. The face must fall inside this cone. 60 means +/-30 degrees off-centre.");
				_faceVisibilityRaycast = Make("FaceVisibilityRaycast", value: true, "Require an unobstructed line of sight to the face. Turn off only for debugging - with this off, walls and doors stop blocking detection.");
				_requiredLookDuration = Make("RequiredLookDuration", 0.35f, "Seconds a player must hold their gaze on the face before it triggers. 0 triggers on the first frame.");
				_triggerAnimationDurationFallback = Make("TriggerAnimationDurationFallback", 30f, "Seconds to hold the trigger pose when the clip length cannot be read. Defaults to the Rage take length.");
				_chaseSpeed = Make("ChaseSpeed", 14f, "Metres per second at full sprint. SCP-096 is meant to be faster than a player can run; 14 is roughly three times player sprint speed.");
				_chaseAcceleration = Make("ChaseAcceleration", 22f, "Metres per second squared. Lower values make it visibly wind up to top speed instead of snapping to it.");
				_turningSpeed = Make("TurningSpeed", 540f, "Degrees per second of yaw rotation while chasing.");
				_navMeshRepathInterval = Make("NavMeshRepathInterval", 0.15f, "Seconds between destination updates. Low values follow moving players tightly at some CPU cost.");
				_attackDistance = Make("AttackDistance", 1.9f, "Metres from the creature's centre to the target's head at which the attack fires.");
				_attackCooldown = Make("AttackCooldown", 2.5f, "Minimum seconds between two attacks. Prevents the attack firing every frame.");
				_damageAmount = Make("DamageAmount", 200f, "Damage applied per attack when InstantKillEnabled is false.");
				_instantKillEnabled = Make("InstantKillEnabled", value: true, "Kill the player outright instead of applying DamageAmount.");
				_returnToPassiveAfterTargetDeath = Make("ReturnToPassiveAfterTargetDeath", value: true, "When the victim dies, respawns or disconnects, calm down and go back to passive.");
				_chaseAnotherTriggeredPlayer = Make("ChaseAnotherTriggeredPlayer", value: false, "Legacy option. Ignored: only one player may trigger per rage cycle, and losing them always calms The Shy Guy down.");
				_directMovementFallback = Make("DirectMovementFallback", value: true, "If the level has no baked NavMesh, or pathfinding fails, steer directly towards the target instead of standing still. Required for most BONELAB levels - see the README.");
				_stuckDetectionTimeout = Make("StuckDetectionTimeout", 1.25f, "Seconds of trying to move without making progress before stuck recovery kicks in.");
				_debugLogging = Make("DebugLogging", value: false, "Verbose console output for detection, pathing and state changes. Repeating messages are rate limited.");
				_fusionSupportEnabled = Make("FusionSupportEnabled", value: true, "Use LabFusion for remote player discovery and identity when Fusion is installed. Has no effect when Fusion is absent.");
				_faceFacingAngle = Make("FaceFacingAngle", 100f, "Full cone angle in degrees around the face's own forward axis. The player must be standing inside this cone for the gaze to count, which is what stops the back and sides of the head from triggering it.");
				_faceHitRadius = Make("FaceHitRadius", 0.55f, "Metres. A line-of-sight hit on the creature only counts as 'saw the face' if it lands within this distance of the face point. This is what stops looking at the body or legs from triggering it.");
				_minimumDetectionDistance = Make("MinimumDetectionDistance", 0.35f, "Metres. Gazes closer than this are ignored so a camera clipping through the head does not self-trigger.");
				_lookMemoryDecay = Make("LookMemoryDecay", 2f, "How fast accumulated look time drains when a player looks away, as a multiple of real time. 2 means half a second of looking is forgotten in a quarter second.");
				_occlusionIgnoreLayers = Make("OcclusionIgnoreLayers", "Player,PlayerAvatar,Trigger,NoCollide,Hand,Interactable,UI,IgnoreRaycast", "Comma separated Unity layer names excluded from the line-of-sight raycast. Names that do not exist in the loaded level are skipped silently.");
				_passiveMode = Make("PassiveMode", 2, "0 = stand perfectly still, 1 = stand still and play the idle animation, 2 = wander slowly near the spawn point (default).");
				_passiveWanderRadius = Make("PassiveWanderRadius", 6f, "Metres from the spawn point when PassiveMode is 2 (Wander).");
				_passiveWanderSpeed = Make("PassiveWanderSpeed", 1.1f, "Metres per second while wandering passively.");
				_passiveWanderInterval = Make("PassiveWanderInterval", 6f, "Seconds between picking a new passive wander destination.");
				_passiveAmbientAudio = Make("PassiveAmbientAudio", value: true, "Loop the ambience clip on the ambient audio source while passive.");
				_maxTriggerDuration = Make("MaxTriggerDuration", 35f, "Hard cap in seconds on how long the creature stays frozen in the trigger animation. Covers the full Rage take (~30s). Set to 0 to disable the cap.");
				_useClipLengthForTrigger = Make("UseClipLengthForTrigger", value: true, "Read the real length of the trigger animation and wait exactly that long (still clamped by MaxTriggerDuration). When false, always use TriggerAnimationDurationFallback.");
				_stoppingDistance = Make("StoppingDistance", 1.2f, "NavMeshAgent stopping distance. Keep below AttackDistance or it will stop short of attack range.");
				_obstacleAvoidanceQuality = Make("ObstacleAvoidanceQuality", 2, "NavMeshAgent avoidance: 0 = none, 1 = low, 2 = medium, 3 = good, 4 = high. Lower is cheaper and less prone to the agent refusing to move in tight corridors.");
				_obstacleAvoidanceRadius = Make("ObstacleAvoidanceRadius", 0.45f, "Metres. Agent radius, and the sweep radius used by the direct-movement fallback.");
				_stuckMoveThreshold = Make("StuckMoveThreshold", 0.35f, "Metres of progress required within StuckDetectionTimeout to be considered 'not stuck'.");
				_stuckAccelerationBoost = Make("StuckAccelerationBoost", 2.5f, "Acceleration multiplier applied for one second during stuck recovery stage 2.");
				_stuckTeleportRecovery = Make("StuckTeleportRecovery", value: false, "Allow stuck recovery stage 4: a very small teleport along the desired direction. Off by default - it feels like sliding.");
				_stuckTeleportDistance = Make("StuckTeleportDistance", 0.4f, "Metres of last-resort teleport. Deliberately tiny - this nudges through a lip of geometry, it is not a blink.");
				_directFallbackGravity = Make("DirectFallbackGravity", 22f, "Metres per second squared of downward pull used by the direct-movement fallback to keep the creature on the floor.");
				_directFallbackStepHeight = Make("DirectFallbackStepHeight", 0.95f, "Metres. Maximum ledge/stair the direct-movement fallback will step up.");
				_breakDoors = Make("BreakDoors", value: true, "Play the door-forcing animation and sound when a door-like collider blocks the path during a chase.");
				_attackRequiresLineOfSight = Make("AttackRequiresLineOfSight", value: true, "Require a clear line to the target before the attack lands, so it cannot kill through a wall it is pressed against.");
				_attackWindup = Make("AttackWindup", 0.25f, "Seconds between the attack animation starting and the damage landing.");
				_resetStateDuration = Make("ResetStateDuration", 5.5f, "Seconds spent in the Resetting state calming down before returning to passive. Defaults to the length of the supplied calmdown clip.");
				_spawnKey = Make("SpawnKey", "F8", "UnityEngine.KeyCode name. Spawns The Shy Guy in front of the local player. Empty string disables the shortcut.");
				_despawnKey = Make("DespawnKey", "F9", "UnityEngine.KeyCode name. Despawns every Shy Guy. Empty string disables the shortcut.");
				_spawnDistance = Make("SpawnDistance", 6f, "Metres in front of the local player to spawn at when using the shortcut or console command.");
				_maxInstances = Make("MaxInstances", 1, "How many Shy Guys may exist at once. Spawning past this limit despawns the oldest, which is what stops duplicates accumulating across level loads.");
				_autoFindPlacedPrefab = Make("AutoFindPlacedPrefab", value: true, "After a level loads, look for a GameObject named TheShyGuy that was placed in the level itself and adopt it.");
				_autoSpawnOnLevelLoad = Make("AutoSpawnOnLevelLoad", value: false, "Spawn one automatically shortly after every level loads. Off by default - use the keybind instead.");
				_bundleFileName = Make("BundleFileName", "theshyguy.bundle", "File name inside UserData/TheShyGuy/ to load the prefab from. Ignored when the bundle is embedded in the DLL.");
				_prefabAssetName = Make("PrefabAssetName", "TheShyGuy", "Asset name to load from the bundle. Matches TheShyGuy.prefab.");
				_fixMaterialsAtRuntime = Make("FixMaterialsAtRuntime", value: true, "Re-point bundle materials at the shaders present in the running game. Fixes the magenta 'missing shader' look when the bundle was built against a different render pipeline.");
				ShyGuyLog.Msg("Preferences ready (" + MelonEnvironment.UserDataDirectory + "\\TheShyGuy.cfg).");
			}
		}

		private static MelonPreferences_Entry<T> Make<T>(string id, T value, string description)
		{
			return _category.CreateEntry<T>(id, value, id, description, false, false, (ValueValidator)null, (string)null);
		}

		internal static void Save()
		{
			try
			{
				MelonPreferences_Category category = _category;
				if (category != null)
				{
					category.SaveToFile(false);
				}
			}
			catch (Exception ex)
			{
				ShyGuyLog.Debug("Could not save preferences: " + ex.Message);
			}
		}

		private static KeyCode ParseKey(string name, string settingId)
		{
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			if (string.IsNullOrWhiteSpace(name))
			{
				return (KeyCode)0;
			}
			if (Enum.TryParse<KeyCode>(name.Trim(), ignoreCase: true, out KeyCode result))
			{
				return result;
			}
			ShyGuyLog.WarnOnce("badkey:" + settingId, "Preference " + settingId + " is set to '" + name + "', which is not a UnityEngine.KeyCode name. The shortcut is disabled. Valid examples: F8, Backslash, Keypad0.");
			return (KeyCode)0;
		}
	}
	internal sealed class ShyGuyPrefabConfig
	{
		internal const string TextAssetName = "TheShyGuyConfig";

		public string faceDetectionPointPath = "FaceDetectionPoint";

		public string headBonePath = "";

		public string animatorPath = "";

		public string mainColliderPath = "MainCollider";

		public string triggerColliderPath = "TriggerCollider";

		public string triggerAudioSourcePath = "Audio/TriggerAudioSource";

		public string chaseAudioSourcePath = "Audio/ChaseAudioSource";

		public string attackAudioSourcePath = "Audio/AttackAudioSource";

		public string ambientAudioSourcePath = "Audio/AmbientAudioSource";

		public bool useLegacyAnimation;

		public string idleState = "Idle";

		public string triggerState = "Panic";

		public string runState = "Run";

		public string attackState = "Kill";

		public string resetState = "Idle";

		public string doorState = "ForceOpenDoor";

		public string walkState = "Walk";

		public string introState = "Intro";

		public int animatorLayer;

		public float animationCrossFade = 0.12f;

		public float triggerClipLength;

		public string triggerClipName = "voicemod_scream";

		public string chaseClipName = "scream";

		public string attackClipName = "096 kill";

		public string ambientClipName = "ambiance";

		public string resetClipName = "calmdown";

		public string doorClipName = "dooropen";

		public float triggerVolume = 1f;

		public float chaseVolume = 1f;

		public float attackVolume = 1f;

		public float ambientVolume = 0.45f;

		public float audioMaxDistance = 60f;

		public float modelScale = 1f;

		public float height = 2.4f;

		public float fallbackFaceHeight = 2.05f;

		public float fallbackFaceForward = 0.16f;

		internal static readonly string[] FacePointNames = new string[6] { "FaceDetectionPoint", "FacePoint", "Face", "FaceDetection", "HeadPoint", "EyePoint" };

		internal static readonly string[] HeadBoneNames = new string[6] { "Head", "head", "Bip01 Head", "mixamorig:Head", "Neck", "Skull" };

		internal static readonly string[] AudioParentNames = new string[3] { "Audio", "Sounds", "SFX" };

		internal static readonly Dictionary<string, string[]> StateNameCandidates = new Dictionary<string, string[]>(StringComparer.OrdinalIgnoreCase)
		{
			{
				"idle",
				new string[4] { "Idle", "idle", "@idle", "scp096.mdl/@idle" }
			},
			{
				"trigger",
				new string[7] { "Panic", "Rage", "@Rage", "scp096.mdl/@Rage", "Trigger", "intro", "@intro" }
			},
			{
				"run",
				new string[5] { "Run", "run", "@run", "scp096.mdl/@run", "Chase" }
			},
			{
				"attack",
				new string[4] { "Kill", "@Kill", "scp096.mdl/@Kill", "Attack" }
			},
			{
				"reset",
				new string[3] { "Idle", "idle", "@idle" }
			},
			{
				"door",
				new string[4] { "ForceOpenDoor", "@ForceOpenDoor", "scp096.mdl/@ForceOpenDoor", "DoorOpen" }
			},
			{
				"walk",
				new string[4] { "Walk", "walk", "@walk", "scp096.mdl/@walk" }
			},
			{
				"intro",
				new string[4] { "Intro", "intro", "@intro", "scp096.mdl/@intro" }
			}
		};

		internal static ShyGuyPrefabConfig Parse(string json, string sourceDescription)
		{
			ShyGuyPrefabConfig shyGuyPrefabConfig = new ShyGuyPrefabConfig();
			if (string.IsNullOrWhiteSpace(json))
			{
				ShyGuyLog.Debug("No baked prefab config found (" + sourceDescription + "); using built-in defaults.");
				return shyGuyPrefabConfig;
			}
			try
			{
				Dictionary<string, string> dictionary = ShyGuyJson.ParseFlatObject(json);
				if (dictionary.Count == 0)
				{
					ShyGuyLog.Warn("The baked prefab config at " + sourceDescription + " contained no readable values; using defaults.");
					return shyGuyPrefabConfig;
				}
				ShyGuyPrefabConfig shyGuyPrefabConfig2 = new ShyGuyPrefabConfig
				{
					faceDetectionPointPath = ShyGuyJson.GetString(dictionary, "faceDetectionPointPath", shyGuyPrefabConfig.faceDetectionPointPath),
					headBonePath = ShyGuyJson.GetString(dictionary, "headBonePath", shyGuyPrefabConfig.headBonePath),
					animatorPath = ShyGuyJson.GetString(dictionary, "animatorPath", shyGuyPrefabConfig.animatorPath),
					mainColliderPath = ShyGuyJson.GetString(dictionary, "mainColliderPath", shyGuyPrefabConfig.mainColliderPath),
					triggerColliderPath = ShyGuyJson.GetString(dictionary, "triggerColliderPath", shyGuyPrefabConfig.triggerColliderPath),
					triggerAudioSourcePath = ShyGuyJson.GetString(dictionary, "triggerAudioSourcePath", shyGuyPrefabConfig.triggerAudioSourcePath),
					chaseAudioSourcePath = ShyGuyJson.GetString(dictionary, "chaseAudioSourcePath", shyGuyPrefabConfig.chaseAudioSourcePath),
					attackAudioSourcePath = ShyGuyJson.GetString(dictionary, "attackAudioSourcePath", shyGuyPrefabConfig.attackAudioSourcePath),
					ambientAudioSourcePath = ShyGuyJson.GetString(dictionary, "ambientAudioSourcePath", shyGuyPrefabConfig.ambientAudioSourcePath),
					useLegacyAnimation = ShyGuyJson.GetBool(dictionary, "useLegacyAnimation", shyGuyPrefabConfig.useLegacyAnimation),
					idleState = ShyGuyJson.GetString(dictionary, "idleState", shyGuyPrefabConfig.idleState),
					triggerState = ShyGuyJson.GetString(dictionary, "triggerState", shyGuyPrefabConfig.triggerState),
					runState = ShyGuyJson.GetString(dictionary, "runState", shyGuyPrefabConfig.runState),
					attackState = ShyGuyJson.GetString(dictionary, "attackState", shyGuyPrefabConfig.attackState),
					resetState = ShyGuyJson.GetString(dictionary, "resetState", shyGuyPrefabConfig.resetState),
					doorState = ShyGuyJson.GetString(dictionary, "doorState", shyGuyPrefabConfig.doorState),
					walkState = ShyGuyJson.GetString(dictionary, "walkState", shyGuyPrefabConfig.walkState),
					introState = ShyGuyJson.GetString(dictionary, "introState", shyGuyPrefabConfig.introState),
					animatorLayer = ShyGuyJson.GetInt(dictionary, "animatorLayer", shyGuyPrefabConfig.animatorLayer),
					animationCrossFade = ShyGuyJson.GetFloat(dictionary, "animationCrossFade", shyGuyPrefabConfig.animationCrossFade),
					triggerClipLength = ShyGuyJson.GetFloat(dictionary, "triggerClipLength", shyGuyPrefabConfig.triggerClipLength),
					triggerClipName = ShyGuyJson.GetString(dictionary, "triggerClipName", shyGuyPrefabConfig.triggerClipName),
					chaseClipName = ShyGuyJson.GetString(dictionary, "chaseClipName", shyGuyPrefabConfig.chaseClipName),
					attackClipName = ShyGuyJson.GetString(dictionary, "attackClipName", shyGuyPrefabConfig.attackClipName),
					ambientClipName = ShyGuyJson.GetString(dictionary, "ambientClipName", shyGuyPrefabConfig.ambientClipName),
					resetClipName = ShyGuyJson.GetString(dictionary, "resetClipName", shyGuyPrefabConfig.resetClipName),
					doorClipName = ShyGuyJson.GetString(dictionary, "doorClipName", shyGuyPrefabConfig.doorClipName),
					triggerVolume = ShyGuyJson.GetFloat(dictionary, "triggerVolume", shyGuyPrefabConfig.triggerVolume),
					chaseVolume = ShyGuyJson.GetFloat(dictionary, "chaseVolume", shyGuyPrefabConfig.chaseVolume),
					attackVolume = ShyGuyJson.GetFloat(dictionary, "attackVolume", shyGuyPrefabConfig.attackVolume),
					ambientVolume = ShyGuyJson.GetFloat(dictionary, "ambientVolume", shyGuyPrefabConfig.ambientVolume),
					audioMaxDistance = ShyGuyJson.GetFloat(dictionary, "audioMaxDistance", shyGuyPrefabConfig.audioMaxDistance),
					modelScale = ShyGuyJson.GetFloat(dictionary, "modelScale", shyGuyPrefabConfig.modelScale),
					height = ShyGuyJson.GetFloat(dictionary, "height", shyGuyPrefabConfig.height),
					fallbackFaceHeight = ShyGuyJson.GetFloat(dictionary, "fallbackFaceHeight", shyGuyPrefabConfig.fallbackFaceHeight),
					fallbackFaceForward = ShyGuyJson.GetFloat(dictionary, "fallbackFaceForward", shyGuyPrefabConfig.fallbackFaceForward)
				};
				shyGuyPrefabConfig2.Sanitise(shyGuyPrefabConfig);
				ShyGuyLog.Msg("Loaded baked prefab config from " + sourceDescription + " (" + dictionary.Count + " value(s)).");
				return shyGuyPrefabConfig2;
			}
			catch (Exception ex)
			{
				ShyGuyLog.Warn("Could not parse the baked prefab config at " + sourceDescription + " (" + ex.Message + "). Using built-in defaults instead.");
				return shyGuyPrefabConfig;
			}
		}

		private void Sanitise(ShyGuyPrefabConfig d)
		{
			if (modelScale <= 0.0001f)
			{
				modelScale = d.modelScale;
			}
			if (height <= 0.1f)
			{
				height = d.height;
			}
			if (animatorLayer < 0)
			{
				animatorLayer = 0;
			}
			if (animationCrossFade < 0f)
			{
				animationCrossFade = 0f;
			}
			if (triggerClipLength < 0f)
			{
				triggerClipLength = 0f;
			}
			if (audioMaxDistance <= 0.5f)
			{
				audioMaxDistance = d.audioMaxDistance;
			}
			if (string.IsNullOrWhiteSpace(triggerClipName))
			{
				triggerClipName = "voicemod_scream";
			}
			if (string.IsNullOrWhiteSpace(chaseClipName) || chaseClipName.IndexOf("voicemod", StringComparison.OrdinalIgnoreCase) >= 0)
			{
				chaseClipName = "scream";
			}
			if (string.Equals(resetState, "HoldItIn", StringComparison.OrdinalIgnoreCase))
			{
				resetState = "Idle";
			}
			if (string.IsNullOrWhiteSpace(introState))
			{
				introState = "Intro";
			}
			triggerVolume = Mathf.Clamp01((triggerVolume <= 0f) ? d.triggerVolume : triggerVolume);
			chaseVolume = Mathf.Clamp01((chaseVolume <= 0f) ? d.chaseVolume : chaseVolume);
			attackVolume = Mathf.Clamp01((attackVolume <= 0f) ? d.attackVolume : attackVolume);
			ambientVolume = Mathf.Clamp01((ambientVolume < 0f) ? d.ambientVolume : ambientVolume);
			faceDetectionPointPath = Trim(faceDetectionPointPath);
			headBonePath = Trim(headBonePath);
			animatorPath = Trim(animatorPath);
			mainColliderPath = Trim(mainColliderPath);
			triggerColliderPath = Trim(triggerColliderPath);
			triggerAudioSourcePath = Trim(triggerAudioSourcePath);
			chaseAudioSourcePath = Trim(chaseAudioSourcePath);
			attackAudioSourcePath = Trim(attackAudioSourcePath);
			ambientAudioSourcePath = Trim(ambientAudioSourcePath);
		}

		private static string Trim(string s)
		{
			if (!string.IsNullOrWhiteSpace(s))
			{
				return s.Trim();
			}
			return string.Empty;
		}
	}
	internal static class ShyGuyHierarchy
	{
		internal static Transform FindByPath(Transform root, string path)
		{
			if ((Object)(object)root == (Object)null || string.IsNullOrWhiteSpace(path))
			{
				return null;
			}
			string text = path.Trim().Trim('/');
			if (text.Length == 0)
			{
				return null;
			}
			Transform val = null;
			try
			{
				val = root.Find(text);
			}
			catch (Exception ex)
			{
				ShyGuyLog.Debug("Transform.Find('" + text + "') threw: " + ex.Message);
			}
			if ((Object)(object)val != (Object)null)
			{
				return val;
			}
			int num = text.LastIndexOf('/');
			string name = ((num >= 0) ? text.Substring(num + 1) : text);
			Transform val2 = FindByName(root, name);
			if ((Object)(object)val2 != (Object)null)
			{
				ShyGuyLog.Debug("Resolved '" + path + "' by name search to '" + GetPath(root, val2) + "'.");
			}
			return val2;
		}

		internal static Transform FindByName(Transform root, string name)
		{
			if ((Object)(object)root == (Object)null || string.IsNullOrWhiteSpace(name))
			{
				return null;
			}
			return Search(root, name, StringComparison.Ordinal) ?? Search(root, name, StringComparison.OrdinalIgnoreCase);
		}

		internal static Transform FindFirstByNames(Transform root, IEnumerable<string> names)
		{
			if ((Object)(object)root == (Object)null || names == null)
			{
				return null;
			}
			foreach (string name in names)
			{
				Transform val = FindByName(root, name);
				if ((Object)(object)val != (Object)null)
				{
					return val;
				}
			}
			return null;
		}

		private static Transform Search(Transform current, string name, StringComparison mode)
		{
			if ((Object)(object)current == (Object)null)
			{
				return null;
			}
			int childCount = current.childCount;
			for (int i = 0; i < childCount; i++)
			{
				Transform val = null;
				try
				{
					val = current.GetChild(i);
				}
				catch
				{
					continue;
				}
				if (!((Object)(object)val == (Object)null))
				{
					if (string.Equals(((Object)val).name, name, mode))
					{
						return val;
					}
					Transform val2 = Search(val, name, mode);
					if ((Object)(object)val2 != (Object)null)
					{
						return val2;
					}
				}
			}
			return null;
		}

		internal static string GetPath(Transform root, Transform target)
		{
			if ((Object)(object)target == (Object)null)
			{
				return "(null)";
			}
			if ((Object)(object)root == (Object)null || (Object)(object)target == (Object)(object)root)
			{
				return ((Object)target).name;
			}
			List<string> list = new List<string>();
			Transform val = target;
			int num = 0;
			while ((Object)(object)val != (Object)null && (Object)(object)val != (Object)(object)root && num++ < 64)
			{
				list.Add(((Object)val).name);
				val = val.parent;
			}
			list.Reverse();
			return string.Join("/", list);
		}

		internal static Transform EnsureChild(Transform parent, string name)
		{
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Expected O, but got Unknown
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)parent == (Object)null)
			{
				return null;
			}
			Transform val = FindByName(parent, name);
			if ((Object)(object)val != (Object)null)
			{
				return val;
			}
			GameObject val2 = new GameObject(name);
			val2.transform.SetParent(parent, false);
			val2.transform.localPosition = Vector3.zero;
			val2.transform.localRotation = Quaternion.identity;
			val2.transform.localScale = Vector3.one;
			ShyGuyLog.Debug("Created missing child '" + name + "' under '" + ((Object)parent).name + "'.");
			return val2.transform;
		}

		internal static T ResolveComponent<T>(Transform root, string path, IEnumerable<string> nameCandidates) where T : Component
		{
			if ((Object)(object)root == (Object)null)
			{
				return default(T);
			}
			Transform val = FindByPath(root, path);
			if ((Object)(object)val != (Object)null)
			{
				T component = ((Component)val).GetComponent<T>();
				if ((Object)(object)component != (Object)null)
				{
					return component;
				}
			}
			if (nameCandidates != null)
			{
				foreach (string nameCandidate in nameCandidates)
				{
					Transform val2 = FindByName(root, nameCandidate);
					if (!((Object)(object)val2 == (Object)null))
					{
						T component2 = ((Component)val2).GetComponent<T>();
						if ((Object)(object)component2 != (Object)null)
						{
							return component2;
						}
					}
				}
			}
			try
			{
				T componentInChildren = ((Component)root).GetComponentInChildren<T>(true);
				if ((Object)(object)componentInChildren != (Object)null)
				{
					return componentInChildren;
				}
			}
			catch (Exception ex)
			{
				ShyGuyLog.Debug("GetComponentInChildren<" + typeof(T).Name + "> threw: " + ex.Message);
			}
			return default(T);
		}

		internal static bool IsPartOf(Transform root, Transform candidate)
		{
			if ((Object)(object)root == (Object)null || (Object)(object)candidate == (Object)null)
			{
				return false;
			}
			Transform val = candidate;
			int num = 0;
			while ((Object)(object)val != (Object)null && num++ < 128)
			{
				if ((Object)(object)val == (Object)(object)root)
				{
					return true;
				}
				val = val.parent;
			}
			return false;
		}

		internal static Camera FindCamera(Transform root)
		{
			if ((Object)(object)root == (Object)null)
			{
				return null;
			}
			try
			{
				Il2CppArrayBase<Camera> componentsInChildren = ((Component)root).GetComponentsInChildren<Camera>(false);
				if (componentsInChildren == null)
				{
					return null;
				}
				for (int i = 0; i < componentsInChildren.Length; i++)
				{
					Camera val = componentsInChildren[i];
					if ((Object)(object)val != (Object)null && ((Behaviour)val).enabled && ((Component)val).gameObject.activeInHierarchy)
					{
						return val;
					}
				}
			}
			catch (Exception ex)
			{
				ShyGuyLog.Debug("FindCamera threw: " + ex.Message);
			}
			return null;
		}

		internal static void DumpHierarchy(Transform root, int maxDepth = 4)
		{
			if (!((Object)(object)root == (Object)null))
			{
				Dump(root, 0, maxDepth);
			}
		}

		private static void Dump(Transform t, int depth, int maxDepth)
		{
			if ((Object)(object)t == (Object)null || depth > maxDepth)
			{
				return;
			}
			List<string> list = new List<string>();
			try
			{
				Il2CppArrayBase<Component> components = ((Component)t).GetComponents<Component>();
				if (components != null)
				{
					foreach (Component item in components)
					{
						if ((Object)(object)item == (Object)null)
						{
							list.Add("<missing script>");
						}
						else
						{
							list.Add(((object)item).GetType().Name);
						}
					}
				}
			}
			catch
			{
			}
			ShyGuyLog.Msg(new string(' ', depth * 2) + "- " + ((Object)t).name + "  [" + string.Join(", ", list) + "]");
			for (int i = 0; i < t.childCount; i++)
			{
				Dump(t.GetChild(i), depth + 1, maxDepth);
			}
		}
	}
	internal static class ShyGuyInput
	{
		private static bool _resolved;

		private static bool _hasInputSystem;

		private static Type _keyboardType;

		private static PropertyInfo _keyboardCurrent;

		private static Type _keyEnum;

		private static void EnsureResolved()
		{
			if (_resolved)
			{
				return;
			}
			_resolved = true;
			try
			{
				_keyboardType = ShyGuyReflect.FindType("UnityEngine.InputSystem.Keyboard", new string[1] { "UnityEngine.InputSystem.Keyboard" });
				if (!(_keyboardType == null))
				{
					_keyboardCurrent = _keyboardType.GetProperty("current", BindingFlags.Static | BindingFlags.Public);
					_keyEnum = ShyGuyReflect.FindType("UnityEngine.InputSystem.Key", new string[1] { "UnityEngine.InputSystem.Key" });
					_hasInputSystem = _keyboardCurrent != null && _keyEnum != null;
					if (_hasInputSystem)
					{
						ShyGuyLog.Msg("Input System detected - spawn shortcuts will also use Keyboard.current.");
					}
				}
			}
			catch (Exception ex)
			{
				ShyGuyLog.Debug("Input System probe failed: " + ex.Message);
				_hasInputSystem = false;
			}
		}

		internal static bool GetKeyDown(KeyCode key)
		{
			//IL_0000: 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)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			if ((int)key == 0)
			{
				return false;
			}
			try
			{
				if (Input.GetKeyDown(key))
				{
					return true;
				}
			}
			catch
			{
			}
			return GetKeyDownInputSystem(key);
		}

		internal static bool GetKey(KeyCode key)
		{
			//IL_0000: 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)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			if ((int)key == 0)
			{
				return false;
			}
			try
			{
				if (Input.GetKey(key))
				{
					return true;
				}
			}
			catch
			{
			}
			return GetKeyInputSystem(key, wasPressedOnly: false);
		}

		private static bool GetKeyDownInputSystem(KeyCode key)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			return GetKeyInputSystem(key, wasPressedOnly: true);
		}

		private static bool GetKeyInputSystem(KeyCode key, bool wasPressedOnly)
		{
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			EnsureResolved();
			if (!_hasInputSystem)
			{
				return false;
			}
			try
			{
				object value = _keyboardCurrent.GetValue(null);
				if (value == null)
				{
					return false;
				}
				if (!TryMapKeyCode(key, out var inputSystemKey))
				{
					return false;
				}
				object obj = null;
				PropertyInfo property = _keyboardType.GetProperty("Item", new Type[1] { _keyEnum });
				if (property != null)
				{
					obj = property.GetValue(value, new object[1] { inputSystemKey });
				}
				if (obj == null)
				{
					return false;
				}
				string name = (wasPressedOnly ? "wasPressedThisFrame" : "isPressed");
				PropertyInfo property2 = obj.GetType().GetProperty(name, BindingFlags.Instance | BindingFlags.Public);
				if (property2 == null)
				{
					return false;
				}
				object value2 = property2.GetValue(obj);
				bool flag = default(bool);
				int num;
				if (value2 is bool)
				{
					flag = (bool)value2;
					num = 1;
				}
				else
				{
					num = 0;
				}
				return (byte)((uint)num & (flag ? 1u : 0u)) != 0;
			}
			catch (Exception ex)
			{
				ShyGuyLog.DebugThrottled("input:system", "Input System key read failed: " + ex.Message, 30f);
				return false;
			}
		}

		private unsafe static bool TryMapKeyCode(KeyCode key, out object inputSystemKey)
		{
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Invalid comparison between Unknown and I4
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: Invalid comparison between Unknown and I4
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Invalid comparison between Unknown and I4
			//IL_00c9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cf: Invalid comparison between Unknown and I4
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: Invalid comparison between Unknown and I4
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Invalid comparison between Unknown and I4
			//IL_00d4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00da: Invalid comparison between Unknown and I4
			//IL_0050: 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_00c4: Expected I4, but got Unknown
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: Invalid comparison between Unknown and I4
			inputSystemKey = null;
			if (_keyEnum == null)
			{
				return false;
			}
			string text;
			if ((int)key <= 256)
			{
				if ((int)key != 92)
				{
					if ((int)key != 112)
					{
						if ((int)key != 256)
						{
							goto IL_019b;
						}
						text = "Numpad0";
					}
					else
					{
						text = "P";
					}
				}
				else
				{
					text = "Backslash";
				}
			}
			else if ((int)key <= 293)
			{
				if ((int)key != 257)
				{
					switch (key - 268)
					{
					case 14:
						break;
					case 15:
						goto IL_00ec;
					case 16:
						goto IL_00f7;
					case 17:
						goto IL_0102;
					case 18:
						goto IL_010d;
					case 19:
						goto IL_0118;
					case 20:
						goto IL_0123;
					case 21:
						goto IL_012b;
					case 22:
						goto IL_0133;
					case 23:
						goto IL_013b;
					case 24:
						goto IL_0143;
					case 25:
						goto IL_014b;
					case 9:
						goto IL_0163;
					case 0:
						goto IL_017b;
					case 2:
						goto IL_0183;
					default:
						goto IL_019b;
					}
					text = "F1";
				}
				else
				{
					text = "Numpad1";
				}
			}
			else if ((int)key != 303)
			{
				if ((int)key != 304)
				{
					goto IL_019b;
				}
				text = "LeftShift";
			}
			else
			{
				text = "RightShift";
			}
			goto IL_01a9;
			IL_0118:
			text = "F6";
			goto IL_01a9;
			IL_012b:
			text = "F8";
			goto IL_01a9;
			IL_0133:
			text = "F9";
			goto IL_01a9;
			IL_01a9:
			string value = text;
			try
			{
				inputSystemKey = Enum.Parse(_keyEnum, value, ignoreCase: true);
				return true;
			}
			catch
			{
				return false;
			}
			IL_019b:
			text = ((object)(*(KeyCode*)(&key))/*cast due to .constrained prefix*/).ToString();
			goto IL_01a9;
			IL_010d:
			text = "F5";
			goto IL_01a9;
			IL_013b:
			text = "F10";
			goto IL_01a9;
			IL_0143:
			text = "F11";
			goto IL_01a9;
			IL_014b:
			text = "F12";
			goto IL_01a9;
			IL_017b:
			text = "NumpadMultiply";
			goto IL_01a9;
			IL_0183:
			text = "NumpadPlus";
			goto IL_01a9;
			IL_0163:
			text = "Insert";
			goto IL_01a9;
			IL_0123:
			text = "F7";
			goto IL_01a9;
			IL_00ec:
			text = "F2";
			goto IL_01a9;
			IL_00f7:
			text = "F3";
			goto IL_01a9;
			IL_0102:
			text = "F4";
			goto IL_01a9;
		}
	}
	internal static class ShyGuyJson
	{
		internal static Dictionary<string, string> ParseFlatObject(string json)
		{
			Dictionary<string, string> dictionary = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
			if (string.IsNullOrWhiteSpace(json))
			{
				return dictionary;
			}
			int i = 0;
			SkipWhitespace(json, ref i);
			if (i >= json.Length || json[i] != '{')
			{
				ShyGuyLog.Debug("JSON does not start with '{'; ignoring it.");
				return dictionary;
			}
			i++;
			while (i < json.Length)
			{
				SkipWhitespace(json, ref i);
				if (i >= json.Length || json[i] == '}')
				{
					break;
				}
				if (json[i] == ',')
				{
					i++;
					continue;
				}
				if (json[i] != '"')
				{
					ShyGuyLog.Debug("Unexpected character '" + json[i] + "' at index " + i + " while reading a JSON key.");
					break;
				}
				string text = ReadString(json, ref i);
				if (text == null)
				{
					break;
				}
				SkipWhitespace(json, ref i);
				if (i >= json.Length || json[i] != ':')
				{
					ShyGuyLog.Debug("Missing ':' after the JSON key '" + text + "'.");
					break;
				}
				i++;
				SkipWhitespace(json, ref i);
				if (i >= json.Length)
				{
					break;
				}
				switch (json[i])
				{
				case '"':
					break;
				case '[':
				case '{':
					SkipContainer(json, ref i);
					continue;
				default:
				{
					string text2 = ReadLiteral(json, ref i);
					if (text2.Length > 0)
					{
						dictionary[text] = text2;
					}
					continue;
				}
				}
				string text3 = ReadString(json, ref i);
				if (text3 == null)
				{
					break;
				}
				dictionary[text] = text3;
			}
			return dictionary;
		}

		private static void SkipWhitespace(string s, ref int i)
		{
			while (i < s.Length && char.IsWhiteSpace(s[i]))
			{
				i++;
			}
		}

		private static string ReadString(string s, ref int i)
		{
			if (i >= s.Length || s[i] != '"')
			{
				return null;
			}
			i++;
			StringBuilder stringBuilder = new StringBuilder();
			while (i < s.Length)
			{
				char c = s[i];
				switch (c)
				{
				case '\\':
				{
					i++;
					if (i >= s.Length)
					{
						return null;
					}
					char c2 = s[i];
					switch (c2)
					{
					case '"':
						stringBuilder.Append('"');
						break;
					case '\\':
						stringBuilder.Append('\\');
						break;
					case '/':
						stringBuilder.Append('/');
						break;
					case 'b':
						stringBuilder.Append('\b');
						break;
					case 'f':
						stringBuilder.Append('\f');
						break;
					case 'n':
						stringBuilder.Append('\n');
						break;
					case 'r':
						stringBuilder.Append('\r');
						break;
					case 't':
						stringBuilder.Append('\t');
						break;
					case 'u':
					{
						if (i + 4 < s.Length && ushort.TryParse(s.Substring(i + 1, 4), NumberStyles.HexNumber, CultureInfo.InvariantCulture, out var result))
						{
							stringBuilder.Append((char)result);
							i += 4;
						}
						break;
					}
					default:
						stringBuilder.Append(c2);
						break;
					}
					i++;
					break;
				}
				case '"':
					i++;
					return stringBuilder.ToString();
				default:
					stringBuilder.Append(c);
					i++;
					break;
				}
			}
			return null;
		}

		private static string ReadLiteral(string s, ref int i)
		{
			int num = i;
			while (i < s.Length)
			{
				char c = s[i];
				if (c == ',' || c == '}' || c == ']' || char.IsWhiteSpace(c))
				{
					break;
				}
				i++;
			}
			return s.Substring(num, i - num).Trim();
		}

		private static void SkipContainer(string s, ref int i)
		{
			char c = s[i];
			char c2 = ((c == '{') ? '}' : ']');
			int num = 0;
			while (i < s.Length)
			{
				char c3 = s[i];
				if (c3 == '"')
				{
					ReadString(s, ref i);
					continue;
				}
				if (c3 == c)
				{
					num++;
				}
				else if (c3 == c2)
				{
					num--;
					if (num == 0)
					{
						i++;
						break;
					}
				}
				i++;
			}
		}

		internal static string GetString(Dictionary<string, string> map, string key, string fallback)
		{
			if (map == null || !map.TryGetValue(key, out var value))
			{
				return fallback;
			}
			if (value == null || string.Equals(value, "null", StringComparison.OrdinalIgnoreCase))
			{
				return fallback;
			}
			return value;
		}

		internal static bool GetBool(Dictionary<string, string> map, string key, bool fallback)
		{
			if (map == null || !map.TryGetValue(key, out var value))
			{
				return fallback;
			}
			if (bool.TryParse(value, out var result))
			{
				return result;
			}
			if (value == "1")
			{
				return true;
			}
			if (value == "0")
			{
				return false;
			}
			ShyGuyLog.Debug("Could not read '" + key + "' as a boolean (value: '" + value + "'); using " + fallback + ".");
			return fallback;
		}

		internal static float GetFloat(Dictionary<string, string> map, string key, float fallback)
		{
			if (map == null || !map.TryGetValue(key, out var value))
			{
				return fallback;
			}
			if (float.TryParse(value, NumberStyles.Float, CultureInfo.InvariantCulture, out var result))
			{
				return result;
			}
			ShyGuyLog.Debug("Could not read '" + key + "' as a number (value: '" + value + "'); using " + fallback + ".");
			return fallback;
		}

		internal static int GetInt(Dictionary<string, string> map, string key, int fallback)
		{
			if (map == null || !map.TryGetValue(key, out var value))
			{
				return fallback;
			}
			if (int.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var result))
			{
				return result;
			}
			if (float.TryParse(value, NumberStyles.Float, CultureInfo.InvariantCulture, out var result2))
			{
				return (int)result2;
			}
			ShyGuyLog.Debug("Could not read '" + key + "' as an integer (value: '" + value + "'); using " + fallback + ".");
			return fallback;
		}
	}
	internal static class ShyGuyLog
	{
		private static Instance _logger;

		private static readonly HashSet<string> OnceKeys = new HashSet<string>(StringComparer.Ordinal);

		private static readonly Dictionary<string, float> ThrottleTimes = new Dictionary<string, float>(StringComparer.Ordinal);

		private const float DefaultThrottleSeconds = 5f;

		private static bool DebugEnabled
		{
			get
			{
				try
				{
					return ShyGuyConfig.DebugLogging;
				}
				catch
				{
					return false;
				}
			}
		}

		internal static void Attach(Instance logger)
		{
			_logger = logger;
		}

		internal static void ResetOnceState()
		{
			OnceKeys.Clear();
			ThrottleTimes.Clear();
		}

		internal static void Msg(string message)
		{
			if (_logger != null)
			{
				_logger.Msg(message);
			}
			else
			{
				MelonLogger.Msg("[TheShyGuy] " + message);
			}
		}

		internal static void Warn(string message)
		{
			if (_logger != null)
			{
				_logger.Warning(message);
			}
			else
			{
				MelonLogger.Warning("[TheShyGuy] " + message);
			}
		}

		internal static void Error(string message)
		{
			if (_logger != null)
			{
				_logger.Error(message);
			}
			else
			{
				MelonLogger.Error("[TheShyGuy] " + message);
			}
		}

		internal static void Error(string message, Exception ex)
		{
			string text = message + " :: " + ((ex == null) ? "(no exception)" : ex.ToString());
			if (_logger != null)
			{
				_logger.Error(text);
			}
			else
			{
				MelonLogger.Error("[TheShyGuy] " + text);
			}
		}

		internal static void Debug(string message)
		{
			if (DebugEnabled)
			{
				Msg("[dbg] " + message);
			}
		}

		internal static void WarnOnce(string key, string message)
		{
			if (OnceKeys.Add(key))
			{
				Warn(message + "  (this warning is only shown once)");
			}
		}

		internal static void MsgOnce(string key, string message)
		{
			if (OnceKeys.Add(key))
			{
				Msg(message);
			}
		}

		internal static void DebugThrottled(string key, string message, float minIntervalSeconds = 5f)
		{
			if (DebugEnabled)
			{
				float unscaledTime = Time.unscaledTime;
				if (!ThrottleTimes.TryGetValue(key, out var value) || !(unscaledTime - value < minIntervalSeconds))
				{
					ThrottleTimes[key] = unscaledTime;
					Msg("[dbg] " + message);
				}
			}
		}

		internal static void WarnThrottled(string key, string message, float minIntervalSeconds = 30f)
		{
			float unscaledTime = Time.unscaledTime;
			if (!ThrottleTimes.TryGetValue(key, out var value) || !(unscaledTime - value < minIntervalSeconds))
			{
				ThrottleTimes[key] = unscaledTime;
				Warn(message);
			}
		}

		internal static void Guard(string context, Action action)
		{
			if (action == null)
			{
				return;
			}
			try
			{
				action();
			}
			catch (Exception ex)
			{
				WarnThrottled("guard:" + context, "Recovered from an error in " + context + ": " + ex.Message);
				Debug("Full trace for " + context + ": " + ex);
			}
		}
	}
	internal static class ShyGuyPhysics
	{
		private const int BufferSize = 16;

		private static readonly Il2CppStructArray<RaycastHit> Buffer = new Il2CppStructArray<RaycastHit>(16L);

		internal const int AllLayers = -1;

		internal static readonly Comparison<RaycastHit> ByDistance = (RaycastHit a, RaycastHit b) => ((RaycastHit)(ref a)).distance.CompareTo(((RaycastHit)(ref b)).distance);

		internal static bool Raycast(Vector3 origin, Vector3 direction, float maxDistance, int layerMask, out RaycastHit nearest, Func<Collider, bool> ignore = null)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: 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)
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			nearest = default(RaycastHit);
			if (((Vector3)(ref direction)).sqrMagnitude < 1E-06f || maxDistance <= 0f)
			{
				return false;
			}
			int count;
			try
			{
				count = Physics.RaycastNonAlloc(new Ray(origin, ((Vector3)(ref direction)).normalized), Buffer, maxDistance, layerMask, (QueryTriggerInteraction)1);
			}
			catch (Exception ex)
			{
				ShyGuyLog.DebugThrottled("physics:ray", "RaycastNonAlloc threw: " + ex.Message, 30f);
				return false;
			}
			return SelectNearest(count, ignore, out nearest);
		}

		internal static bool SphereCast(Vector3 origin, float radius, Vector3 direction, float maxDistance, int layerMask, out RaycastHit nearest, Func<Collider, bool> ignore = null)
		{
			//IL_0002: 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_002d: Unknown result type (might be due to invalid IL or missing references)
			nearest = default(RaycastHit);
			if (((Vector3)(ref direction)).sqrMagnitude < 1E-06f || maxDistance <= 0f || radius <= 0f)
			{
				return false;
			}
			int count;
			try
			{
				count = Physics.SphereCastNonAlloc(origin, radius, ((Vector3)(ref direction)).normalized, Buffer, maxDistance, layerMask, (QueryTriggerInteraction)1);
			}
			catch (Exception ex)
			{
				ShyGuyLog.DebugThrottled("physics:sphere", "SphereCastNonAlloc threw: " + ex.Message, 30f);
				return false;
			}
			return SelectNearest(count, ignore, out nearest);
		}

		private static bool SelectNearest(int count, Func<Collider, bool> ignore, out RaycastHit nearest)
		{
			//IL_0001: 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_0096: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bb: Unknown result type (might be due to invalid IL or missing references)
			nearest = default(RaycastHit);
			if (count <= 0)
			{
				return false;
			}
			bool result = false;
			float num = float.MaxValue;
			int num2 = ((count < 16) ? count : 16);
			for (int i = 0; i < num2; i++)
			{
				RaycastHit val = ((Il2CppArrayBase<RaycastHit>)(object)Buffer)[i];
				Collider collider = ((RaycastHit)(ref val)).collider;
				if ((Object)(object)collider == (Object)null)
				{
					continue;
				}
				if (ignore != null)
				{
					bool flag;
					try
					{
						flag = ignore(collider);
					}
					catch (Exception ex)
					{
						ShyGuyLog.DebugThrottled("physics:ignore", "A collider filter threw: " + ex.Message, 30f);
						flag = false;
					}
					if (flag)
					{
						continue;
					}
				}
				if (((RaycastHit)(ref val)).distance <= 0f)
				{
					Vector3 normal = ((RaycastHit)(ref val)).normal;
					if (((Vector3)(ref normal)).sqrMagnitude < 1E-06f)
					{
						continue;
					}
				}
				if (((RaycastHit)(ref val)).distance < num)
				{
					num = ((RaycastHit)(ref val)).distance;
					nearest = val;
					result = true;
				}
			}
			return result;
		}

		internal static int RaycastAllSorted(Vector3 origin, Vector3 direction, float maxDistance, int layerMask, List<RaycastHit> results)
		{
			//IL_0026: 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_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0080: Unknown result type (might be due to invalid IL or missing references)
			if (results == null)
			{
				return 0;
			}
			results.Clear();
			if (((Vector3)(ref direction)).sqrMagnitude < 1E-06f || maxDistance <= 0f)
			{
				return 0;
			}
			int num;
			try
			{
				num = Physics.RaycastNonAlloc(new Ray(origin, ((Vector3)(ref direction)).normalized), Buffer, maxDistance, layerMask, (QueryTriggerInteraction)1);
			}
			catch (Exception ex)
			{
				ShyGuyLog.DebugThrottled("physics:rayall", "RaycastNonAlloc threw: " + ex.Message, 30f);
				return 0;
			}
			int num2 = ((num < 16) ? num : 16);
			for (int i = 0; i < num2; i++)
			{
				results.Add(((Il2CppArrayBase<RaycastHit>)(object)Buffer)[i]);
			}
			results.Sort(ByDistance);
			return results.Count;
		}

		internal static bool CheckSphere(Vector3 position, float radius, int layerMask = -1)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				return Physics.CheckSphere(position, radius, layerMask, (QueryTriggerInteraction)1);
			}
			catch (Exception ex)
			{
				ShyGuyLog.DebugThrottled("physics:checksphere", "CheckSphere threw: " + ex.Message, 30f);
				return true;
			}
		}
	}
	internal static class ShyGuyReflect
	{
		private const BindingFlags AnyInstance = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;

		private const BindingFlags AnyStatic = BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic;

		private const BindingFlags Any = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy;

		private static readonly Dictionary<string, Type> TypeCache = new Dictionary<string, Type>(StringComparer.Ordinal);

		private static readonly Dictionary<string, MemberInfo> MemberCache = new Dictionary<string, MemberInfo>(StringComparer.Ordinal);

		private static readonly Dictionary<string, bool> AssemblyPresence = new Dictionary<string, bool>(StringComparer.OrdinalIgnoreCase);

		private static Assembly[] _assemblies;

		private static Assembly[] Assemblies
		{
			get
			{
				if (_assemblies == null || _assemblies.Length == 0)
				{
					try
					{
						_assemblies = AppDomain.CurrentDomain.GetAssemblies();
					}
					catch
					{
						_assemblies = Array.Empty<Assembly>();
					}
				}
				return _assemblies;
			}
		}

		internal static void InvalidateCaches()
		{
			TypeCache.Clear();
			MemberCache.Clear();
			AssemblyPresence.Clear();
			_assemblies = null;
		}

		internal static bool HasAssembly(string simpleName)
		{
			if (string.IsNullOrEmpty(simpleName))
			{
				return false;
			}
			if (AssemblyPresence.TryGetValue(simpleName, out var value))
			{
				return value;
			}
			bool flag = false;
			try
			{
				flag = Assemblies.Any(delegate(Assembly a)
				{
					try
					{
						return string.Equals(a.GetName().Name, simpleName, StringComparison.OrdinalIgnoreCase);
					}
					catch
					{
						return false;
					}
				});
				if (!flag)
				{
					_assemblies = null;
					flag = Assemblies.Any(delegate(Assembly a)
					{
						try
						{
							return string.Equals(a.GetName().Name, simpleName, StringComparison.OrdinalIgnoreCase);
						}
						catch
						{
							return false;
						}
					});
				}
			}
			catch (Exception ex)
			{
				ShyGuyLog.Debug("HasAssembly(" + simpleName + ") failed: " + ex.Message);
			}
			AssemblyPresence[simpleName] = flag;
			return flag;
		}

		internal static Type FindType(string cacheKey, string[] fullNameCandidates, string simpleNameFallback = null)
		{
			if (TypeCache.TryGetValue(cacheKey, out var value))
			{
				return value;
			}
			Type type = null;
			if (fullNameCandidates != null)
			{
				foreach (string text in fullNameCandidates)
				{
					if (string.IsNullOrEmpty(text))
					{
						continue;
					}
					Assembly[] assemblies = Assemblies;
					foreach (Assembly assembly in assemblies)
					{
						try
						{
							type = assembly.GetType(text, throwOnError: false);
							if (type != null)
							{
								break;
							}
						}
						catch
						{
						}
					}
					if (type != null)
					{
						break;
					}
				}
			}
			if (type == null && !string.IsNullOrEmpty(simpleNameFallback))
			{
				type = ScanForSimpleName(simpleNameFallback);
			}
			TypeCache[cacheKey] = type;
			if (type == null)
			{
				ShyGuyLog.Debug("Reflection: type '" + cacheKey + "' not found in " + Assemblies.Length + " loaded assemblies.");
			}
			else
			{
				ShyGuyLog.Debug("Reflection: resolved '" + cacheKey + "' -> " + type.FullName);
			}
			return type;
		}

		private static Type ScanForSimpleName(string simpleName)
		{
			List<Type> list = new List<Type>();
			Assembly[] assemblies = Assemblies;
			foreach (Assembly assembly in assemblies)
			{
				Type[] array;
				try
				{
					array = assembly.GetTypes();
				}
				catch (ReflectionTypeLoadException ex)
				{
					array = ex.Types?.Where((Type t) => t != null).ToArray() ?? Array.Empty<Type>();
				}
				catch
				{
					continue;
				}
				Type[] array2 = array;
				foreach (Type type in array2)
				{
					if (type != null && string.Equals(type.Name, simpleName, StringComparison.Ordinal))
					{
						list.Add(type);
					}
				}
			}
			if (list.Count == 0)
			{
				return null;
			}
			if (list.Count == 1)
			{
				return list[0];
			}
			return (from t in list
				orderby t.IsNested ? 1 : 0, (t.FullName ?? t.Name