Decompiled source of TeleportCreatures v0.2.4

TeleportCreatures.dll

Decompiled 21 hours ago
using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Serialization;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using JetBrains.Annotations;
using LocalizationManager;
using Microsoft.CodeAnalysis;
using Odinplus.Shared.YamlEditor;
using ServerSync;
using TMPro;
using UnityEngine;
using YamlDotNet.Core;
using YamlDotNet.Core.Events;
using YamlDotNet.Core.ObjectPool;
using YamlDotNet.Core.Tokens;
using YamlDotNet.Helpers;
using YamlDotNet.Serialization;
using YamlDotNet.Serialization.BufferedDeserialization;
using YamlDotNet.Serialization.BufferedDeserialization.TypeDiscriminators;
using YamlDotNet.Serialization.Callbacks;
using YamlDotNet.Serialization.Converters;
using YamlDotNet.Serialization.EventEmitters;
using YamlDotNet.Serialization.NamingConventions;
using YamlDotNet.Serialization.NodeDeserializers;
using YamlDotNet.Serialization.NodeTypeResolvers;
using YamlDotNet.Serialization.ObjectFactories;
using YamlDotNet.Serialization.ObjectGraphTraversalStrategies;
using YamlDotNet.Serialization.ObjectGraphVisitors;
using YamlDotNet.Serialization.Schemas;
using YamlDotNet.Serialization.TypeInspectors;
using YamlDotNet.Serialization.TypeResolvers;
using YamlDotNet.Serialization.Utilities;
using YamlDotNet.Serialization.ValueDeserializers;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyTitle("TeleportCreatures")]
[assembly: AssemblyDescription("https://valheim.hexium.gg/mods/OdinPlus/TeleportCreatures")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("odinplus")]
[assembly: AssemblyProduct("TeleportCreatures")]
[assembly: AssemblyCopyright("Copyright © 2026")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyFileVersion("0.2.4")]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("0.2.4.0")]
[module: UnverifiableCode]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

		public NullableAttribute(byte P_0)
		{
			NullableFlags = new byte[1] { P_0 };
		}

		public NullableAttribute(byte[] P_0)
		{
			NullableFlags = P_0;
		}
	}
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableContextAttribute : Attribute
	{
		public readonly byte Flag;

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
}
namespace LocalizationManager
{
	public class Localizer
	{
		private static readonly Dictionary<string, Dictionary<string, Func<string>>> PlaceholderProcessors;

		private static readonly Dictionary<string, Dictionary<string, string>> loadedTexts;

		private static readonly ConditionalWeakTable<Localization, string> localizationLanguage;

		private static readonly List<WeakReference<Localization>> localizationObjects;

		private static BaseUnityPlugin? _plugin;

		private static readonly List<string> fileExtensions;

		private static BaseUnityPlugin plugin
		{
			get
			{
				//IL_009b: Unknown result type (might be due to invalid IL or missing references)
				//IL_00a5: Expected O, but got Unknown
				if (_plugin == null)
				{
					IEnumerable<TypeInfo> source;
					try
					{
						source = Assembly.GetExecutingAssembly().DefinedTypes.ToList();
					}
					catch (ReflectionTypeLoadException ex)
					{
						source = from t in ex.Types
							where t != null
							select t.GetTypeInfo();
					}
					_plugin = (BaseUnityPlugin)Chainloader.ManagerObject.GetComponent((Type)source.First((TypeInfo t) => t.IsClass && typeof(BaseUnityPlugin).IsAssignableFrom(t)));
				}
				return _plugin;
			}
		}

		public static event Action? OnLocalizationComplete;

		private static void UpdatePlaceholderText(Localization localization, string key)
		{
			localizationLanguage.TryGetValue(localization, out string value);
			string text = loadedTexts[value][key];
			if (PlaceholderProcessors.TryGetValue(key, out Dictionary<string, Func<string>> value2))
			{
				text = value2.Aggregate(text, (string current, KeyValuePair<string, Func<string>> kv) => current.Replace("{" + kv.Key + "}", kv.Value()));
			}
			localization.AddWord(key, text);
		}

		public static void AddPlaceholder<T>(string key, string placeholder, ConfigEntry<T> config, Func<T, string>? convertConfigValue = null) where T : notnull
		{
			if (convertConfigValue == null)
			{
				convertConfigValue = (T val) => val.ToString();
			}
			if (!PlaceholderProcessors.ContainsKey(key))
			{
				PlaceholderProcessors[key] = new Dictionary<string, Func<string>>();
			}
			config.SettingChanged += delegate
			{
				UpdatePlaceholder();
			};
			if (loadedTexts.ContainsKey(Localization.instance.GetSelectedLanguage()))
			{
				UpdatePlaceholder();
			}
			void UpdatePlaceholder()
			{
				PlaceholderProcessors[key][placeholder] = () => convertConfigValue(config.Value);
				UpdatePlaceholderText(Localization.instance, key);
			}
		}

		public static void AddText(string key, string text)
		{
			List<WeakReference<Localization>> list = new List<WeakReference<Localization>>();
			foreach (WeakReference<Localization> localizationObject in localizationObjects)
			{
				if (localizationObject.TryGetTarget(out var target))
				{
					Dictionary<string, string> dictionary = loadedTexts[localizationLanguage.GetOrCreateValue(target)];
					if (!target.m_translations.ContainsKey(key))
					{
						dictionary[key] = text;
						target.AddWord(key, text);
					}
				}
				else
				{
					list.Add(localizationObject);
				}
			}
			foreach (WeakReference<Localization> item in list)
			{
				localizationObjects.Remove(item);
			}
		}

		public static void Load()
		{
			_ = plugin;
		}

		public static void LoadLocalizationLater(Localization __instance)
		{
			LoadLocalization(Localization.instance, __instance.GetSelectedLanguage());
		}

		public static void SafeCallLocalizeComplete()
		{
			Localizer.OnLocalizationComplete?.Invoke();
		}

		private static void LoadLocalization(Localization __instance, string language)
		{
			if (!localizationLanguage.Remove(__instance))
			{
				localizationObjects.Add(new WeakReference<Localization>(__instance));
			}
			localizationLanguage.Add(__instance, language);
			Dictionary<string, string> dictionary = new Dictionary<string, string>();
			foreach (string item in from f in Directory.GetFiles(Path.GetDirectoryName(Paths.PluginPath), plugin.Info.Metadata.Name + ".*", SearchOption.AllDirectories)
				where fileExtensions.IndexOf(Path.GetExtension(f)) >= 0
				select f)
			{
				string[] array = Path.GetFileNameWithoutExtension(item).Split(new char[1] { '.' });
				if (array.Length >= 2)
				{
					string text = array[1];
					if (dictionary.ContainsKey(text))
					{
						Debug.LogWarning((object)("Duplicate key " + text + " found for " + plugin.Info.Metadata.Name + ". The duplicate file found at " + item + " will be skipped."));
					}
					else
					{
						dictionary[text] = item;
					}
				}
			}
			byte[] array2 = LoadTranslationFromAssembly("English");
			if (array2 == null)
			{
				throw new Exception("Found no English localizations in mod " + plugin.Info.Metadata.Name + ". Expected an embedded resource translations/English.json or translations/English.yml.");
			}
			Dictionary<string, string> dictionary2 = new DeserializerBuilder().IgnoreFields().Build().Deserialize<Dictionary<string, string>>(Encoding.UTF8.GetString(array2));
			if (dictionary2 == null)
			{
				throw new Exception("Localization for mod " + plugin.Info.Metadata.Name + " failed: Localization file was empty.");
			}
			string text2 = null;
			if (language != "English")
			{
				if (dictionary.TryGetValue(language, out var value))
				{
					text2 = File.ReadAllText(value);
				}
				else
				{
					byte[] array3 = LoadTranslationFromAssembly(language);
					if (array3 != null)
					{
						text2 = Encoding.UTF8.GetString(array3);
					}
				}
			}
			if (text2 == null && dictionary.TryGetValue("English", out var value2))
			{
				text2 = File.ReadAllText(value2);
			}
			if (text2 != null)
			{
				foreach (KeyValuePair<string, string> item2 in new DeserializerBuilder().IgnoreFields().Build().Deserialize<Dictionary<string, string>>(text2) ?? new Dictionary<string, string>())
				{
					dictionary2[item2.Key] = item2.Value;
				}
			}
			loadedTexts[language] = dictionary2;
			foreach (KeyValuePair<string, string> item3 in dictionary2)
			{
				UpdatePlaceholderText(__instance, item3.Key);
			}
		}

		static Localizer()
		{
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			//IL_0080: Unknown result type (might be due to invalid IL or missing references)
			//IL_008d: Expected O, but got Unknown
			//IL_008e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c9: Expected O, but got Unknown
			//IL_00f7: Unknown result type (might be due to invalid IL or missing references)
			//IL_0104: Expected O, but got Unknown
			PlaceholderProcessors = new Dictionary<string, Dictionary<string, Func<string>>>();
			loadedTexts = new Dictionary<string, Dictionary<string, string>>();
			localizationLanguage = new ConditionalWeakTable<Localization, string>();
			localizationObjects = new List<WeakReference<Localization>>();
			fileExtensions = new List<string> { ".json", ".yml" };
			Harmony val = new Harmony("org.bepinex.helpers.LocalizationManager");
			val.Patch((MethodBase)AccessTools.DeclaredMethod(typeof(Localization), "SetupLanguage", (Type[])null, (Type[])null), (HarmonyMethod)null, new HarmonyMethod(AccessTools.DeclaredMethod(typeof(Localizer), "LoadLocalization", (Type[])null, (Type[])null)), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			val.Patch((MethodBase)AccessTools.DeclaredMethod(typeof(FejdStartup), "SetupGui", (Type[])null, (Type[])null), (HarmonyMethod)null, new HarmonyMethod(AccessTools.DeclaredMethod(typeof(Localizer), "LoadLocalizationLater", (Type[])null, (Type[])null)), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			val.Patch((MethodBase)AccessTools.DeclaredMethod(typeof(FejdStartup), "Start", (Type[])null, (Type[])null), (HarmonyMethod)null, new HarmonyMethod(AccessTools.DeclaredMethod(typeof(Localizer), "SafeCallLocalizeComplete", (Type[])null, (Type[])null)), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
		}

		private static byte[]? LoadTranslationFromAssembly(string language)
		{
			foreach (string fileExtension in fileExtensions)
			{
				byte[] array = ReadEmbeddedFileBytes("translations." + language + fileExtension);
				if (array != null)
				{
					return array;
				}
			}
			return null;
		}

		public static byte[]? ReadEmbeddedFileBytes(string resourceFileName, Assembly? containingAssembly = null)
		{
			using MemoryStream memoryStream = new MemoryStream();
			if ((object)containingAssembly == null)
			{
				containingAssembly = Assembly.GetCallingAssembly();
			}
			string text = containingAssembly.GetManifestResourceNames().FirstOrDefault((string str) => str.EndsWith(resourceFileName, StringComparison.Ordinal));
			if (text != null)
			{
				containingAssembly.GetManifestResourceStream(text)?.CopyTo(memoryStream);
			}
			return (memoryStream.Length == 0L) ? null : memoryStream.ToArray();
		}
	}
	public static class LocalizationManagerVersion
	{
		public const string Version = "1.4.1";
	}
}
namespace TeleportCreatures
{
	internal enum AllyTransportMessageMode
	{
		NoMessages,
		TopLeft,
		TopCenter,
		Center
	}
	internal static class AllyTransportDisplayPolicy
	{
		public static bool ShouldShowMessage(AllyTransportMessageMode mode, int allyCount)
		{
			if (mode != AllyTransportMessageMode.NoMessages)
			{
				return allyCount > 0;
			}
			return false;
		}

		public static AllyTransportMessageMode ParseMessageMode(string? value)
		{
			return value?.Trim().ToLowerInvariant() switch
			{
				"no messages" => AllyTransportMessageMode.NoMessages, 
				"top left" => AllyTransportMessageMode.TopLeft, 
				"top center" => AllyTransportMessageMode.TopCenter, 
				_ => AllyTransportMessageMode.Center, 
			};
		}
	}
	internal enum CreatureArrivalAction
	{
		Skip,
		MoveInstance,
		MoveOwnedRecord
	}
	internal static class CreatureArrivalDecision
	{
		public static CreatureArrivalAction Select(bool instanceAvailable, bool recordAvailable, bool recordIsOwnedLocally)
		{
			if (instanceAvailable)
			{
				return CreatureArrivalAction.MoveInstance;
			}
			if (recordAvailable && recordIsOwnedLocally)
			{
				return CreatureArrivalAction.MoveOwnedRecord;
			}
			return CreatureArrivalAction.Skip;
		}

		public static bool ShouldPersistNetworkRecord(CreatureArrivalAction action)
		{
			if ((uint)(action - 1) <= 1u)
			{
				return true;
			}
			return false;
		}
	}
	public sealed class CreatureConfig
	{
		public int SchemaVersion { get; set; } = 1;

		public CreatureFilterConfig Allies { get; set; } = new CreatureFilterConfig
		{
			Allow = new List<string> { "all" },
			Deny = new List<string>()
		};

		public PlacementConfig Placement { get; set; } = new PlacementConfig();
	}
	public sealed class CreatureFilterConfig
	{
		public bool Enabled { get; set; } = true;

		public AllyTeleportConfig Teleport { get; set; } = new AllyTeleportConfig();

		public float MaximumSearchRadius { get; set; } = 20f;

		public float MaximumSearchHeightRadius { get; set; } = 8f;

		public List<string> Allow { get; set; } = new List<string>();

		public List<string> Deny { get; set; } = new List<string>();
	}
	public enum AllySelectionMode
	{
		AllTamed,
		OnlyFollow,
		OnlyNamed,
		AllTamedExceptNamed
	}
	public static class AllySelectionPolicy
	{
		public static bool IsSelected(AllySelectionMode mode, bool isFollowingPlayer, bool hasCustomName)
		{
			return mode switch
			{
				AllySelectionMode.AllTamed => true, 
				AllySelectionMode.OnlyFollow => isFollowingPlayer, 
				AllySelectionMode.OnlyNamed => hasCustomName, 
				AllySelectionMode.AllTamedExceptNamed => !hasCustomName, 
				_ => false, 
			};
		}
	}
	public sealed class AllyTeleportConfig
	{
		public PortalTransportConfig Portals { get; set; } = new PortalTransportConfig();

		public TransitionToggleConfig Dungeons { get; set; } = new TransitionToggleConfig();

		public TransitionToggleConfig Basements { get; set; } = new TransitionToggleConfig();
	}
	public sealed class TransitionToggleConfig
	{
		public bool Enabled { get; set; } = true;
	}
	public sealed class PortalTransportConfig
	{
		public bool Enabled { get; set; } = true;

		public List<PortalRuleConfig> Rules { get; set; } = new List<PortalRuleConfig>();
	}
	public sealed class PortalRuleConfig
	{
		public string Id { get; set; } = string.Empty;

		public int? Priority { get; set; }

		public PortalRuleSourceConfig Source { get; set; } = new PortalRuleSourceConfig();

		public List<string>? Allow { get; set; }

		public List<string> Deny { get; set; } = new List<string>();
	}
	public sealed class PortalRuleSourceConfig
	{
		public List<string>? Tags { get; set; }

		public List<string>? Prefabs { get; set; }
	}
	public sealed class DungeonPlacementConfig
	{
		public float Radius { get; set; } = 12f;

		public bool RequireNaturalGroundOnExit { get; set; } = true;
	}
	public sealed class BasementPlacementConfig
	{
		public float Radius { get; set; } = 8f;

		public bool RequireClearance { get; set; } = true;
	}
	public sealed class PlacementConfig
	{
		public float Radius { get; set; } = 10f;

		public float MaximumHeightDifference { get; set; } = 3f;

		public float DistanceFromPlayer { get; set; } = 3f;

		public float AnimalSpacing { get; set; } = 2.5f;

		public WaterPolicy Water { get; set; } = WaterPolicy.Allow;

		public DungeonPlacementConfig Dungeons { get; set; } = new DungeonPlacementConfig();

		public BasementPlacementConfig Basements { get; set; } = new BasementPlacementConfig();
	}
	public enum WaterPolicy
	{
		Avoid,
		Allow,
		Require
	}
	public static class CreatureConfigValidator
	{
		public static void Validate(CreatureConfig config)
		{
			if (config == null)
			{
				throw new InvalidDataException("Creature configuration cannot be null.");
			}
			if (config.SchemaVersion != 1)
			{
				throw new InvalidDataException("Only SchemaVersion 1 is supported. Move DungeonPlacement and BasementPlacement under Placement.");
			}
			if (config.Allies == null || config.Allies.Teleport == null || config.Placement == null || config.Allies.Teleport.Portals == null || config.Placement.Dungeons == null || config.Placement.Basements == null || config.Allies.Teleport.Dungeons == null || config.Allies.Teleport.Basements == null)
			{
				throw new InvalidDataException("Allies, Allies.Teleport, Placement, Placement.Dungeons, and Placement.Basements sections are required.");
			}
			if (config.Placement.Dungeons.Radius <= 0f || config.Placement.Basements.Radius <= 0f)
			{
				throw new InvalidDataException("Placement.Dungeons.Radius and Placement.Basements.Radius must be positive.");
			}
			if (config.Allies.MaximumSearchRadius <= 0f || config.Allies.MaximumSearchHeightRadius <= 0f)
			{
				throw new InvalidDataException("Allies.MaximumSearchRadius and Allies.MaximumSearchHeightRadius must be positive.");
			}
			if (config.Placement.Radius <= 0f || config.Placement.MaximumHeightDifference < 0f || config.Placement.DistanceFromPlayer <= 0f || config.Placement.AnimalSpacing <= 0f)
			{
				throw new InvalidDataException("Placement radius, distance, and spacing must be positive; MaximumHeightDifference cannot be negative.");
			}
			if (!Enum.IsDefined(typeof(WaterPolicy), config.Placement.Water))
			{
				throw new InvalidDataException("Placement.Water must be Avoid, Allow, or Require.");
			}
			CreatureMatcher.ValidateList(config.Allies.Allow, "Allies.Allow");
			CreatureMatcher.ValidateList(config.Allies.Deny, "Allies.Deny");
			ValidatePortalRules(config.Allies.Teleport.Portals.Rules);
		}

		internal static void ValidatePortalRules(IReadOnlyList<PortalRuleConfig> rules)
		{
			if (rules == null)
			{
				throw new InvalidDataException("Allies.Teleport.Portals.Rules cannot be null.");
			}
			HashSet<string> hashSet = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
			for (int i = 0; i < rules.Count; i++)
			{
				PortalRuleConfig portalRuleConfig = rules[i] ?? throw new InvalidDataException($"Allies.Teleport.Portals.Rules[{i}] cannot be null.");
				string text = $"Allies.Teleport.Portals.Rules[{i}]";
				if (string.IsNullOrWhiteSpace(portalRuleConfig.Id))
				{
					throw new InvalidDataException(text + ".Id is required.");
				}
				if (!hashSet.Add(portalRuleConfig.Id.Trim()))
				{
					throw new InvalidDataException(text + ".Id '" + portalRuleConfig.Id + "' duplicates another portal rule Id.");
				}
				bool flag;
				switch (portalRuleConfig.Priority)
				{
				default:
					flag = true;
					break;
				case null:
				case 1:
				case 2:
				case 3:
				case 4:
				case 5:
				case 6:
				case 7:
				case 8:
				case 9:
				case 10:
				case 11:
				case 12:
				case 13:
				case 14:
				case 15:
				case 16:
				case 17:
				case 18:
				case 19:
				case 20:
				case 21:
				case 22:
				case 23:
				case 24:
				case 25:
				case 26:
				case 27:
				case 28:
				case 29:
				case 30:
				case 31:
				case 32:
				case 33:
				case 34:
				case 35:
				case 36:
				case 37:
				case 38:
				case 39:
				case 40:
				case 41:
				case 42:
				case 43:
				case 44:
				case 45:
				case 46:
				case 47:
				case 48:
				case 49:
				case 50:
				case 51:
				case 52:
				case 53:
				case 54:
				case 55:
				case 56:
				case 57:
				case 58:
				case 59:
				case 60:
				case 61:
				case 62:
				case 63:
				case 64:
				case 65:
				case 66:
				case 67:
				case 68:
				case 69:
				case 70:
				case 71:
				case 72:
				case 73:
				case 74:
				case 75:
				case 76:
				case 77:
				case 78:
				case 79:
				case 80:
				case 81:
				case 82:
				case 83:
				case 84:
				case 85:
				case 86:
				case 87:
				case 88:
				case 89:
				case 90:
				case 91:
				case 92:
				case 93:
				case 94:
				case 95:
				case 96:
				case 97:
				case 98:
				case 99:
				case 100:
					flag = false;
					break;
				}
				if (flag)
				{
					throw new InvalidDataException(text + ".Priority must be between 1 and 100 when specified.");
				}
				if (portalRuleConfig.Source == null)
				{
					throw new InvalidDataException(text + ".Source is required.");
				}
				List<string> tags = portalRuleConfig.Source.Tags;
				bool num = tags != null && tags.Count > 0;
				tags = portalRuleConfig.Source.Prefabs;
				bool flag2 = tags != null && tags.Count > 0;
				if (!num && !flag2)
				{
					throw new InvalidDataException(text + ".Source requires Tags and/or Prefabs.");
				}
				if (portalRuleConfig.Source.Tags != null)
				{
					CreatureMatcher.ValidateList(portalRuleConfig.Source.Tags, text + ".Source.Tags");
				}
				if (portalRuleConfig.Source.Prefabs != null)
				{
					CreatureMatcher.ValidateList(portalRuleConfig.Source.Prefabs, text + ".Source.Prefabs");
				}
				if (portalRuleConfig.Allow != null)
				{
					CreatureMatcher.ValidateList(portalRuleConfig.Allow, text + ".Allow");
				}
				CreatureMatcher.ValidateList(portalRuleConfig.Deny, text + ".Deny");
			}
		}
	}
	public static class CreatureEligibility
	{
		public static bool IsAllowed(CreatureConfig config, string prefabName, bool isTamed)
		{
			if (!isTamed || string.IsNullOrWhiteSpace(prefabName))
			{
				return false;
			}
			if (config.Allies.Deny.Any((string pattern) => CreatureMatcher.IsMatch(pattern, prefabName)))
			{
				return false;
			}
			return config.Allies.Allow.Any((string pattern) => CreatureMatcher.IsMatch(pattern, prefabName));
		}
	}
	internal static class CreaturePlacementPolicy
	{
		public static bool IsNaturalGround(float terrainHeight, float solidHeight, float tolerance)
		{
			return Math.Abs(terrainHeight - solidHeight) <= tolerance;
		}

		public static bool CanUseConstructedFloor(bool isBasement, bool clearanceIsOpen)
		{
			return !isBasement || clearanceIsOpen;
		}

		public static bool UsesNativePlayerLevel(bool isBasement)
		{
			return isBasement;
		}

		public static float SelectLandingHeight(bool hasInteriorFloor, float interiorFloorHeight, float solidHeight)
		{
			if (!hasInteriorFloor)
			{
				return solidHeight;
			}
			return interiorFloorHeight;
		}

		public static bool IsOnPlayerLevel(float playerHeight, float floorHeight, float maximumDifference)
		{
			return Math.Abs(playerHeight - floorHeight) <= maximumDifference;
		}

		public static bool CanUseDungeonFallbackHeight(float playerHeight, float solidHeight, float maximumDifference)
		{
			return IsOnPlayerLevel(playerHeight, solidHeight, maximumDifference);
		}

		public static int GetCandidateSlot(int animalIndex, int attempt, int positionsPerRing)
		{
			return animalIndex + attempt;
		}

		public static float GetForwardSearchAngle(int slot, int positionsPerRing, float halfAngleDegrees)
		{
			int num = slot % positionsPerRing;
			if (num == 0)
			{
				return 0f;
			}
			int num2 = (num + 1) / 2;
			int num3 = (positionsPerRing - 1) / 2;
			return ((num % 2 == 0) ? 1f : (-1f)) * halfAngleDegrees * (float)num2 / (float)num3;
		}

		public static bool IsClearanceFree(int collisionCount, int bufferCapacity)
		{
			if (collisionCount == 0)
			{
				return collisionCount < bufferCapacity;
			}
			return false;
		}
	}
	public sealed class PlayerCreaturePreferences
	{
		public int SchemaVersion { get; set; } = 1;

		public PlayerAllyPreferences Allies { get; set; } = new PlayerAllyPreferences();

		public static PlayerCreaturePreferences CreateDefault()
		{
			return new PlayerCreaturePreferences();
		}
	}
	public sealed class PlayerAllyPreferences
	{
		public bool Enabled { get; set; } = true;

		public AllySelectionMode Mode { get; set; }

		public PlayerAllyTeleportConfig Teleport { get; set; } = new PlayerAllyTeleportConfig();

		public float SearchRadius { get; set; } = 10f;

		public float SearchHeightRadius { get; set; } = 3f;

		public List<string> Allow { get; set; } = new List<string> { "all" };

		public List<string> Deny { get; set; } = new List<string>();
	}
	public sealed class PlayerAllyTeleportConfig
	{
		public PortalTransportConfig Portals { get; set; } = new PortalTransportConfig();

		public TransitionToggleConfig Dungeons { get; set; } = new TransitionToggleConfig();

		public TransitionToggleConfig Basements { get; set; } = new TransitionToggleConfig();
	}
	public static class PlayerCreaturePreferencesValidator
	{
		public static void Validate(PlayerCreaturePreferences preferences)
		{
			if (preferences == null)
			{
				throw new InvalidDataException("Player creature preferences cannot be null.");
			}
			if (preferences.SchemaVersion != 1)
			{
				throw new InvalidDataException("Only SchemaVersion 1 is supported.");
			}
			if (preferences.Allies == null || preferences.Allies.Teleport == null || preferences.Allies.Teleport.Portals == null || preferences.Allies.Teleport.Dungeons == null || preferences.Allies.Teleport.Basements == null)
			{
				throw new InvalidDataException("Allies and Allies.Teleport sections are required.");
			}
			if (!Enum.IsDefined(typeof(AllySelectionMode), preferences.Allies.Mode))
			{
				throw new InvalidDataException("Allies.Mode must be valid.");
			}
			if (preferences.Allies.SearchRadius <= 0f || preferences.Allies.SearchHeightRadius <= 0f)
			{
				throw new InvalidDataException("Allies.SearchRadius and Allies.SearchHeightRadius must be positive.");
			}
			CreatureMatcher.ValidateList(preferences.Allies.Allow, "Allies.Allow");
			CreatureMatcher.ValidateList(preferences.Allies.Deny, "Allies.Deny");
			CreatureConfigValidator.ValidatePortalRules(preferences.Allies.Teleport.Portals.Rules);
		}
	}
	public static class PlayerPreferenceSnapshot
	{
		public static bool TryReplace(PlayerCreaturePreferences lastValid, string yaml, out PlayerCreaturePreferences retained, out string error)
		{
			try
			{
				PlayerCreaturePreferences playerCreaturePreferences = new DeserializerBuilder().Build().Deserialize<PlayerCreaturePreferences>(yaml);
				PlayerCreaturePreferencesValidator.Validate(playerCreaturePreferences);
				retained = playerCreaturePreferences;
				error = string.Empty;
				return true;
			}
			catch (Exception ex)
			{
				retained = lastValid;
				error = ex.Message;
				return false;
			}
		}
	}
	public static class CreatureMatcher
	{
		private static readonly char[] LegacyRegexCharacters = new char[9] { '^', '$', '[', ']', '(', ')', '|', '+', '\\' };

		public static bool IsMatch(string pattern, string prefabName)
		{
			ValidatePattern(pattern, "filter");
			string text = pattern.Trim();
			if (text.Equals("all", StringComparison.OrdinalIgnoreCase) || text == "*")
			{
				return true;
			}
			if (text.Equals("none", StringComparison.OrdinalIgnoreCase))
			{
				return false;
			}
			string pattern2 = "^" + GlobToRegex(text) + "$";
			return Regex.IsMatch(prefabName ?? string.Empty, pattern2, RegexOptions.IgnoreCase | RegexOptions.CultureInvariant);
		}

		public static void ValidateList(IEnumerable<string> patterns, string section)
		{
			if (patterns == null)
			{
				throw new InvalidDataException(section + " cannot be null.");
			}
			foreach (string pattern in patterns)
			{
				ValidatePattern(pattern, section);
			}
		}

		private static void ValidatePattern(string pattern, string section)
		{
			if (string.IsNullOrWhiteSpace(pattern))
			{
				throw new InvalidDataException(section + " cannot contain an empty filter.");
			}
			string text = pattern.Trim();
			if (text.StartsWith("regex:", StringComparison.OrdinalIgnoreCase) || text.IndexOfAny(LegacyRegexCharacters) >= 0)
			{
				throw new InvalidDataException(section + " uses legacy regular-expression syntax. Use an exact prefab name or * / ? glob instead.");
			}
		}

		private static string GlobToRegex(string pattern)
		{
			StringBuilder stringBuilder = new StringBuilder();
			for (int i = 0; i < pattern.Length; i++)
			{
				char c = pattern[i];
				StringBuilder stringBuilder2 = stringBuilder;
				stringBuilder2.Append(c switch
				{
					'*' => ".*", 
					'?' => ".", 
					_ => Regex.Escape(c.ToString()), 
				});
			}
			return stringBuilder.ToString();
		}
	}
	public sealed class PortalContext
	{
		public string PrefabName { get; }

		public string Tag { get; }

		public bool IsUsable => !string.IsNullOrWhiteSpace(PrefabName);

		public PortalContext(string prefabName, string tag)
		{
			PrefabName = prefabName ?? string.Empty;
			Tag = tag ?? string.Empty;
		}
	}
	public static class PortalRuleResolver
	{
		public static PortalRuleConfig? Select(PortalTransportConfig portals, PortalContext context)
		{
			if (portals == null || context == null || !context.IsUsable)
			{
				return null;
			}
			PortalRuleConfig portalRuleConfig = null;
			foreach (PortalRuleConfig rule in portals.Rules)
			{
				if (Matches(rule, context) && (portalRuleConfig == null || GetEffectivePriority(rule) < GetEffectivePriority(portalRuleConfig)))
				{
					portalRuleConfig = rule;
				}
			}
			return portalRuleConfig;
		}

		public static int GetEffectivePriority(PortalRuleConfig rule)
		{
			return rule.Priority ?? 100;
		}

		public static bool Matches(PortalRuleConfig rule, PortalContext context)
		{
			if (rule?.Source == null || context == null || !context.IsUsable)
			{
				return false;
			}
			List<string> tags = rule.Source.Tags;
			bool flag = tags != null && tags.Count > 0;
			tags = rule.Source.Prefabs;
			bool flag2 = tags != null && tags.Count > 0;
			if (!flag && !flag2)
			{
				return false;
			}
			if (!flag || MatchesAny(rule.Source.Tags, context.Tag))
			{
				if (flag2)
				{
					return MatchesAny(rule.Source.Prefabs, context.PrefabName);
				}
				return true;
			}
			return false;
		}

		private static bool MatchesAny(IEnumerable<string> patterns, string value)
		{
			return patterns.Any((string pattern) => CreatureMatcher.IsMatch(pattern, value));
		}
	}
	public sealed class EffectiveCreaturePolicy
	{
		public CreatureConfig ServerPolicy { get; }

		public PlayerCreaturePreferences PlayerPreferences { get; }

		private EffectiveCreaturePolicy(CreatureConfig serverPolicy, PlayerCreaturePreferences playerPreferences)
		{
			ServerPolicy = serverPolicy;
			PlayerPreferences = playerPreferences;
		}

		public static EffectiveCreaturePolicy Create(CreatureConfig server, PlayerCreaturePreferences player)
		{
			CreatureConfigValidator.Validate(server);
			PlayerCreaturePreferencesValidator.Validate(player);
			return new EffectiveCreaturePolicy(server, player);
		}

		public bool IsAllowed(string prefabName, bool isTamed, bool isFollowingPlayer, bool hasCustomName, PortalContext? portalContext = null)
		{
			if (!isTamed || !ServerPolicy.Allies.Enabled || !PlayerPreferences.Allies.Enabled || string.IsNullOrWhiteSpace(prefabName))
			{
				return false;
			}
			if (!AllySelectionPolicy.IsSelected(PlayerPreferences.Allies.Mode, isFollowingPlayer, hasCustomName))
			{
				return false;
			}
			PortalRuleConfig portalRuleConfig = ((portalContext == null) ? null : PortalRuleResolver.Select(ServerPolicy.Allies.Teleport.Portals, portalContext));
			PortalRuleConfig portalRuleConfig2 = ((portalContext == null) ? null : PortalRuleResolver.Select(PlayerPreferences.Allies.Teleport.Portals, portalContext));
			IEnumerable<string> patterns = portalRuleConfig2?.Allow ?? PlayerPreferences.Allies.Allow;
			if (MatchesAllowedList(ServerPolicy.Allies.Allow, prefabName) && (portalRuleConfig?.Allow == null || MatchesAllowedList(portalRuleConfig.Allow, prefabName)) && MatchesAllowedList(patterns, prefabName) && !MatchesAny(ServerPolicy.Allies.Deny, prefabName) && (portalRuleConfig == null || !MatchesAny(portalRuleConfig.Deny, prefabName)) && !MatchesAny(PlayerPreferences.Allies.Deny, prefabName))
			{
				if (portalRuleConfig2 != null)
				{
					return !MatchesAny(portalRuleConfig2.Deny, prefabName);
				}
				return true;
			}
			return false;
		}

		public bool IsTransitionEnabled(TransportTransitionKind transitionKind, bool basementsInstalled)
		{
			if (CreatureTransitionPolicy.IsEnabled(ServerPolicy.Allies.Teleport, transitionKind, basementsInstalled))
			{
				return CreatureTransitionPolicy.IsEnabled(PlayerPreferences.Allies.Teleport, transitionKind, basementsInstalled);
			}
			return false;
		}

		public float GetAllySearchRadius()
		{
			return Math.Min(ServerPolicy.Allies.MaximumSearchRadius, PlayerPreferences.Allies.SearchRadius);
		}

		public float GetAllySearchHeightRadius()
		{
			return Math.Min(ServerPolicy.Allies.MaximumSearchHeightRadius, PlayerPreferences.Allies.SearchHeightRadius);
		}

		private static bool MatchesAllowedList(IEnumerable<string> patterns, string prefabName)
		{
			return patterns.Any((string pattern) => CreatureMatcher.IsMatch(pattern, prefabName));
		}

		private static bool MatchesAny(IEnumerable<string> patterns, string prefabName)
		{
			return patterns.Any((string pattern) => CreatureMatcher.IsMatch(pattern, prefabName));
		}
	}
	internal static class CreatureTeleportDiagnostics
	{
		public static string FormatCapture(TransportTransitionKind transitionKind, string player, string source, string requestedDestination, int allies)
		{
			return $"TeleportCaptured: transition={transitionKind} player={player} source={source} requestedDestination={requestedDestination} allies={allies}";
		}

		public static string FormatMovement(TransportTransitionKind transitionKind, string prefabName, string customName, string zdoId, string source, string destination, string method)
		{
			return $"AllyMoved: transition={transitionKind} prefab={prefabName} name={customName} zdo={zdoId} source={source} destination={destination} method={method}";
		}

		public static string FormatCompletion(TransportTransitionKind transitionKind, string player, string source, string destination, int moved, int skipped)
		{
			return $"TeleportCompleted: transition={transitionKind} player={player} source={source} destination={destination} moved={moved} skipped={skipped}";
		}

		public static string FormatSkip(TransportTransitionKind transitionKind, string prefabName, string customName, string zdoId, string source, string reason)
		{
			return $"AllySkipped: transition={transitionKind} prefab={prefabName} name={customName} zdo={zdoId} source={source} reason={reason}";
		}

		public static string FormatFiltered(TransportTransitionKind transitionKind, string player, int nonTamed, int outOfRange, int policyDenied)
		{
			return $"AllyFilterSummary: transition={transitionKind} player={player} nonTamed={nonTamed} outOfRange={outOfRange} policyDenied={policyDenied}";
		}

		public static string FormatPosition(float x, float y, float z)
		{
			return string.Format(CultureInfo.InvariantCulture, "({0:0.0}, {1:0.0}, {2:0.0})", x, y, z);
		}
	}
	public enum TransportTransitionKind
	{
		Portal,
		DungeonEntry,
		DungeonExit,
		Basement
	}
	internal static class CreatureTransitionPolicy
	{
		public static bool IsEnabled(AllyTeleportConfig config, TransportTransitionKind kind, bool basementsInstalled)
		{
			switch (kind)
			{
			default:
				return config.Dungeons.Enabled;
			case TransportTransitionKind.Basement:
				if (basementsInstalled)
				{
					return config.Basements.Enabled;
				}
				return false;
			case TransportTransitionKind.Portal:
				return config.Portals.Enabled;
			}
		}

		public static bool IsEnabled(PlayerAllyTeleportConfig config, TransportTransitionKind kind, bool basementsInstalled)
		{
			switch (kind)
			{
			default:
				return config.Dungeons.Enabled;
			case TransportTransitionKind.Basement:
				if (basementsInstalled)
				{
					return config.Basements.Enabled;
				}
				return false;
			case TransportTransitionKind.Portal:
				return config.Portals.Enabled;
			}
		}
	}
	internal sealed class CreatureTransportMetrics
	{
		public int CharactersScanned { get; private set; }

		public int CandidatesEvaluated { get; private set; }

		public int ClearanceChecks { get; private set; }

		public void RecordCharacterScanned()
		{
			CharactersScanned++;
		}

		public void RecordCandidateEvaluated()
		{
			CandidatesEvaluated++;
		}

		public void RecordClearanceCheck()
		{
			ClearanceChecks++;
		}
	}
	internal static class DirectTeleportDecision
	{
		public static bool ShouldCapture(bool teleportSucceeded, bool isLocalPlayer, bool isInterior, bool isBasement, bool nativePortalTransition, bool hasPortalContext, bool transitionEnabled)
		{
			return teleportSucceeded && isLocalPlayer && !isInterior && !isBasement && !nativePortalTransition && hasPortalContext && transitionEnabled;
		}
	}
	internal static class AllyTransport
	{
		private sealed class PendingTransport
		{
			public IReadOnlyList<PendingCreature> Creatures { get; }

			public TransportTransitionKind TransitionKind { get; }

			public PortalContext? PortalContext { get; }

			public Vector3 PlayerSourcePosition { get; }

			public Vector3 RequestedDestination { get; }

			public CreatureTransportMetrics? Metrics { get; }

			public double CaptureElapsedMilliseconds { get; }

			public PendingTransport(IReadOnlyList<PendingCreature> creatures, TransportTransitionKind transitionKind, PortalContext? portalContext, Vector3 playerSourcePosition, Vector3 requestedDestination, CreatureTransportMetrics? metrics, double captureElapsedMilliseconds)
			{
				//IL_001c: Unknown result type (might be due to invalid IL or missing references)
				//IL_001e: Unknown result type (might be due to invalid IL or missing references)
				//IL_0024: Unknown result type (might be due to invalid IL or missing references)
				//IL_0026: Unknown result type (might be due to invalid IL or missing references)
				Creatures = creatures;
				TransitionKind = transitionKind;
				PortalContext = portalContext;
				PlayerSourcePosition = playerSourcePosition;
				RequestedDestination = requestedDestination;
				Metrics = metrics;
				CaptureElapsedMilliseconds = captureElapsedMilliseconds;
			}
		}

		private sealed class PendingCreature
		{
			public ZDOID Id { get; }

			public string PrefabName { get; }

			public string CustomName { get; }

			public Vector3 SourcePosition { get; }

			public Character SourceInstance { get; }

			public float Radius { get; }

			public float Height { get; }

			public Tameable? Tameable { get; }

			public float OriginalUnsummonDistance { get; }

			public PendingCreature(ZDOID id, string prefabName, string customName, Vector3 sourcePosition, Character sourceInstance, float radius, float height, Tameable? tameable, float originalUnsummonDistance)
			{
				//IL_0007: Unknown result type (might be due to invalid IL or missing references)
				//IL_0008: Unknown result type (might be due to invalid IL or missing references)
				//IL_001c: Unknown result type (might be due to invalid IL or missing references)
				//IL_001e: Unknown result type (might be due to invalid IL or missing references)
				Id = id;
				PrefabName = prefabName;
				CustomName = customName;
				SourcePosition = sourcePosition;
				SourceInstance = sourceInstance;
				Radius = radius;
				Height = height;
				Tameable = tameable;
				OriginalUnsummonDistance = originalUnsummonDistance;
			}
		}

		private const int PlacementAttemptsPerAnimal = 12;

		private const int PositionsPerRing = 7;

		private const float ForwardHalfAngleDegrees = 75f;

		private const float MaximumSlopeDegrees = 35f;

		private const float GroundClearance = 0.1f;

		private const float WaterClearance = 0.25f;

		private const float MaximumDungeonFloorHeightDifference = 3f;

		private const int ClearanceBufferSize = 32;

		private static readonly Dictionary<ZDOID, PendingTransport> PendingByPlayer = new Dictionary<ZDOID, PendingTransport>();

		private static readonly Collider[] ClearanceBuffer = (Collider[])(object)new Collider[32];

		public static int CountEligibleAllies(Player player, PortalContext portalContext)
		{
			//IL_00a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ae: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d5: Unknown result type (might be due to invalid IL or missing references)
			if (!TeleportCreaturesPlugin.IsEnabled || !((Character)player).IsOwner() || !portalContext.IsUsable || !TeleportCreaturesPlugin.Policy.IsTransitionEnabled(TransportTransitionKind.Portal, TeleportCreaturesPlugin.BasementsInstalled))
			{
				return 0;
			}
			EffectiveCreaturePolicy policy = TeleportCreaturesPlugin.Policy;
			float allySearchRadius = policy.GetAllySearchRadius();
			float allySearchHeightRadius = policy.GetAllySearchHeightRadius();
			float num = allySearchRadius * allySearchRadius;
			int num2 = 0;
			foreach (Character allCharacter in Character.GetAllCharacters())
			{
				if (!((Object)(object)allCharacter == (Object)null) && allCharacter.IsTamed() && !((Object)(object)allCharacter.m_nview == (Object)null) && allCharacter.m_nview.IsValid() && !(HorizontalDistanceSquared(((Component)allCharacter).transform.position, ((Component)player).transform.position) > num) && !(Mathf.Abs(((Component)allCharacter).transform.position.y - ((Component)player).transform.position.y) > allySearchHeightRadius))
				{
					Tameable component = ((Component)allCharacter).GetComponent<Tameable>();
					MonsterAI component2 = ((Component)allCharacter).GetComponent<MonsterAI>();
					bool isFollowingPlayer = (Object)(object)((component2 != null) ? component2.GetFollowTarget() : null) == (Object)(object)((Component)player).gameObject;
					bool hasCustomName = (Object)(object)component != (Object)null && !string.IsNullOrWhiteSpace(component.GetText());
					if (policy.IsAllowed(allCharacter.m_nview.GetPrefabName(), isTamed: true, isFollowingPlayer, hasCustomName, portalContext))
					{
						num2++;
					}
				}
			}
			return num2;
		}

		public static void CaptureEligibleAllies(Player player, TransportTransitionKind transitionKind, PortalContext? portalContext = null)
		{
			//IL_0257: Unknown result type (might be due to invalid IL or missing references)
			//IL_025c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0279: Unknown result type (might be due to invalid IL or missing references)
			//IL_026b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0290: Unknown result type (might be due to invalid IL or missing references)
			//IL_029b: Unknown result type (might be due to invalid IL or missing references)
			//IL_02a1: Unknown result type (might be due to invalid IL or missing references)
			//IL_02c4: Unknown result type (might be due to invalid IL or missing references)
			//IL_02cf: Unknown result type (might be due to invalid IL or missing references)
			//IL_0139: Unknown result type (might be due to invalid IL or missing references)
			//IL_0144: Unknown result type (might be due to invalid IL or missing references)
			//IL_015d: Unknown result type (might be due to invalid IL or missing references)
			//IL_016d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0202: Unknown result type (might be due to invalid IL or missing references)
			//IL_0217: Unknown result type (might be due to invalid IL or missing references)
			if (!TeleportCreaturesPlugin.IsEnabled || !((Character)player).IsOwner())
			{
				return;
			}
			if (transitionKind == TransportTransitionKind.Portal && (portalContext == null || !portalContext.IsUsable))
			{
				TeleportCreaturesPlugin.LogDiagnostic("Skipped creature portal transport because the source portal context was unavailable.");
				return;
			}
			long timestamp = Stopwatch.GetTimestamp();
			CreatureTransportMetrics creatureTransportMetrics = (TeleportCreaturesPlugin.TransportProfilingEnabled ? new CreatureTransportMetrics() : null);
			EffectiveCreaturePolicy policy = TeleportCreaturesPlugin.Policy;
			_ = policy.ServerPolicy;
			List<PendingCreature> list = new List<PendingCreature>();
			int num = 0;
			int num2 = 0;
			int num3 = 0;
			foreach (Character allCharacter in Character.GetAllCharacters())
			{
				creatureTransportMetrics?.RecordCharacterScanned();
				if ((Object)(object)allCharacter == (Object)null || (Object)(object)allCharacter.m_nview == (Object)null || !allCharacter.m_nview.IsValid())
				{
					continue;
				}
				if (!allCharacter.IsTamed())
				{
					num++;
					continue;
				}
				string prefabName = allCharacter.m_nview.GetPrefabName();
				Tameable component = ((Component)allCharacter).GetComponent<Tameable>();
				MonsterAI component2 = ((Component)allCharacter).GetComponent<MonsterAI>();
				bool isFollowingPlayer = (Object)(object)((component2 != null) ? component2.GetFollowTarget() : null) == (Object)(object)((Component)player).gameObject;
				bool hasCustomName = (Object)(object)component != (Object)null && !string.IsNullOrWhiteSpace(component.GetText());
				if (!policy.IsAllowed(prefabName, isTamed: true, isFollowingPlayer, hasCustomName, portalContext))
				{
					num3++;
					continue;
				}
				if (HorizontalDistance(((Component)allCharacter).transform.position, ((Component)player).transform.position) > policy.GetAllySearchRadius() || Mathf.Abs(((Component)allCharacter).transform.position.y - ((Component)player).transform.position.y) > policy.GetAllySearchHeightRadius())
				{
					num2++;
					continue;
				}
				TeleportCreaturesPlugin.LogSkipped(CreatureTeleportDiagnostics.FormatFiltered(transitionKind, player.GetPlayerName(), num, num2, num3));
				allCharacter.m_nview.ClaimOwnership();
				if (!allCharacter.IsOwner())
				{
					LogSkipped(transitionKind, allCharacter, prefabName, component, "could not claim source network ownership");
					continue;
				}
				float originalUnsummonDistance = (((Object)(object)component != (Object)null) ? component.m_unsummonDistance : 0f);
				if ((Object)(object)component != (Object)null)
				{
					component.m_unsummonDistance = 0f;
				}
				list.Add(new PendingCreature(allCharacter.GetZDOID(), prefabName, GetCustomName(component), ((Component)allCharacter).transform.position, allCharacter, allCharacter.GetRadius(), allCharacter.GetHeight(), component, originalUnsummonDistance));
			}
			ZDOID zDOID = ((Character)player).GetZDOID();
			if (list.Count == 0)
			{
				PendingByPlayer.Remove(zDOID);
				return;
			}
			if (PendingByPlayer.TryGetValue(zDOID, out PendingTransport value))
			{
				RestoreUnsummonDistances(value);
			}
			PendingByPlayer[zDOID] = new PendingTransport(list, transitionKind, portalContext, ((Component)player).transform.position, player.m_teleportTargetPos, creatureTransportMetrics, GetElapsedMilliseconds(timestamp));
			TeleportCreaturesPlugin.LogDiagnostic(CreatureTeleportDiagnostics.FormatCapture(transitionKind, player.GetPlayerName(), FormatPosition(((Component)player).transform.position), FormatPosition(player.m_teleportTargetPos), list.Count) + FormatPortalRule(policy, portalContext));
		}

		public static void CompleteAfterNativeArrival(Player player)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0327: Unknown result type (might be due to invalid IL or missing references)
			//IL_0337: Unknown result type (might be due to invalid IL or missing references)
			//IL_009d: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f7: Unknown result type (might be due to invalid IL or missing references)
			//IL_0206: Unknown result type (might be due to invalid IL or missing references)
			//IL_0213: Unknown result type (might be due to invalid IL or missing references)
			//IL_0221: Unknown result type (might be due to invalid IL or missing references)
			//IL_027b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0288: Unknown result type (might be due to invalid IL or missing references)
			//IL_0299: Unknown result type (might be due to invalid IL or missing references)
			//IL_02a1: Unknown result type (might be due to invalid IL or missing references)
			//IL_02b2: Unknown result type (might be due to invalid IL or missing references)
			//IL_02dd: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ed: Unknown result type (might be due to invalid IL or missing references)
			ZDOID zDOID = ((Character)player).GetZDOID();
			if (!PendingByPlayer.TryGetValue(zDOID, out PendingTransport value))
			{
				return;
			}
			PendingByPlayer.Remove(zDOID);
			if (!TeleportCreaturesPlugin.IsEnabled || !((Character)player).IsOwner())
			{
				TeleportCreaturesPlugin.LogDiagnostic("Discarded pending creature transport because the local player is no longer the owner.");
				return;
			}
			long timestamp = Stopwatch.GetTimestamp();
			EffectiveCreaturePolicy policy = TeleportCreaturesPlugin.Policy;
			CreatureConfig serverPolicy = policy.ServerPolicy;
			List<Vector3> list = new List<Vector3>();
			int num = 0;
			int num2 = 0;
			foreach (PendingCreature creature in value.Creatures)
			{
				Character val = ResolveCharacter(creature);
				object obj;
				if (!((Object)(object)val == (Object)null))
				{
					obj = null;
				}
				else
				{
					ZDOMan instance = ZDOMan.instance;
					obj = ((instance != null) ? instance.GetZDO(creature.Id) : null);
				}
				ZDO val2 = (ZDO)obj;
				CreatureArrivalAction creatureArrivalAction = CreatureArrivalDecision.Select((Object)(object)val != (Object)null, val2 != null, val2 != null && val2.IsOwner());
				if (creatureArrivalAction == CreatureArrivalAction.Skip)
				{
					num2++;
					LogSkipped(value, creature, "no locally-owned network state was available after arrival");
					RestoreUnsummonDistance(creature);
					continue;
				}
				if ((Object)(object)val != (Object)null)
				{
					Tameable component = ((Component)val).GetComponent<Tameable>();
					MonsterAI component2 = ((Component)val).GetComponent<MonsterAI>();
					bool isFollowingPlayer = (Object)(object)((component2 != null) ? component2.GetFollowTarget() : null) == (Object)(object)((Component)player).gameObject;
					bool hasCustomName = (Object)(object)component != (Object)null && !string.IsNullOrWhiteSpace(component.GetText());
					if (!val.IsTamed() || !policy.IsAllowed(val.m_nview.GetPrefabName(), isTamed: true, isFollowingPlayer, hasCustomName, value.PortalContext))
					{
						num2++;
						LogSkipped(value, creature, "it is no longer eligible after arrival");
						RestoreUnsummonDistance(creature);
						continue;
					}
				}
				if (!TryFindSafePlacement(player, creature, serverPolicy, value.TransitionKind, list, num + num2, value.Metrics, out Vector3 placement, out string failureReason))
				{
					num2++;
					LogSkipped(value, creature, failureReason);
					RestoreUnsummonDistance(creature);
					continue;
				}
				if (!CreatureArrivalDecision.ShouldPersistNetworkRecord(creatureArrivalAction))
				{
					num2++;
					LogSkipped(value, creature, "no persistable network state was available after arrival");
					RestoreUnsummonDistance(creature);
					continue;
				}
				if (creatureArrivalAction == CreatureArrivalAction.MoveOwnedRecord)
				{
					val2.SetPosition(placement);
					val2.SetRotation(((Component)player).transform.rotation);
					LogMovement(value, creature, placement, "owned-zdo");
					list.Add(placement);
					num++;
					RestoreUnsummonDistance(creature);
					continue;
				}
				val.m_nview.ClaimOwnership();
				if (!val.IsOwner())
				{
					num2++;
					LogSkipped(value, creature, "could not retain network ownership");
					RestoreUnsummonDistance(creature);
					continue;
				}
				ZDO zDO = val.m_nview.GetZDO();
				zDO.SetPosition(placement);
				zDO.SetRotation(((Component)player).transform.rotation);
				((Component)val).transform.SetPositionAndRotation(placement, ((Component)player).transform.rotation);
				val.m_body.linearVelocity = Vector3.zero;
				val.InvalidateCachedLiquidDepth();
				MonsterAI component3 = ((Component)val).GetComponent<MonsterAI>();
				if (component3 != null)
				{
					component3.SetFollowTarget(((Component)player).gameObject);
				}
				list.Add(placement);
				num++;
				LogMovement(value, creature, placement, "live-instance");
				RestoreUnsummonDistance(creature);
			}
			TeleportCreaturesPlugin.LogDiagnostic(CreatureTeleportDiagnostics.FormatCompletion(value.TransitionKind, player.GetPlayerName(), FormatPosition(value.PlayerSourcePosition), FormatPosition(((Component)player).transform.position), num, num2));
			if (value.TransitionKind == TransportTransitionKind.Portal)
			{
				PortalAllyDisplay.ShowCompletionMessage(num);
			}
			if (value.Metrics != null)
			{
				TeleportCreaturesPlugin.LogDiagnostic($"TransportProfile: transition={value.TransitionKind} scanned={value.Metrics.CharactersScanned} eligible={value.Creatures.Count} candidates={value.Metrics.CandidatesEvaluated} clearanceChecks={value.Metrics.ClearanceChecks} captureMs={value.CaptureElapsedMilliseconds:0.00} arrivalMs={GetElapsedMilliseconds(timestamp):0.00}");
			}
		}

		public static void CancelPendingTransports()
		{
			foreach (PendingTransport value in PendingByPlayer.Values)
			{
				RestoreUnsummonDistances(value);
			}
			PendingByPlayer.Clear();
			PortalTransitionState.Clear();
		}

		private static Character? ResolveCharacter(PendingCreature pendingCreature)
		{
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)pendingCreature.SourceInstance != (Object)null && (Object)(object)pendingCreature.SourceInstance.m_nview != (Object)null && pendingCreature.SourceInstance.m_nview.IsValid())
			{
				return pendingCreature.SourceInstance;
			}
			ZNetScene instance = ZNetScene.instance;
			GameObject val = ((instance != null) ? instance.FindInstance(pendingCreature.Id) : null);
			if (!((Object)(object)val != (Object)null))
			{
				return null;
			}
			return val.GetComponent<Character>();
		}

		private static void RestoreUnsummonDistance(PendingCreature pendingCreature)
		{
			if ((Object)(object)pendingCreature.Tameable != (Object)null)
			{
				pendingCreature.Tameable.m_unsummonDistance = pendingCreature.OriginalUnsummonDistance;
			}
		}

		private static void RestoreUnsummonDistances(PendingTransport pending)
		{
			foreach (PendingCreature creature in pending.Creatures)
			{
				RestoreUnsummonDistance(creature);
			}
		}

		private static void LogMovement(PendingTransport pending, PendingCreature creature, Vector3 destination, string method)
		{
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			TeleportCreaturesPlugin.LogDiagnostic(CreatureTeleportDiagnostics.FormatMovement(pending.TransitionKind, creature.PrefabName, creature.CustomName, ((object)creature.Id/*cast due to .constrained prefix*/).ToString(), FormatPosition(creature.SourcePosition), FormatPosition(destination), method));
		}

		private static void LogSkipped(TransportTransitionKind transitionKind, Character creature, string prefabName, Tameable? tameable, string reason)
		{
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			TeleportCreaturesPlugin.LogSkipped(CreatureTeleportDiagnostics.FormatSkip(transitionKind, prefabName, GetCustomName(tameable), ((object)creature.GetZDOID()/*cast due to .constrained prefix*/).ToString(), FormatPosition(((Component)creature).transform.position), reason));
		}

		private static void LogSkipped(PendingTransport pending, PendingCreature creature, string reason)
		{
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			TeleportCreaturesPlugin.LogSkipped(CreatureTeleportDiagnostics.FormatSkip(pending.TransitionKind, creature.PrefabName, creature.CustomName, ((object)creature.Id/*cast due to .constrained prefix*/).ToString(), FormatPosition(creature.SourcePosition), reason));
		}

		private static string GetCustomName(Tameable? tameable)
		{
			if (!((Object)(object)tameable != (Object)null) || string.IsNullOrWhiteSpace(tameable.GetText()))
			{
				return "-";
			}
			return tameable.GetText();
		}

		private static string FormatPosition(Vector3 position)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			return CreatureTeleportDiagnostics.FormatPosition(position.x, position.y, position.z);
		}

		private static bool TryFindSafePlacement(Player player, PendingCreature creature, CreatureConfig config, TransportTransitionKind transitionKind, IReadOnlyCollection<Vector3> reservedPlacements, int animalIndex, CreatureTransportMetrics? metrics, out Vector3 placement, out string failureReason)
		{
			//IL_0207: Unknown result type (might be due to invalid IL or missing references)
			//IL_0087: Unknown result type (might be due to invalid IL or missing references)
			//IL_0092: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00af: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c9: Unknown result type (might be due to invalid IL or missing references)
			//IL_0109: Unknown result type (might be due to invalid IL or missing references)
			//IL_015d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0145: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a9: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e9: Unknown result type (might be due to invalid IL or missing references)
			//IL_01eb: Unknown result type (might be due to invalid IL or missing references)
			float num = transitionKind switch
			{
				TransportTransitionKind.Portal => config.Placement.Radius, 
				TransportTransitionKind.Basement => config.Placement.Basements.Radius, 
				_ => config.Placement.Dungeons.Radius, 
			};
			int num2 = Mathf.Max(1, Mathf.FloorToInt((num - config.Placement.DistanceFromPlayer + 0.001f) / config.Placement.AnimalSpacing) + 1);
			int num3 = Mathf.Max(12, num2 * 7);
			for (int i = 0; i < num3; i++)
			{
				metrics?.RecordCandidateEvaluated();
				Vector3 val = CreateCandidate(((Component)player).transform.position, ((Component)player).transform.rotation, config.Placement, animalIndex, i);
				if (HorizontalDistance(val, ((Component)player).transform.position) > num)
				{
					failureReason = "the landing search reached its configured radius";
					continue;
				}
				if (!TryGetGround(val, transitionKind, config.Placement.MaximumHeightDifference, out Vector3 groundPosition, out float solidHeight, out string failureReason2))
				{
					failureReason = failureReason2;
					continue;
				}
				if (transitionKind == TransportTransitionKind.DungeonExit && config.Placement.Dungeons.RequireNaturalGroundOnExit && !CreaturePlacementPolicy.IsNaturalGround(ZoneSystem.instance.GetGroundHeight(val), solidHeight, 0.5f))
				{
					failureReason = "the landing surface was above natural terrain";
					continue;
				}
				if ((transitionKind != TransportTransitionKind.Basement || config.Placement.Basements.RequireClearance) && !HasCreatureClearance(creature, groundPosition, metrics))
				{
					failureReason = "the landing space was blocked by a wall, ceiling, furniture, or structure";
					continue;
				}
				if (!MatchesWaterPolicy(groundPosition, config.Placement.Water))
				{
					failureReason = $"no location matched Placement.Water = {config.Placement.Water}";
					continue;
				}
				bool flag = false;
				foreach (Vector3 reservedPlacement in reservedPlacements)
				{
					if (HorizontalDistance(groundPosition, reservedPlacement) < config.Placement.AnimalSpacing)
					{
						flag = true;
						break;
					}
				}
				if (flag)
				{
					failureReason = "no location met Placement.AnimalSpacing";
					continue;
				}
				placement = groundPosition;
				failureReason = string.Empty;
				return true;
			}
			placement = default(Vector3);
			failureReason = "no safe landing location was found";
			return false;
		}

		private static Vector3 CreateCandidate(Vector3 playerPosition, Quaternion rotation, PlacementConfig config, int animalIndex, int attempt)
		{
			//IL_002e: 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_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_0055: 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_0057: Unknown result type (might be due to invalid IL or missing references)
			int candidateSlot = CreaturePlacementPolicy.GetCandidateSlot(animalIndex, attempt, 7);
			int num = candidateSlot / 7 + 1;
			float num2 = config.DistanceFromPlayer + (float)(num - 1) * config.AnimalSpacing;
			float forwardSearchAngle = CreaturePlacementPolicy.GetForwardSearchAngle(candidateSlot, 7, 75f);
			Vector3 val = rotation * (Quaternion.Euler(0f, forwardSearchAngle, 0f) * Vector3.forward * num2);
			return playerPosition + val;
		}

		private static bool TryGetGround(Vector3 candidate, TransportTransitionKind transitionKind, float maximumHeightDifference, out Vector3 groundPosition, out float solidHeight, out string failureReason)
		{
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_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)
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e4: Unknown result type (might be due to invalid IL or missing references)
			//IL_0070: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f3: Unknown result type (might be due to invalid IL or missing references)
			//IL_007a: Unknown result type (might be due to invalid IL or missing references)
			//IL_010f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d0: Unknown result type (might be due to invalid IL or missing references)
			//IL_008e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0167: Unknown result type (might be due to invalid IL or missing references)
			//IL_0168: Unknown result type (might be due to invalid IL or missing references)
			//IL_013b: Unknown result type (might be due to invalid IL or missing references)
			//IL_011f: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a2: Unknown result type (might be due to invalid IL or missing references)
			//IL_01af: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b5: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_017a: Unknown result type (might be due to invalid IL or missing references)
			//IL_014f: Unknown result type (might be due to invalid IL or missing references)
			if (CreaturePlacementPolicy.UsesNativePlayerLevel(transitionKind == TransportTransitionKind.Basement))
			{
				groundPosition = new Vector3(candidate.x, candidate.y + 0.1f, candidate.z);
				solidHeight = candidate.y;
				failureReason = string.Empty;
				return true;
			}
			if ((Object)(object)ZoneSystem.instance == (Object)null)
			{
				groundPosition = default(Vector3);
				solidHeight = 0f;
				failureReason = "the destination terrain was not ready";
				return false;
			}
			float num = default(float);
			if (transitionKind == TransportTransitionKind.DungeonEntry && ZoneSystem.instance.FindFloor(candidate, ref num))
			{
				if (!CreaturePlacementPolicy.IsOnPlayerLevel(candidate.y, num, 3f))
				{
					groundPosition = default(Vector3);
					solidHeight = 0f;
					failureReason = "the dungeon floor was not on the player's current vertical level";
					return false;
				}
				solidHeight = CreaturePlacementPolicy.SelectLandingHeight(hasInteriorFloor: true, num, 0f);
				groundPosition = new Vector3(candidate.x, solidHeight + 0.1f, candidate.z);
				failureReason = string.Empty;
				return true;
			}
			float num2 = default(float);
			Vector3 val = default(Vector3);
			GameObject val2 = default(GameObject);
			if (!ZoneSystem.instance.GetSolidHeight(candidate, ref num2, ref val, ref val2))
			{
				groundPosition = default(Vector3);
				solidHeight = 0f;
				failureReason = "the destination terrain was not ready";
				return false;
			}
			if (transitionKind == TransportTransitionKind.Basement && !CreaturePlacementPolicy.IsOnPlayerLevel(candidate.y, num2, maximumHeightDifference))
			{
				groundPosition = default(Vector3);
				solidHeight = 0f;
				failureReason = "the basement surface was on another vertical layer";
				return false;
			}
			if (transitionKind == TransportTransitionKind.DungeonEntry && !CreaturePlacementPolicy.CanUseDungeonFallbackHeight(candidate.y, num2, 3f))
			{
				groundPosition = default(Vector3);
				solidHeight = 0f;
				failureReason = "the dungeon fallback surface was not on the player's current vertical level";
				return false;
			}
			if (Vector3.Angle(val, Vector3.up) > 35f)
			{
				groundPosition = default(Vector3);
				solidHeight = 0f;
				failureReason = $"the destination slope exceeded {35f:0} degrees";
				return false;
			}
			groundPosition = new Vector3(candidate.x, num2 + 0.1f, candidate.z);
			solidHeight = CreaturePlacementPolicy.SelectLandingHeight(hasInteriorFloor: false, 0f, num2);
			failureReason = string.Empty;
			return true;
		}

		private static bool HasCreatureClearance(PendingCreature creature, Vector3 groundPosition, CreatureTransportMetrics? metrics)
		{
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			//IL_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)
			metrics?.RecordClearanceCheck();
			float num = Mathf.Max(0.2f, creature.Radius * 0.9f);
			float num2 = Mathf.Max(num * 2f, creature.Height * 0.9f);
			Vector3 val = groundPosition + Vector3.up * num;
			Vector3 val2 = groundPosition + Vector3.up * (num2 - num);
			return CreaturePlacementPolicy.IsClearanceFree(Physics.OverlapCapsuleNonAlloc(val, val2, num, ClearanceBuffer, -5, (QueryTriggerInteraction)1), ClearanceBuffer.Length);
		}

		private static bool MatchesWaterPolicy(Vector3 groundPosition, WaterPolicy policy)
		{
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			bool flag = false;
			foreach (WaterVolume instance in WaterVolume.Instances)
			{
				if ((Object)(object)instance != (Object)null && instance.GetWaterSurface(groundPosition, Time.time) > groundPosition.y + 0.25f)
				{
					flag = true;
					break;
				}
			}
			return policy switch
			{
				WaterPolicy.Allow => true, 
				WaterPolicy.Require => flag, 
				_ => !flag, 
			};
		}

		private static float HorizontalDistance(Vector3 first, Vector3 second)
		{
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			first.y = 0f;
			second.y = 0f;
			return Vector3.Distance(first, second);
		}

		private static float HorizontalDistanceSquared(Vector3 first, Vector3 second)
		{
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			first.y = 0f;
			second.y = 0f;
			Vector3 val = first - second;
			return ((Vector3)(ref val)).sqrMagnitude;
		}

		private static double GetElapsedMilliseconds(long startTimestamp)
		{
			return (double)(Stopwatch.GetTimestamp() - startTimestamp) * 1000.0 / (double)Stopwatch.Frequency;
		}

		private static string FormatPortalRule(EffectiveCreaturePolicy policy, PortalContext? context)
		{
			if (context == null)
			{
				return string.Empty;
			}
			PortalRuleConfig portalRuleConfig = PortalRuleResolver.Select(policy.ServerPolicy.Allies.Teleport.Portals, context);
			PortalRuleConfig portalRuleConfig2 = PortalRuleResolver.Select(policy.PlayerPreferences.Allies.Teleport.Portals, context);
			return ", portalPrefab=" + context.PrefabName + ", portalTag=" + context.Tag + ", serverPortalRule=" + (portalRuleConfig?.Id ?? "default") + ", playerPortalRule=" + (portalRuleConfig2?.Id ?? "default");
		}
	}
	internal static class PortalAllyDisplay
	{
		private const float RefreshIntervalSeconds = 0.25f;

		private const float StaleTimeoutSeconds = 1f;

		private const string TranslationKey = "$tc_transporting_allies_message";

		private const string CompletionTranslationKey = "$tc_transported_allies_message";

		private static TeleportWorld? _trackedPortal;

		private static TMP_Text? _counterText;

		private static AllyTransportMessageMode _counterMode;

		private static float _nextRefreshTime;

		private static float _nextCenterMessageTime;

		private static int _lastCenterMessageCount;

		private static float _lastShownTime;

		private static bool _reportedUpdateError;

		public static void UpdateForPortal(TeleportWorld portal)
		{
			try
			{
				UpdateForPortalSafely(portal);
			}
			catch (Exception ex)
			{
				Hide();
				if (!_reportedUpdateError)
				{
					_reportedUpdateError = true;
					TeleportCreaturesPlugin.Log.LogWarning((object)("Could not update the local portal ally counter: " + ex.Message));
				}
			}
		}

		public static void ShowCompletionMessage(int allyCount)
		{
			if (allyCount > 0 && !((Object)(object)Player.m_localPlayer == (Object)null) && !((Character)Player.m_localPlayer).IsDead() && !((Object)(object)MessageHud.instance == (Object)null) && Localization.instance != null)
			{
				MessageHud.instance.ShowMessage((MessageType)1, LocalizeCompletionCount(allyCount), 0, (Sprite)null, false, true);
			}
		}

		public static void Dispose()
		{
			if ((Object)(object)_counterText != (Object)null)
			{
				Object.Destroy((Object)(object)((Component)_counterText).gameObject);
			}
			_counterText = null;
			_trackedPortal = null;
			_nextRefreshTime = 0f;
			_nextCenterMessageTime = 0f;
			_lastCenterMessageCount = 0;
			_lastShownTime = 0f;
			_reportedUpdateError = false;
		}

		private static void UpdateForPortalSafely(TeleportWorld portal)
		{
			if (TeleportCreaturesPlugin.AllyTransportMessageMode == AllyTransportMessageMode.NoMessages)
			{
				Hide();
				return;
			}
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null || ((Character)localPlayer).IsDead() || (Object)(object)portal == (Object)null || (Object)(object)portal.m_proximityRoot == (Object)null || !IsPlayerWithinRange(localPlayer, portal))
			{
				if ((Object)(object)portal != (Object)null)
				{
					HideIfTracked(portal);
				}
				return;
			}
			if (!portal.HaveTarget() || !((Humanoid)localPlayer).IsTeleportable(portal.m_allowAllItems))
			{
				HideIfTracked(portal);
				return;
			}
			_trackedPortal = portal;
			if (Time.time < _nextRefreshTime)
			{
				return;
			}
			_nextRefreshTime = Time.time + 0.25f;
			if (!PortalContextReader.TryRead(portal, out PortalContext context))
			{
				Hide();
				return;
			}
			int allyCount = AllyTransport.CountEligibleAllies(localPlayer, context);
			if (!AllyTransportDisplayPolicy.ShouldShowMessage(TeleportCreaturesPlugin.AllyTransportMessageMode, allyCount))
			{
				Hide();
				return;
			}
			if (TeleportCreaturesPlugin.AllyTransportMessageMode == AllyTransportMessageMode.TopCenter)
			{
				ShowTopCenterMessage(allyCount);
				Hide();
				return;
			}
			TMP_Text orCreateCounter = GetOrCreateCounter(TeleportCreaturesPlugin.AllyTransportMessageMode);
			if (!((Object)(object)orCreateCounter == (Object)null))
			{
				orCreateCounter.text = LocalizeCount(allyCount);
				((Component)orCreateCounter).gameObject.SetActive(true);
				_lastShownTime = Time.time;
			}
		}

		public static void HideIfStale()
		{
			if (!((Object)(object)_counterText == (Object)null) && ((Component)_counterText).gameObject.activeSelf && Time.time - _lastShownTime > 1f)
			{
				Hide();
			}
		}

		public static void Hide()
		{
			if ((Object)(object)_counterText != (Object)null)
			{
				((Component)_counterText).gameObject.SetActive(false);
			}
			_trackedPortal = null;
			_nextRefreshTime = 0f;
		}

		private static bool IsPlayerWithinRange(Player player, TeleportWorld portal)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			Vector3 val = ((Component)player).transform.position - portal.m_proximityRoot.position;
			return ((Vector3)(ref val)).sqrMagnitude <= portal.m_activationRange * portal.m_activationRange;
		}

		private static TMP_Text? GetOrCreateCounter(AllyTransportMessageMode mode)
		{
			//IL_0113: Unknown result type (might be due to invalid IL or missing references)
			//IL_0131: Unknown result type (might be due to invalid IL or missing references)
			//IL_014f: Unknown result type (might be due to invalid IL or missing references)
			//IL_016d: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00dc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e1: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)_counterText != (Object)null && _counterMode == mode)
			{
				return _counterText;
			}
			if ((Object)(object)_counterText != (Object)null)
			{
				Object.Destroy((Object)(object)((Component)_counterText).gameObject);
			}
			TMP_Text val = (TMP_Text)((mode != AllyTransportMessageMode.TopLeft) ? ((object)Hud.instance?.m_hoverName) : ((object)MessageHud.instance?.m_messageText));
			if ((Object)(object)val == (Object)null)
			{
				return null;
			}
			_counterText = Object.Instantiate<TMP_Text>(val, val.transform.parent);
			((Object)_counterText).name = "TeleportCreaturesAllyCounter";
			_counterText.textWrappingMode = (TextWrappingModes)0;
			_counterText.fontSize = 18f;
			if (mode == AllyTransportMessageMode.TopLeft)
			{
				_counterText.alignment = (TextAlignmentOptions)257;
				_counterText.rectTransform.anchoredPosition = val.rectTransform.anchoredPosition + new Vector2(0f, -36f);
			}
			else
			{
				_counterText.alignment = (TextAlignmentOptions)514;
				_counterText.rectTransform.anchorMin = new Vector2(0.5f, 0.5f);
				_counterText.rectTransform.anchorMax = new Vector2(0.5f, 0.5f);
				_counterText.rectTransform.pivot = new Vector2(0.5f, 0.5f);
				_counterText.rectTransform.anchoredPosition = new Vector2(0f, 72f);
			}
			_counterMode = mode;
			((Component)_counterText).gameObject.SetActive(false);
			return _counterText;
		}

		private static string LocalizeCount(int allyCount)
		{
			return Localization.instance.Localize("$tc_transporting_allies_message", new string[1] { allyCount.ToString() });
		}

		private static void ShowTopCenterMessage(int allyCount)
		{
			if (!((Object)(object)MessageHud.instance == (Object)null) && (allyCount != _lastCenterMessageCount || !(Time.time < _nextCenterMessageTime)))
			{
				MessageHud.instance.ShowMessage((MessageType)2, LocalizeCount(allyCount), 0, (Sprite)null, false, true);
				_lastCenterMessageCount = allyCount;
				_nextCenterMessageTime = Time.time + 1f;
			}
		}

		private static string LocalizeCompletionCount(int allyCount)
		{
			return Localization.instance.Localize("$tc_transported_allies_message", new string[1] { allyCount.ToString() });
		}

		private static void HideIfTracked(TeleportWorld portal)
		{
			if ((Object)(object)_trackedPortal == (Object)(object)portal)
			{
				Hide();
			}
		}
	}
	internal static class PortalTransitionState
	{
		internal readonly struct DirectTeleportCapture
		{
			public bool IsValid { get; }

			public Vector3 SourcePosition { get; }

			public PortalContext Context { get; }

			public DirectTeleportCapture(bool isValid, Vector3 sourcePosition, PortalContext context)
			{
				//IL_0008: Unknown result type (might be due to invalid IL or missing references)
				//IL_0009: Unknown result type (might be due to invalid IL or missing references)
				IsValid = isValid;
				SourcePosition = sourcePosition;
				Context = context;
			}
		}

		private const float SourceStateLifetimeSeconds = 30f;

		private const float MaximumSourceDistance = 10f;

		private static TeleportWorld? _sourcePortal;

		private static Vector3 _sourcePosition;

		private static float _sourceCapturedAt;

		private static bool _hasSource;

		private static bool _nativePortalTransition;

		private static bool _directTeleportInProgress;

		public static void ObservePortalEntry(TeleportWorld portal, Player player)
		{
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)portal == (Object)null) && !((Object)(object)player == (Object)null) && !((Object)(object)player != (Object)(object)Player.m_localPlayer))
			{
				_sourcePortal = portal;
				_sourcePosition = ((Component)player).transform.position;
				_sourceCapturedAt = Time.time;
				_hasSource = true;
				_directTeleportInProgress = false;
			}
		}

		public static void BeginNativePortalTransition()
		{
			_nativePortalTransition = true;
		}

		public static void EndNativePortalTransition()
		{
			_nativePortalTransition = false;
			_directTeleportInProgress = false;
		}

		public static DirectTeleportCapture BeginDirectTeleport(Player player)
		{
			//IL_0043: 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_0088: Unknown result type (might be due to invalid IL or missing references)
			if (_nativePortalTransition || _directTeleportInProgress || (Object)(object)player == (Object)null || (Object)(object)player != (Object)(object)Player.m_localPlayer || !_hasSource || Time.time - _sourceCapturedAt > 30f || Vector3.Distance(((Component)player).transform.position, _sourcePosition) > 10f)
			{
				return default(DirectTeleportCapture);
			}
			if (!TryReadContext(_sourcePortal, out PortalContext context))
			{
				_sourcePortal = null;
				return default(DirectTeleportCapture);
			}
			_directTeleportInProgress = true;
			return new DirectTeleportCapture(isValid: true, _sourcePosition, context);
		}

		public static void EndDirectTeleport()
		{
			_directTeleportInProgress = false;
			ClearSource();
		}

		public static void Clear()
		{
			_nativePortalTransition = false;
			_directTeleportInProgress = false;
			ClearSource();
		}

		private static void ClearSource()
		{
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			_sourcePortal = null;
			_sourcePosition = default(Vector3);
			_sourceCapturedAt = 0f;
			_hasSource = false;
		}

		private static bool TryReadContext(TeleportWorld? portal, out PortalContext context)
		{
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)portal != (Object)null && PortalContextReader.TryRead(portal, out context))
			{
				return true;
			}
			TeleportWorld[] array = Object.FindObjectsOfType<TeleportWorld>();
			foreach (TeleportWorld val in array)
			{
				if (!((Object)(object)val == (Object)null) && !(Vector3.Distance(((Component)val).transform.position, _sourcePosition) > 10f) && PortalContextReader.TryRead(val, out context))
				{
					return true;
				}
			}
			context = null;
			return false;
		}
	}
	[HarmonyPatch(typeof(Player), "TeleportTo")]
	internal static class DirectPortalCreatureTeleportPatch
	{
		private static void Prefix(Player __instance, out PortalTransitionState.DirectTeleportCapture __state)
		{
			__state = PortalTransitionState.BeginDirectTeleport(__instance);
		}

		private static void Postfix(Player __instance, bool __result, PortalTransitionState.DirectTeleportCapture __state)
		{
			if (!__state.IsValid)
			{
				return;
			}
			try
			{
				bool isBasement = TeleportCreaturesPlugin.BasementsInstalled && (Object)(object)EnvMan.instance != (Object)null && EnvMan.instance.GetCurrentEnvironment().m_name == "Basement";
				if (DirectTeleportDecision.ShouldCapture(__result, (Object)(object)__instance == (Object)(object)Player.m_localPlayer, ((Character)__instance).InInterior(), isBasement, nativePortalTransition: false, __state.IsValid, TeleportCreaturesPlugin.IsEnabled && TeleportCreaturesPlugin.Policy.IsTransitionEnabled(TransportTransitionKind.Portal, TeleportCreaturesPlugin.BasementsInstalled)))
				{
					AllyTransport.CaptureEligibleAllies(__instance, TransportTransitionKind.Portal, __state.Context);
					TeleportCreaturesPlugin.LogDiagnostic("Captured creature portal transport through the direct Player.TeleportTo compatibility path.");
				}
			}
			catch (Exception arg)
			{
				TeleportCreaturesPlugin.Log.LogError((object)$"Could not capture creatures for a direct portal teleport: {arg}");
			}
			finally
			{
				PortalTransitionState.EndDirectTeleport();
			}
		}
	}
	[HarmonyPatch(typeof(Teleport), "Interact")]
	internal static class DungeonCreatureTeleportPatch
	{
		private static void Postfix(Humanoid character, bool hold, Teleport __instance, bool __result)
		{
			if (!__result || hold || (Object)(object)__instance.m_targetPoint == (Object)null)
			{
				return;
			}
			Player val = (Player)(object)((character is Player) ? character : null);
			if (val == null || !TeleportCreaturesPlugin.IsEnabled || !((Character)val).IsTeleporting())
			{
				return;
			}
			TransportTransitionKind transportTransitionKind = ((TeleportCreaturesPlugin.BasementsInstalled && (Object)(object)EnvMan.instance != (Object)null && (EnvMan.instance.GetCurrentEnvironment().m_name == "Basement" || EnvMan.instance.GetCurrentEnvironment().m_name == "Undercroft")) ? TransportTransitionKind.Basement : ((!((Character)val).InInterior()) ? TransportTransitionKind.DungeonEntry : TransportTransitionKind.DungeonExit));
			if (!TeleportCreaturesPlugin.Policy.IsTransitionEnabled(transportTransitionKind, TeleportCreaturesPlugin.BasementsInstalled))
			{
				return;
			}
			try
			{
				AllyTransport.CaptureEligibleAllies(val, transportTransitionKind);
			}
			catch (Exception arg)
			{
				TeleportCreaturesPlugin.Log.LogError((object)$"Could not capture creatures for a {transportTransitionKind} teleport: {arg}");
			}
		}
	}
	[HarmonyPatch(typeof(Player), "UpdateTeleport")]
	[HarmonyPriority(0)]
	internal static class PlayerCreatureArrivalPatch
	{
		private static void Prefix(Player __instance, out bool __state)
		{
			__state = ((Character)__instance).IsTeleporting();
		}

		private static void Postfix(Player __instance, bool __state)
		{
			if (!__state || ((Character)__instance).IsTeleporting())
			{
				return;
			}
			try
			{
				AllyTransport.CompleteAfterNativeArrival(__instance);
			}
			catch (Exception arg)
			{
				TeleportCreaturesPlugin.Log.LogError((object)$"Could not reconcile creatures after portal arrival: {arg}");
			}
		}
	}
	[HarmonyPatch(typeof(Player), "Update")]
	internal static class PlayerAllyDisplayStalePatch
	{
		private static void Postfix(Player __instance)
		{
			if (!((Object)(object)__instance != (Object)(object)Player.m_localPlayer))
			{
				if (((Character)__instance).IsDead())
				{
					PortalAllyDisplay.Hide();
				}
				else
				{
					PortalAllyDisplay.HideIfStale();
				}
			}
		}
	}
	[HarmonyPatch(typeof(Player), "OnDeath")]
	internal static class PlayerAllyDisplayDeathPatch
	{
		private static void Postfix(Player __instance)
		{
			if ((Object)(object)__instance == (Object)(object)Player.m_localPlayer)
			{
				PortalAllyDisplay.Hide();
			}
		}
	}
	[HarmonyPatch(typeof(TeleportWorld), "Teleport")]
	internal static class TeleportWorldCreaturePatch
	{
		private static void Prefix()
		{
			PortalTransitionState.BeginNativePortalTransition();
		}

		private static void Postfix(TeleportWorld __instance, Player player)
		{
			try
			{
				if (TeleportCreaturesPlugin.IsEnabled && ((Character)player).IsTeleporting() && TeleportCreaturesPlugin.Policy.IsTransitionEnabled(TransportTransitionKind.Portal, TeleportCreaturesPlugin.BasementsInstalled))
				{
					if (!PortalContextReader.TryRead(__instance, out PortalContext context))
					{
						TeleportCreaturesPlugin.LogDiagnostic("Skipped creature portal transport because the source portal prefab or tag could not be read safely.");
					}
					else
					{
						AllyTransport.CaptureEligibleAllies(player, TransportTransitionKind.Portal, context);
					}
				}
			}
			catch (Exception arg)
			{
				TeleportCreaturesPlugin.Log.LogError((object)$"Could not capture creatures for a portal teleport: {arg}");
			}
			finally
			{
				PortalTransitionState.EndNativePortalTransition();
			}
		}
	}
	[HarmonyPatch(typeof(TeleportWorldTrigger), "OnTriggerEnter")]
	internal static class TeleportWorldTriggerCreaturePatch
	{
		private static void Prefix(TeleportWorldTrigger __instance, Collider colliderIn)
		{
			Player component = ((Component)colliderIn).GetComponent<Player>();
			if ((Object)(object)component != (Object)null && (Object)(object)__instance.m_teleportWorld != (Object)null)
			{
				PortalTransitionState.ObservePortalEntry(__instance.m_teleportWorld, component);
			}
		}
	}
	[HarmonyPatch(typeof(TeleportWorld), "UpdatePortal")]
	internal static class TeleportWorldAllyDisplayPatch
	{
		private static void Postfix(TeleportWorld __instance)
		{
			PortalAllyDisplay.UpdateForPortal(__instance);
		}
	}
	internal static class PortalContextReader
	{
		public static bool TryRead(TeleportWorld portal, out PortalContext context)
		{
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			context = null;
			try
			{
				if ((Object)(object)portal == (Object)null || (Object)(object)portal.m_nview == (Object)null || !portal.m_nview.IsValid())
				{
					return false;
				}
				string prefabName = portal.m_nview.GetPrefabName();
				if (string.IsNullOrWhiteSpace(prefabName))
				{
					return false;
				}
				context = new PortalContext(prefabName, portal.GetTagInfo().text);
				return true;
			}
			catch (Exception ex)
			{
				TeleportCreaturesPlugin.LogDiagnostic("Could not read source portal context: " + ex.Message);
				return false;
			}
		}
	}
	[BepInPlugin("odinplus.TeleportCreatures", "TeleportCreatures", "0.2.4")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public sealed class TeleportCreaturesPlugin : BaseUnityPlugin
	{
		private enum Toggle
		{
			On = 1,
			Off = 0
		}

		internal const string Author = "odinplus";

		internal const string ModGuid = "odinplus.TeleportCreatures";

		internal const string ModName = "TeleportCreatures";

		internal const string ModVersion = "0.2.4";

		private const string ConfigFileName = "odinplus.TeleportCreatures.cfg";

		private static readonly ConfigSync Sync = new ConfigSync("odinplus.TeleportCreatures")
		{
			DisplayName = "TeleportCreatures",
			CurrentVersion = "0.2.4",
			MinimumRequiredVersion = "0.2.4"
		};

		private static readonly CustomSyncedValue<string> Snapshot = new CustomSyncedValue<string>(Sync, "YamlPolicy", string.Empty);

		private static ConfigEntry<Toggle>? _forceServerConfig;

		private static ConfigEntry<bool>? _enableMod;

		private static ConfigEntry<bool>? _enableDiagnostics;

		private static ConfigEntry<bool>? _logSkippedAnimals;

		private static ConfigEntry<bool>? _profileCreatureTransport;

		private static ConfigEntry<string>? _allyTransportMessageMode;

		private readonly Harmony _harmony = new Harmony("odinplus.TeleportCreatures");

		private readonly ISerializer _serializer = new SerializerBuilder().Build();

		private FileSystemWatcher? _serverYamlWatcher;

		private FileSystemWatcher? _playerYamlWatcher;

		private FileSystemWatcher? _configWatcher;

		private string _serverYamlPath;

		private string _playerYamlPath;

		private GameObject? _yamlEditorHost;

		public static readonly ManualLogSource TeleportCreaturesLogger = Logger.CreateLogSource("TeleportCreatures");

		internal static ManualLogSource Log = TeleportCreaturesLogger;

		internal static CreatureConfig ServerPolicy { get; private set; } = new CreatureConfig();

		internal static PlayerCreaturePreferences PlayerPreferences { get; private set; } = PlayerCreaturePreferences.CreateDefault();

		internal static EffectiveCreaturePolicy Policy { get; private set; } = EffectiveCreaturePolicy.Create(ServerPolicy, PlayerPreferences);

		internal static bool IsEnabled => _enableMod?.Value ?? false;

		internal static bool DiagnosticsEnabled => _enableDiagnostics?.Value ?? false;

		internal static bool LogSkippedAnimals => _logSkippedAnimals?.Value ?? false;

		internal static bool TransportProfilingEnabled
		{
			get
			{
				if (DiagnosticsEnabled)
				{
					return _profileCreatureTransport?.Value ?? false;
				}
				return false;
			}
		}

		internal static AllyTransportMessageMode AllyTransportMessageMode => AllyTransportDisplayPolicy.ParseMessageMode(_allyTransportMessageMode?.Value);

		internal static bool BasementsInstalled { get; private set; }

		internal static bool IsBlocked { get; private set; }

		private void Awake()
		{
			Log = TeleportCreaturesLogger;
			FindIncompatibleMods();
			BindConfiguration();
			_serverYamlPath = Path.Combine(Paths.ConfigPath, "odinplus.TeleportCreatures.server.yml");
			_playerYamlPath = Path.Combine(Paths.ConfigPath, "odinplus.TeleportCreatures.yml");
			Snapshot.ValueChanged += ReadSnapshot;
			if (Sync.IsSourceOfTruth)
			{
				ReloadServerYaml();
				SetupServerYamlWatcher();
			}
			ReloadPlayerYaml();
			SetupPlayerYamlWatcher();
			SetupConfigWatcher();
			RegisterYamlEditor();
			Localizer.Load();
			_harmony.PatchAll(Assembly.GetExecutingAssembly());
		}

		private void Start()
		{
			if (!IsBlocked)
			{
				BasementsInstalled = Chainloader.PluginInfos.ContainsKey("com.rolopogo.Basement");
				((BaseUnityPlugin)this).Logger.LogInfo((object)string.Format("{0} loaded. enabled={1}, yamlSource={2}.", "TeleportCreatures", IsEnabled, Sync.IsSourceOfTruth));
			}
		}

		private void OnDestroy()
		{
			if (!IsBlocked)
			{
				_serverYamlWatcher?.Dispose();
				_playerYamlWatcher?.Dispose();
				_configWatcher?.Dispose();
				if ((Object)(object)_yamlEditorHost != (Object)null)
				{
					Object.Destroy((Object)(object)_yamlEditorHost);
				}
				Snapshot.ValueChanged -= ReadSnapshot;
				AllyTransport.CancelPendingTransports();
				PortalAllyDisplay.Dispose();
				_harmony.UnpatchSelf();
				((BaseUnityPlugin)this).Config.Save();
			}
		}

		private void BindConfiguration()
		{
			//IL_00a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b2: Expected O, but got Unknown
			//IL_00fe: Unknown result type (might be due to invalid IL or missing references)
			//IL_0108: Expected O, but got Unknown
			//IL_013e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0148: Expected O, but got Unknown
			//IL_017e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0188: Expected O, but got Unknown
			bool saveOnConfigSet = ((BaseUnityPlugin)this).Config.SaveOnConfigSet;
			((BaseUnityPlugin)this).Config.SaveOnConfigSet = false;
			try
			{
				_forceServerConfig = BindSynced("General", "Force Server Config", Toggle.On, "When On, the server's synchronized TeleportCreatures settings are enforced for connected clients.");
				Sync.AddLockingConfigEntry<Toggle>(_forceServerConfig);
				_enableMod = BindSynced("General", "Enable Mod", defaultValue: true, "Master switch for TeleportCreatures portal transport.");
				_allyTransportMessageMode = ((BaseUnityPlugin)this).Config.Bind<string>("Allies Display", "Portal Message Mode", "Top left", new ConfigDescription("Shows a local live count of eligible allies while you stand in an active portal. No messages hides it.", (AcceptableValueBase)(object)new AcceptableValueList<string>(new string[4] { "No messages", "Top left", "Top center", "Center" }), Array.Empty<object>()));
				YamlEditorButton.Bind(((BaseUnityPlugin)this).Config, "Advanced Configuration", "Teleport Rules");
				_enableDiagnostics = ((BaseUnityPlugin)this).Config.Bind<bool>("Diagnostics", "Enable Creature Teleport Diagnostics", false, new ConfigDescription("Writes capture, arrival, and per-animal placement details to this machine's log.", (AcceptableValueBase)null, new object[1]
				{
					new Odinplus.Shared.YamlEditor.ConfigurationManagerAttributes
					{
						IsAdvanced = true
					}
				}));
				_logSkippedAnimals = ((BaseUnityPlugin)this).Config.Bind<bool>("Diagnostics", "Log Skipped Animals", false, new ConfigDescription("When diagnostics are enabled, writes an individual reason for each animal not transported.", (AcceptableValueBase)null, new object[1]
				{
					new Odinplus.Shared.YamlEditor.ConfigurationManagerAttributes
					{
						IsAdvanced = true
					}
				}));
				_profileCreatureTransport = ((BaseUnityPlugin)this).Config.Bind<bool>("Diagnostics", "Profile Creature Transport", false, new ConfigDescription("When diagnostics are enabled, writes one timing and work-count summary for each creature transport.", (AcceptableValueBase)null, new object[1]
				{
					new Odinplus.Shared.YamlEditor.ConfigurationManagerAttributes
					{
						IsAdvanced = true
					}
				}));
			}
			finally
			{
				((BaseUnityPlugin)this).Config.SaveOnConfigSet = saveOnConfigSet;
				((BaseUnityPlugin)this).Config.Save();
			}
		}

		private ConfigEntry<T> BindSynced<T>(string group, string key, T defaultValue, string description)
		{
			ConfigEntry<T> val = ((BaseUnityPlugin)this).Config.Bind<T>(group, key, defaultValue, Describe(description, "Server synchronized"));
			Sync.AddConfigEntry<T>(val).SynchronizedConfig = true;
			return val;
		}

		private ConfigEntry<T> BindLocal<T>(string group, string key, T defaultValue, string description)
		{
			return ((BaseUnityPlugin)this).Config.Bind<T>(group, key, defaultValue, Describe(description, "Local only"));
		}

		private static ConfigDescription Describe(string description, string scope)
		{
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Expected O, but got Unknown
			return new ConfigDescription(description + " [" + scope + "]", (AcceptableValueBase)null, Array.Empty<object>());
		}

		private void SetupServerYamlWatcher()
		{
			_serverYamlWatcher = new FileSystemWatcher(Paths.ConfigPath, Path.GetFileName(_serverYamlPath))
			{
				IncludeSubdirectories = false,
				NotifyFilter = (NotifyFilters.FileName | NotifyFilters.LastWrite | NotifyFilters.CreationTime),
				SynchronizingObject = ThreadingHelper.SynchronizingObject,
				EnableRaisingEvents = true
			};
			_serverYamlWatcher.Changed += delegate
			{
				ReloadServerYaml();
			};
			_serverYamlWatcher.Created += delegate
			{
				ReloadServerYaml();
			};
			_serverYamlWatcher.Renamed += delegate
			{
				ReloadServerYaml();
			};
		}

		private void SetupPlayerYamlWatcher()
		{
			_playerYamlWatcher = new FileSystemWatcher(Paths.ConfigPath, Path.GetFileName(_playerYamlPath))
			{
				IncludeSubdirectories = false,
				NotifyFilter = (NotifyFilters.FileName | NotifyFilters.LastWrite | NotifyFilters.CreationTime),
				SynchronizingObject = ThreadingHelper.SynchronizingObject,
				EnableRaisingEvents = true
			};
			_playerYamlWatcher.Changed += delegate
			{
				ReloadPlayerYaml();
			};
			_playerYamlWatcher.Created += delegate
			{
				ReloadPlayerYaml();
			};
			_playerYamlWatcher.Renamed += delegate
			{
				ReloadPlayerYaml();
			};
		}

		private void SetupConfigWatcher()
		{
			_configWatcher = new FileSystemWatcher(Paths.ConfigPath, "odinplus.TeleportCreatures.cfg")
			{
				IncludeSubdirectories = false,
				NotifyFilter = (NotifyFilters.FileName | NotifyFilters.LastWrite | NotifyFilters.CreationTime),
				SynchronizingObject = ThreadingHelper.SynchronizingObject,
				EnableRaisingEvents = true
			};
			_configWatcher.Changed += ReloadBepInExConfig;
			_configWatcher.Created += ReloadBepInExConfig;
			_configWatcher.Renamed += ReloadBepInExConfig;
		}

		private void RegisterYamlEditor()
		{
			//IL_0119: Unknown result type (might be due to invalid IL or missing references)
			//IL_0123: Expected O, but got Unknown
			YamlEditorRegistry.Initialize("odinplus.TeleportCreatures", "TeleportCreatures");
			YamlEditorRegistry.Register(new YamlDocument("server", "Server policy (" + Path.GetFileName(_serverYamlPath) + ")", _serverYamlPath, YamlValidation.FromExceptional(delegate(string text)
			{
				CreatureConfigValidator.Validate(new DeserializerBuilder().Build().Deserialize<CreatureConfig>(text) ?? throw new InvalidDataException("The document is empty."));
			}), () => Sync.IsSourceOfTruth, () => "only the server host can edit the ser