Decompiled source of BeastTribes CompatShim v0.4.2

BTShim.dll

Decompiled 2 weeks ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text.RegularExpressions;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Mono.Cecil;
using Mono.Cecil.Cil;
using Mono.Cecil.Rocks;
using Mono.Collections.Generic;
using MonoMod.Utils;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: AssemblyVersion("0.0.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace BTShim
{
	internal static class AnimatorCompat
	{
		private static ManualLogSource Log;

		private static ConfigEntry<bool> Enabled;

		private static FieldInfo _controllersField;

		private static FieldInfo _playerField;

		private static FieldInfo _animatorField;

		private static bool _loggedSkip;

		public const string BetterArcheryGuid = "ishid4.mods.betterarchery";

		private static FieldInfo _baAnimatorField;

		private static bool _loggedYield;

		private static readonly (string type, string method, string player)[] Known = new(string, string, string)[2]
		{
			("BeastRacesMod.BeastRacesMod+BeastRaceController", "RefreshRaceAnimationController", "beastPlayer"),
			("BeastTribes.BeastTribes+BTController", "RefreshTribeAnimationController", "BTPlayer")
		};

		public static void Init(ConfigFile config, PluginInfo bt, Harmony harmony, ManualLogSource log)
		{
			//IL_0127: Unknown result type (might be due to invalid IL or missing references)
			//IL_0135: Expected O, but got Unknown
			Log = log;
			Assembly assembly = ((object)bt.Instance).GetType().Assembly;
			Type type = null;
			MethodInfo methodInfo = null;
			(string, string, string)[] known = Known;
			for (int i = 0; i < known.Length; i++)
			{
				(string, string, string) tuple = known[i];
				type = assembly.GetType(tuple.Item1);
				if (!(type == null))
				{
					methodInfo = AccessTools.Method(type, tuple.Item2, new Type[1] { typeof(int) }, (Type[])null);
					if (methodInfo != null)
					{
						_playerField = AccessTools.Field(type, tuple.Item3);
						break;
					}
				}
			}
			if (methodInfo == null)
			{
				Log.LogInfo((object)("AnimatorCompat: no Refresh*AnimationController(int) found in " + assembly.GetName().Name + "; module inactive."));
				return;
			}
			_controllersField = AccessTools.Field(type, "AnimationControllers");
			_animatorField = ((_playerField == null) ? null : AccessTools.Field(_playerField.FieldType, "m_animator"));
			Enabled = config.Bind<bool>("AnimatorCompat", "Enabled", true, "Do not let BeastTribes replace another mod's animation controller with its own default one (fixes the A-pose flicker with mods such as Better Archery 2.0 that also override player animations). Has no effect when no such mod is installed.");
			harmony.Patch((MethodBase)methodInfo, new HarmonyMethod(typeof(AnimatorCompat), "Prefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			Log.LogInfo((object)$"AnimatorCompat: active for {bt.Metadata.Name} {bt.Metadata.Version} (enabled={Enabled.Value}).");
			PatchBetterArchery(harmony);
		}

		private static void PatchBetterArchery(Harmony harmony)
		{
			//IL_00e9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f7: Expected O, but got Unknown
			if (Chainloader.PluginInfos.TryGetValue("ishid4.mods.betterarchery", out var value) && !((Object)(object)value.Instance == (Object)null))
			{
				Assembly assembly = ((object)value.Instance).GetType().Assembly;
				Type type = assembly.GetType("BetterArchery.CrouchBowDrawOverlay") ?? AccessTools.GetTypesFromAssembly(assembly).FirstOrDefault((Type t) => t.Name == "CrouchBowDrawOverlay");
				MethodInfo methodInfo = ((type == null) ? null : AccessTools.GetDeclaredMethods(type).FirstOrDefault((MethodInfo m) => m.Name == "EnsureController" && m.GetParameters().Length == 1));
				if (methodInfo == null)
				{
					Log.LogWarning((object)($"AnimatorCompat: Better Archery {value.Metadata.Version} is installed but CrouchBowDrawOverlay.EnsureController " + "was not found; a flying dragon may still flicker next to it. Please report the Better Archery version."));
					return;
				}
				harmony.Patch((MethodBase)methodInfo, new HarmonyMethod(typeof(AnimatorCompat), "BetterArcheryPrefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				Log.LogInfo((object)$"AnimatorCompat: Better Archery {value.Metadata.Version} detected; it yields the Animator while BeastTribes' flight controller is active.");
			}
		}

		private static bool BetterArcheryPrefix(object __0)
		{
			try
			{
				if (Enabled == null || !Enabled.Value || __0 == null)
				{
					return true;
				}
				if (_baAnimatorField == null)
				{
					_baAnimatorField = AccessTools.Field(__0.GetType(), "m_animator");
				}
				Animator val = (Animator)((_baAnimatorField != null) ? /*isinst with value type is only supported in some contexts*/: null);
				RuntimeAnimatorController val2 = (((Object)(object)val != (Object)null) ? val.runtimeAnimatorController : null);
				if ((Object)(object)val2 == (Object)null || !IsBeastTribesFlightController(val2))
				{
					return true;
				}
				if (!_loggedYield)
				{
					_loggedYield = true;
					Log.LogInfo((object)"AnimatorCompat: BeastTribes' flight controller is active; Better Archery's controller re-assert is held back until it ends.");
				}
				return false;
			}
			catch (Exception ex)
			{
				Log.LogWarning((object)("AnimatorCompat: Better Archery prefix failed, letting it proceed: " + ex.GetType().Name + ": " + ex.Message));
				return true;
			}
		}

		private static bool IsBeastTribesFlightController(RuntimeAnimatorController c)
		{
			if (_controllersField != null && _controllersField.GetValue(null) is IDictionary dictionary)
			{
				foreach (DictionaryEntry item in dictionary)
				{
					if (item.Value == c)
					{
						return !(item.Key is int num) || num != 0;
					}
				}
			}
			return ((Object)c).name == "BeastFlightRAC";
		}

		private static bool Prefix(object __instance, int __0)
		{
			try
			{
				if (Enabled == null || !Enabled.Value)
				{
					return true;
				}
				RuntimeAnimatorController val = Lookup(__0);
				if (!(((Object)(object)val != (Object)null) ? (((Object)val).name == "DefaultRAC") : (__0 == 0)))
				{
					return true;
				}
				Animator animator = GetAnimator(__instance);
				RuntimeAnimatorController val2 = (((Object)(object)animator != (Object)null) ? animator.runtimeAnimatorController : null);
				if ((Object)(object)val2 == (Object)null)
				{
					return true;
				}
				if (!(val2 is AnimatorOverrideController))
				{
					return true;
				}
				if (IsBeastTribesController(val2))
				{
					return true;
				}
				if (!_loggedSkip)
				{
					_loggedSkip = true;
					Log.LogInfo((object)("AnimatorCompat: another mod owns the player's Animator controller ('" + ((Object)val2).name + "'); BeastTribes' default-controller swap is skipped so the two don't keep rebinding the Animator."));
				}
				return false;
			}
			catch (Exception ex)
			{
				Log.LogWarning((object)("AnimatorCompat: prefix failed, letting BeastTribes proceed: " + ex.GetType().Name + ": " + ex.Message));
				return true;
			}
		}

		private static RuntimeAnimatorController Lookup(int index)
		{
			if (_controllersField == null)
			{
				return null;
			}
			if (!(_controllersField.GetValue(null) is IDictionary dictionary) || !dictionary.Contains(index))
			{
				return null;
			}
			object? obj = dictionary[index];
			return (RuntimeAnimatorController)((obj is RuntimeAnimatorController) ? obj : null);
		}

		private static bool IsBeastTribesController(RuntimeAnimatorController c)
		{
			if (_controllersField != null && _controllersField.GetValue(null) is IDictionary dictionary)
			{
				foreach (object value in dictionary.Values)
				{
					if (value == c)
					{
						return true;
					}
				}
			}
			if (!(((Object)c).name == "DefaultRAC"))
			{
				return ((Object)c).name == "BeastFlightRAC";
			}
			return true;
		}

		private static Animator GetAnimator(object ctrl)
		{
			if (_playerField != null && _animatorField != null)
			{
				object value = _playerField.GetValue(ctrl);
				if (value != null)
				{
					object? value2 = _animatorField.GetValue(value);
					Animator val = (Animator)((value2 is Animator) ? value2 : null);
					if (val != null)
					{
						return val;
					}
				}
			}
			object obj = ((ctrl is Component) ? ctrl : null);
			if (obj == null)
			{
				return null;
			}
			return ((Component)obj).GetComponentInChildren<Animator>();
		}
	}
	internal static class CameraCompat
	{
		private static ManualLogSource Log;

		private static ConfigEntry<bool> Enabled;

		private static FieldInfo _defaultFov;

		private static FieldInfo _wasFlying;

		private static Type _ctrlType;

		private static FieldInfo _flightEnabled;

		private static FieldInfo _bodyItem;

		private static bool _loggedTrack;

		private static bool _loggedError;

		public const string CustomizableCameraGuid = "manfredo52.CustomizableCamera";

		private const string CustomizableCameraPatchClass = "GameCamera_GetCameraPosition_Patch";

		private static Component _cachedPlayer;

		private static Component _cachedCtrl;

		private static bool _yielding;

		private static bool _loggedYieldError;

		public static void Init(ConfigFile config, PluginInfo bt, Harmony harmony, ManualLogSource log)
		{
			//IL_01cd: Unknown result type (might be due to invalid IL or missing references)
			//IL_01db: Expected O, but got Unknown
			Log = log;
			if (!GameRefs.Init(log) || !GameRefs.HasCameraFov)
			{
				Log.LogInfo((object)"CameraCompat: GameCamera.instance / m_fov not resolved; module inactive.");
				return;
			}
			Type type = ((object)bt.Instance).GetType();
			MethodInfo methodInfo = AccessTools.Method(type, "ResetFlightChanges", (Type[])null, (Type[])null);
			_defaultFov = AccessTools.Field(type, "DefaultFOV");
			_wasFlying = AccessTools.Field(type, "wasFlying");
			_ctrlType = AccessTools.Field(type, "ctrl")?.FieldType;
			_flightEnabled = ((_ctrlType == null) ? null : AccessTools.Field(_ctrlType, "flightEnabled"));
			_bodyItem = ((_ctrlType == null) ? null : (AccessTools.Field(_ctrlType, "RaceBodyItem") ?? AccessTools.Field(_ctrlType, "BodyItem")));
			string text = null;
			if (methodInfo == null)
			{
				text = "ResetFlightChanges";
			}
			else if (_defaultFov == null || !_defaultFov.IsStatic || _defaultFov.FieldType != typeof(float))
			{
				text = "static float DefaultFOV";
			}
			else if (_wasFlying == null || !_wasFlying.IsStatic || _wasFlying.FieldType != typeof(bool))
			{
				text = "static bool wasFlying";
			}
			if (text != null)
			{
				Log.LogInfo((object)("CameraCompat: " + text + " not found in " + bt.Metadata.Name + "; module inactive."));
			}
			else
			{
				Enabled = config.Bind<bool>("CameraCompat", "Enabled", true, "Stop BeastTribes from forcing the camera field of view back to a value it remembered once per session (fixes the flickering, extremely zoomed-in camera - a smeared close-up of the ground - with camera mods such as Customizable Camera, and lets such mods control the FOV while you are not flying). With Customizable Camera installed, its camera update is also held back during a BeastTribes flight so the flight zoom works. Has no effect when nothing else changes the FOV.");
				harmony.Patch((MethodBase)methodInfo, new HarmonyMethod(typeof(CameraCompat), "ResetPrefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				Log.LogInfo((object)$"CameraCompat: active for {bt.Metadata.Name} {bt.Metadata.Version} (enabled={Enabled.Value}).");
				PatchCustomizableCamera(harmony);
			}
		}

		private static void ResetPrefix()
		{
			try
			{
				if (Enabled == null || !Enabled.Value)
				{
					return;
				}
				object value = _wasFlying.GetValue(null);
				bool flag = default(bool);
				int num;
				if (value is bool)
				{
					flag = (bool)value;
					num = 1;
				}
				else
				{
					num = 0;
				}
				if (((uint)num & (flag ? 1u : 0u)) != 0)
				{
					return;
				}
				float? num2 = GameRefs.CameraFov();
				if (num2.HasValue && !float.IsNaN(num2.Value) && !(num2.Value <= 0f))
				{
					if (!_loggedTrack)
					{
						_loggedTrack = true;
						Log.LogDebug((object)$"CameraCompat: BeastTribes' remembered FOV now follows the live camera FOV (currently {num2.Value:0.#}).");
					}
					_defaultFov.SetValue(null, num2.Value);
				}
			}
			catch (Exception ex)
			{
				if (!_loggedError)
				{
					_loggedError = true;
					Log.LogWarning((object)("CameraCompat: prefix failed, BeastTribes' FOV handling left as is: " + ex.GetType().Name + ": " + ex.Message));
				}
			}
		}

		private static void PatchCustomizableCamera(Harmony harmony)
		{
			//IL_01d4: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e2: Expected O, but got Unknown
			if (_ctrlType == null || _flightEnabled == null || _flightEnabled.FieldType != typeof(bool))
			{
				Log.LogDebug((object)"CameraCompat: controller flightEnabled not found; the Customizable Camera flight patch is skipped.");
				return;
			}
			Type type = null;
			string text = null;
			PluginInfo value;
			bool flag = Chainloader.PluginInfos.TryGetValue("manfredo52.CustomizableCamera", out value) && (Object)(object)value.Instance != (Object)null;
			if (flag)
			{
				type = FindPatchClass(((object)value.Instance).GetType().Assembly);
				text = $"{value.Metadata.Name} {value.Metadata.Version}";
			}
			if (type == null)
			{
				foreach (PluginInfo value2 in Chainloader.PluginInfos.Values)
				{
					if (!((Object)(object)value2.Instance == (Object)null) && !(value2.Instance is Plugin))
					{
						type = FindPatchClass(((object)value2.Instance).GetType().Assembly);
						if (!(type == null))
						{
							text = $"{value2.Metadata.Name} {value2.Metadata.Version}";
							break;
						}
					}
				}
			}
			if (type == null)
			{
				if (flag)
				{
					Log.LogWarning((object)("CameraCompat: " + text + " is installed but its GameCamera_GetCameraPosition_Patch class was not found; the camera may still flicker while flying. Please report the Customizable Camera version."));
				}
				return;
			}
			MethodInfo methodInfo = AccessTools.GetDeclaredMethods(type).FirstOrDefault((MethodInfo m) => m.IsStatic && m.Name == "Postfix");
			if (methodInfo == null)
			{
				Log.LogWarning((object)("CameraCompat: " + text + ": GameCamera_GetCameraPosition_Patch.Postfix not found; the camera may still flicker while flying."));
				return;
			}
			harmony.Patch((MethodBase)methodInfo, new HarmonyMethod(typeof(CameraCompat), "CustomizableCameraPrefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			Log.LogInfo((object)("CameraCompat: " + text + " detected; its camera update yields while a BeastTribes flight is in progress."));
		}

		private static Type FindPatchClass(Assembly asm)
		{
			if (asm == typeof(CameraCompat).Assembly)
			{
				return null;
			}
			return asm.GetType("GameCamera_GetCameraPosition_Patch") ?? AccessTools.GetTypesFromAssembly(asm).FirstOrDefault((Type t) => t.Name == "GameCamera_GetCameraPosition_Patch");
		}

		private static bool CustomizableCameraPrefix()
		{
			try
			{
				if (Enabled == null || !Enabled.Value)
				{
					return true;
				}
				bool flag = LocalFlightActive();
				if (flag != _yielding)
				{
					_yielding = flag;
					Log.LogDebug((object)(flag ? "CameraCompat: BeastTribes flight started; Customizable Camera's camera update is held back until it ends." : "CameraCompat: BeastTribes flight ended; Customizable Camera resumes."));
				}
				return !flag;
			}
			catch (Exception ex)
			{
				if (!_loggedYieldError)
				{
					_loggedYieldError = true;
					Log.LogWarning((object)("CameraCompat: Customizable Camera prefix failed, letting it proceed: " + ex.GetType().Name + ": " + ex.Message));
				}
				return true;
			}
		}

		private static bool LocalFlightActive()
		{
			Component val = GameRefs.LocalPlayer();
			if ((Object)(object)val == (Object)null)
			{
				_cachedPlayer = null;
				_cachedCtrl = null;
				return false;
			}
			if (val != _cachedPlayer || (Object)(object)_cachedCtrl == (Object)null)
			{
				_cachedPlayer = val;
				_cachedCtrl = val.GetComponent(_ctrlType);
			}
			if ((Object)(object)_cachedCtrl == (Object)null)
			{
				return false;
			}
			if (_bodyItem != null && (Object)/*isinst with value type is only supported in some contexts*/ == (Object)null)
			{
				return false;
			}
			object value = _flightEnabled.GetValue(_cachedCtrl);
			bool flag = default(bool);
			int num;
			if (value is bool)
			{
				flag = (bool)value;
				num = 1;
			}
			else
			{
				num = 0;
			}
			return (byte)((uint)num & (flag ? 1u : 0u)) != 0;
		}
	}
	internal static class ColorCompat
	{
		private static ManualLogSource Log;

		private static ConfigEntry<bool> Enabled;

		private static ConfigEntry<string> FemaleDragonMaterials;

		private static FieldInfo _bodyField;

		private static Dictionary<string, string> _swapMap = new Dictionary<string, string>();

		private static readonly HashSet<string> _loggedSwaps = new HashSet<string>();

		private static bool _loggedExtra;

		private const string ControllerType = "BeastRacesMod.BeastRacesMod+BeastRaceController";

		private const string DragonBody = "dragon_skin";

		public static void Init(ConfigFile config, PluginInfo bt, Harmony harmony, ManualLogSource log)
		{
			//IL_0163: Unknown result type (might be due to invalid IL or missing references)
			//IL_0170: Expected O, but got Unknown
			Log = log;
			Assembly assembly = ((object)bt.Instance).GetType().Assembly;
			Type type = assembly.GetType("BeastRacesMod.BeastRacesMod+BeastRaceController");
			MethodInfo methodInfo = ((type == null) ? null : AccessTools.Method(type, "RefreshBeastColors", new Type[4]
			{
				typeof(Color),
				typeof(Color),
				typeof(Color),
				typeof(Color)
			}, (Type[])null));
			_bodyField = ((type == null) ? null : AccessTools.Field(type, "RaceBodyItem"));
			if (methodInfo == null || _bodyField == null || _bodyField.FieldType != typeof(GameObject))
			{
				Log.LogInfo((object)("ColorCompat: " + assembly.GetName().Name + " has no BeastRaceController.RefreshBeastColors/RaceBodyItem (no colour zones in this build); module inactive."));
				return;
			}
			Enabled = config.Bind<bool>("ColorCompat", "Enabled", true, "Apply the Beast Tribes colour zones (base / highlights / tattoos) to every body material that supports them, not only the ones Beast Tribes picks by name. Fixes the dragon's wing membrane and the female lynx body ignoring the chosen colours.");
			FemaleDragonMaterials = config.Bind<string>("ColorCompat", "FemaleDragonMaterials", "", "EXPERIMENTAL. The female dragon body uses old, painted materials that cannot take colour zones at all. This swaps them for the male dragon's colourable materials. Comma-separated pairs <female material>=<male material>; empty = off. Suggested value: Dragon01_Material=Dragon_Male_Main,Dragon02_Material=Dragon_Male_Main");
			FemaleDragonMaterials.SettingChanged += delegate
			{
				ParseSwapMap();
			};
			ParseSwapMap();
			harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, new HarmonyMethod(typeof(ColorCompat), "Postfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			Log.LogInfo((object)($"ColorCompat: active for {bt.Metadata.Name} {bt.Metadata.Version} (enabled={Enabled.Value}, " + $"female dragon swaps={_swapMap.Count})."));
		}

		private static void ParseSwapMap()
		{
			Dictionary<string, string> dictionary = new Dictionary<string, string>();
			string[] array = (FemaleDragonMaterials.Value ?? "").Split(new char[1] { ',' });
			foreach (string text in array)
			{
				int num = text.IndexOf('=');
				if (num > 0)
				{
					string text2 = text.Substring(0, num).Trim();
					string text3 = text.Substring(num + 1).Trim();
					if (text2.Length > 0 && text3.Length > 0)
					{
						dictionary[text2] = text3;
					}
				}
			}
			_swapMap = dictionary;
		}

		private static void Postfix(object __instance, Color __0, Color __1, Color __2)
		{
			//IL_006d: Unknown result type (might be due to invalid IL or missing references)
			//IL_006e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0073: Unknown result type (might be due to invalid IL or missing references)
			//IL_0074: Unknown result type (might be due to invalid IL or missing references)
			//IL_0075: 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_007b: Unknown result type (might be due to invalid IL or missing references)
			//IL_007c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0081: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f4: Unknown result type (might be due to invalid IL or missing references)
			//IL_0101: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				if (Enabled == null || !Enabled.Value)
				{
					return;
				}
				object? value = _bodyField.GetValue(__instance);
				GameObject val = (GameObject)((value is GameObject) ? value : null);
				if ((Object)(object)val == (Object)null)
				{
					return;
				}
				SkinnedMeshRenderer[] componentsInChildren = val.GetComponentsInChildren<SkinnedMeshRenderer>();
				if (componentsInChildren.Length == 0)
				{
					return;
				}
				if (_swapMap.Count > 0 && ((Object)val).name == "dragon_skin")
				{
					SwapFemaleDragonMaterials(val, componentsInChildren);
				}
				Color val2 = Clamp(__0);
				Color val3 = Clamp(__1);
				Color val4 = Clamp(__2);
				int num = 0;
				SkinnedMeshRenderer[] array = componentsInChildren;
				for (int i = 0; i < array.Length; i++)
				{
					Material[] materials = ((Renderer)array[i]).materials;
					foreach (Material val5 in materials)
					{
						if (!((Object)(object)val5 == (Object)null) && val5.HasProperty("_BaseColor") && val5.HasProperty("_HighlightColor") && val5.HasProperty("_TattooColor"))
						{
							val5.SetColor("_BaseColor", val2);
							val5.SetColor("_HighlightColor", val3);
							val5.SetColor("_TattooColor", val4);
							if (((Object)val5).name.IndexOf("_Main", StringComparison.Ordinal) < 0)
							{
								num++;
							}
						}
					}
				}
				if (num > 0 && !_loggedExtra)
				{
					_loggedExtra = true;
					Log.LogInfo((object)$"ColorCompat: coloured {num} material(s) on '{((Object)val).name}' that BeastTribes' name filter skips.");
				}
			}
			catch (Exception ex)
			{
				Log.LogWarning((object)("ColorCompat: postfix failed: " + ex.GetType().Name + ": " + ex.Message));
			}
		}

		private static void SwapFemaleDragonMaterials(GameObject body, SkinnedMeshRenderer[] renderers)
		{
			Transform val = body.transform.Find("splitbody_female");
			Transform val2 = body.transform.Find("splitbody");
			if ((Object)(object)val == (Object)null || (Object)(object)val2 == (Object)null || !((Component)val).gameObject.activeSelf)
			{
				return;
			}
			Dictionary<string, Material> dictionary = null;
			foreach (SkinnedMeshRenderer val3 in renderers)
			{
				if (!((Component)val3).transform.IsChildOf(val))
				{
					continue;
				}
				Material[] sharedMaterials = ((Renderer)val3).sharedMaterials;
				bool flag = false;
				for (int j = 0; j < sharedMaterials.Length; j++)
				{
					if ((Object)(object)sharedMaterials[j] == (Object)null)
					{
						continue;
					}
					string text = AssetName(((Object)sharedMaterials[j]).name);
					if (!_swapMap.TryGetValue(text, out var value))
					{
						continue;
					}
					if (dictionary == null)
					{
						dictionary = CollectDonors(val2);
					}
					if (!dictionary.TryGetValue(value, out var value2))
					{
						if (_loggedSwaps.Add("missing:" + value))
						{
							Log.LogWarning((object)("ColorCompat: male dragon material '" + value + "' not found; '" + text + "' stays as is."));
						}
						continue;
					}
					sharedMaterials[j] = value2;
					flag = true;
					if (_loggedSwaps.Add(text + ">" + value))
					{
						Log.LogInfo((object)("ColorCompat: female dragon material '" + text + "' replaced by '" + value + "' (experimental)."));
					}
				}
				if (flag)
				{
					((Renderer)val3).sharedMaterials = sharedMaterials;
				}
			}
		}

		private static Dictionary<string, Material> CollectDonors(Transform male)
		{
			Dictionary<string, Material> dictionary = new Dictionary<string, Material>();
			SkinnedMeshRenderer[] componentsInChildren = ((Component)male).GetComponentsInChildren<SkinnedMeshRenderer>(true);
			for (int i = 0; i < componentsInChildren.Length; i++)
			{
				Material[] sharedMaterials = ((Renderer)componentsInChildren[i]).sharedMaterials;
				foreach (Material val in sharedMaterials)
				{
					if ((Object)(object)val != (Object)null && !dictionary.ContainsKey(AssetName(((Object)val).name)))
					{
						dictionary[AssetName(((Object)val).name)] = val;
					}
				}
			}
			return dictionary;
		}

		private static string AssetName(string name)
		{
			while (name.EndsWith(" (Instance)", StringComparison.Ordinal))
			{
				name = name.Substring(0, name.Length - " (Instance)".Length);
			}
			return name;
		}

		private static Color Clamp(Color c)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: 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_0045: Unknown result type (might be due to invalid IL or missing references)
			return new Color(Mathf.Clamp(c.r, 0.2f, 0.9f), Mathf.Clamp(c.g, 0.2f, 0.9f), Mathf.Clamp(c.b, 0.2f, 0.9f), c.a);
		}
	}
	internal static class GameRefs
	{
		public static Type Player;

		public static Type Humanoid;

		public static Type FejdStartup;

		public static Type GameCamera;

		private static FieldInfo _localPlayer;

		private static Func<object> _fejdInstance;

		private static Func<object> _cameraInstance;

		private static FieldInfo _cameraFov;

		private static Func<GameObject> _customGuiFront;

		private static Action<bool> _blockInput;

		private static bool _initialised;

		private static bool _ok;

		public static bool HasCameraFov
		{
			get
			{
				if (_cameraInstance != null)
				{
					return _cameraFov != null;
				}
				return false;
			}
		}

		public static bool CanBlockInput => _blockInput != null;

		public static bool Init(ManualLogSource log)
		{
			if (_initialised)
			{
				return _ok;
			}
			_initialised = true;
			Player = AccessTools.TypeByName("Player");
			Humanoid = AccessTools.TypeByName("Humanoid");
			FejdStartup = AccessTools.TypeByName("FejdStartup");
			_localPlayer = ((Player == null) ? null : AccessTools.Field(Player, "m_localPlayer"));
			if (FejdStartup != null)
			{
				MethodInfo getter = AccessTools.PropertyGetter(FejdStartup, "instance");
				FieldInfo field = AccessTools.Field(FejdStartup, "instance") ?? AccessTools.Field(FejdStartup, "m_instance");
				if (getter != null)
				{
					_fejdInstance = () => getter.Invoke(null, null);
				}
				else if (field != null)
				{
					_fejdInstance = () => field.GetValue(null);
				}
			}
			GameCamera = AccessTools.TypeByName("GameCamera");
			if (GameCamera != null)
			{
				MethodInfo getter2 = AccessTools.PropertyGetter(GameCamera, "instance");
				FieldInfo field2 = AccessTools.Field(GameCamera, "instance") ?? AccessTools.Field(GameCamera, "m_instance");
				if (getter2 != null)
				{
					_cameraInstance = () => getter2.Invoke(null, null);
				}
				else if (field2 != null)
				{
					_cameraInstance = () => field2.GetValue(null);
				}
				FieldInfo fieldInfo = AccessTools.Field(GameCamera, "m_fov");
				if (fieldInfo != null && fieldInfo.FieldType == typeof(float))
				{
					_cameraFov = fieldInfo;
				}
			}
			Type type = AccessTools.TypeByName("Jotunn.Managers.GUIManager");
			if (type != null)
			{
				MethodInfo front = AccessTools.PropertyGetter(type, "CustomGUIFront");
				if (front != null)
				{
					_customGuiFront = delegate
					{
						object? obj = front.Invoke(null, null);
						return (GameObject)((obj is GameObject) ? obj : null);
					};
				}
				MethodInfo block = AccessTools.Method(type, "BlockInput", new Type[1] { typeof(bool) }, (Type[])null);
				if (block != null)
				{
					_blockInput = delegate(bool state)
					{
						block.Invoke(null, new object[1] { state });
					};
				}
			}
			_ok = Player != null && Humanoid != null && _localPlayer != null;
			if (!_ok)
			{
				log.LogWarning((object)"Could not resolve Player/Humanoid from assembly_valheim; the optional modules that need them stay inactive.");
			}
			return _ok;
		}

		public static Component LocalPlayer()
		{
			object? obj = _localPlayer?.GetValue(null);
			Component val = (Component)((obj is Component) ? obj : null);
			if (!((Object)(object)val == (Object)null))
			{
				return val;
			}
			return null;
		}

		public static bool InStartMenu()
		{
			object obj = _fejdInstance?.Invoke();
			return (Object)((obj is Object) ? obj : null) != (Object)null;
		}

		public static Component Camera()
		{
			object obj = _cameraInstance?.Invoke();
			Component val = (Component)((obj is Component) ? obj : null);
			if (!((Object)(object)val == (Object)null))
			{
				return val;
			}
			return null;
		}

		public static float? CameraFov()
		{
			Component val = Camera();
			if ((Object)(object)val == (Object)null || _cameraFov == null)
			{
				return null;
			}
			return (float)_cameraFov.GetValue(val);
		}

		public static GameObject CustomGuiFront()
		{
			return _customGuiFront?.Invoke();
		}

		public static void BlockInput(bool state)
		{
			_blockInput?.Invoke(state);
		}
	}
	internal static class MenuTargetFix
	{
		private static ManualLogSource Log;

		private static ConfigEntry<bool> Enabled;

		private static FieldInfo _ctrl;

		private static Type _ctrlType;

		private static readonly string[] MenuNames = new string[2] { "BeastMenu", "BT_Menu" };

		public static void Init(ConfigFile config, PluginInfo bt, Harmony harmony, ManualLogSource log)
		{
			//IL_0137: Unknown result type (might be due to invalid IL or missing references)
			//IL_0144: Expected O, but got Unknown
			//IL_0160: Unknown result type (might be due to invalid IL or missing references)
			//IL_016e: Expected O, but got Unknown
			//IL_018a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0198: Expected O, but got Unknown
			//IL_01b7: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c4: Expected O, but got Unknown
			Log = log;
			if (!GameRefs.Init(log))
			{
				Log.LogInfo((object)"MenuTargetFix: game types not resolved; module inactive.");
				return;
			}
			Type type = ((object)bt.Instance).GetType();
			_ctrl = AccessTools.Field(type, "ctrl");
			if (_ctrl == null || !_ctrl.IsStatic || !typeof(Component).IsAssignableFrom(_ctrl.FieldType))
			{
				Log.LogInfo((object)("MenuTargetFix: no static controller field 'ctrl' in " + type.FullName + "; module inactive."));
				return;
			}
			_ctrlType = _ctrl.FieldType;
			MethodInfo methodInfo = AccessTools.Method(type, "Humanoid_Awake_Controller", (Type[])null, (Type[])null);
			MethodInfo methodInfo2 = AccessTools.Method(type, "CheckMenuKey", (Type[])null, (Type[])null);
			MethodInfo methodInfo3 = AccessTools.Method(_ctrlType, "MenuBeastPreview", (Type[])null, (Type[])null);
			MethodInfo methodInfo4 = AccessTools.Method(GameRefs.Player, "OnSpawned", (Type[])null, (Type[])null);
			if (methodInfo == null && methodInfo2 == null)
			{
				Log.LogInfo((object)"MenuTargetFix: neither Humanoid_Awake_Controller nor CheckMenuKey found; module inactive.");
				return;
			}
			Enabled = config.Bind<bool>("MenuTargetFix", "Enabled", true, "Keep the Beast menu bound to the local player. Fixes the menu acting on (or being stuck on) another player's or a destroyed player's controller: menu that opens but no button works, menu that cannot be closed after a death or after another player left, species not switching in multiplayer, and the NullReferenceException spam from MenuBeastPreview while respawning. Off = BeastTribes' original behaviour.");
			if (methodInfo != null)
			{
				harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, new HarmonyMethod(typeof(MenuTargetFix), "AwakePostfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			}
			if (methodInfo2 != null)
			{
				harmony.Patch((MethodBase)methodInfo2, new HarmonyMethod(typeof(MenuTargetFix), "CheckMenuKeyPrefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			}
			if (methodInfo3 != null)
			{
				harmony.Patch((MethodBase)methodInfo3, new HarmonyMethod(typeof(MenuTargetFix), "PreviewPrefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			}
			if (methodInfo4 != null)
			{
				harmony.Patch((MethodBase)methodInfo4, (HarmonyMethod)null, new HarmonyMethod(typeof(MenuTargetFix), "SpawnedPostfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			}
			Log.LogInfo((object)($"MenuTargetFix: active for {bt.Metadata.Name} {bt.Metadata.Version} (enabled={Enabled.Value}; " + $"awake={methodInfo != null}, menuKey={methodInfo2 != null}, preview={methodInfo3 != null}, spawn={methodInfo4 != null})."));
		}

		private static bool On()
		{
			if (Enabled != null)
			{
				return Enabled.Value;
			}
			return false;
		}

		private static Component LocalController(Component local)
		{
			if (!((Object)(object)local == (Object)null))
			{
				return local.GetComponent(_ctrlType);
			}
			return null;
		}

		private static void AwakePostfix(object __0)
		{
			try
			{
				if (!On())
				{
					return;
				}
				Component val = (Component)((__0 is Component) ? __0 : null);
				if ((Object)(object)val == (Object)null || !GameRefs.Player.IsInstanceOfType(val))
				{
					return;
				}
				Component val2 = GameRefs.LocalPlayer();
				if (!((Object)(object)val2 == (Object)null) && !((Object)(object)val2.gameObject == (Object)(object)val.gameObject))
				{
					Component val3 = LocalController(val2);
					if (!((Object)(object)val3 == (Object)null))
					{
						_ctrl.SetValue(null, val3);
						Log.LogDebug((object)"MenuTargetFix: another player's controller awoke; menu target kept on the local player.");
					}
				}
			}
			catch (Exception ex)
			{
				Log.LogWarning((object)("MenuTargetFix: Awake postfix failed: " + ex.GetType().Name + ": " + ex.Message));
			}
		}

		private static bool CheckMenuKeyPrefix()
		{
			try
			{
				if (!On())
				{
					return true;
				}
				if (GameRefs.InStartMenu())
				{
					return true;
				}
				Component val = GameRefs.LocalPlayer();
				if ((Object)(object)val == (Object)null)
				{
					return false;
				}
				object? value = _ctrl.GetValue(null);
				Component val2 = (Component)((value is Component) ? value : null);
				if ((Object)(object)val2 != (Object)null && (Object)(object)val2.gameObject == (Object)(object)val.gameObject)
				{
					return true;
				}
				Component val3 = LocalController(val);
				if ((Object)(object)val3 == (Object)null)
				{
					return false;
				}
				_ctrl.SetValue(null, val3);
				Log.LogDebug((object)"MenuTargetFix: menu target re-pointed at the local player's controller.");
				return true;
			}
			catch (Exception ex)
			{
				Log.LogWarning((object)("MenuTargetFix: CheckMenuKey prefix failed, letting BeastTribes proceed: " + ex.GetType().Name + ": " + ex.Message));
				return true;
			}
		}

		private static bool PreviewPrefix()
		{
			try
			{
				if (!On())
				{
					return true;
				}
				return GameRefs.InStartMenu() || (Object)(object)GameRefs.LocalPlayer() != (Object)null;
			}
			catch
			{
				return true;
			}
		}

		private static void SpawnedPostfix()
		{
			try
			{
				if (!On())
				{
					return;
				}
				GameObject val = GameRefs.CustomGuiFront();
				if ((Object)(object)val == (Object)null)
				{
					return;
				}
				int num = 0;
				int num2 = 0;
				Transform transform = val.transform;
				for (int num3 = transform.childCount - 1; num3 >= 0; num3--)
				{
					GameObject gameObject = ((Component)transform.GetChild(num3)).gameObject;
					if (Array.IndexOf(MenuNames, ((Object)gameObject).name) >= 0)
					{
						if (gameObject.activeSelf && GameRefs.CanBlockInput)
						{
							GameRefs.BlockInput(state: false);
							num2++;
						}
						Object.Destroy((Object)(object)gameObject);
						num++;
					}
				}
				if (num > 0)
				{
					Log.LogInfo((object)($"MenuTargetFix: removed {num} orphaned Beast menu panel(s) left by a previous player object" + ((num2 > 0) ? $" and released {num2} stuck input block(s)" : "") + "."));
				}
			}
			catch (Exception ex)
			{
				Log.LogWarning((object)("MenuTargetFix: OnSpawned postfix failed: " + ex.GetType().Name + ": " + ex.Message));
			}
		}
	}
	internal static class MenuTextFix
	{
		private static ManualLogSource Log;

		private static ConfigEntry<bool> Enabled;

		private static string _btGuid;

		private static Func<object> _managerInstance;

		private static FieldInfo _localizations;

		private static MethodInfo _contains;

		private static object _btLocalization;

		private static int _fixed;

		private static bool _loggedFirst;

		public static void Init(ConfigFile config, PluginInfo bt, Harmony harmony, ManualLogSource log)
		{
			//IL_0134: Unknown result type (might be due to invalid IL or missing references)
			//IL_0142: Expected O, but got Unknown
			Log = log;
			_btGuid = bt.Metadata.GUID;
			Type type = AccessTools.TypeByName("Jotunn.Managers.LocalizationManager");
			Type type2 = AccessTools.TypeByName("Jotunn.Entities.CustomLocalization");
			MethodInfo methodInfo = ((type == null) ? null : AccessTools.Method(type, "TryTranslate", new Type[1] { typeof(string) }, (Type[])null));
			MethodInfo instance = ((type == null) ? null : AccessTools.PropertyGetter(type, "Instance"));
			_localizations = ((type == null) ? null : AccessTools.Field(type, "Localizations"));
			_contains = ((type2 == null) ? null : AccessTools.Method(type2, "Contains", (Type[])null, (Type[])null));
			if (methodInfo == null || instance == null || _localizations == null || _contains == null)
			{
				Log.LogWarning((object)"MenuTextFix: Jotunn's LocalizationManager.TryTranslate / Localizations / CustomLocalization.Contains not found; module inactive (menu labels may show raw tokens).");
				return;
			}
			_managerInstance = () => instance.Invoke(null, null);
			Enabled = config.Bind<bool>("MenuTextFix", "Enabled", true, "Show proper texts in the Beast Tribes menu (species names, colour buttons, toggles) instead of raw tokens such as 'BeastRacefoxName'. Needed with Jotunn 2.25 and newer.");
			harmony.Patch((MethodBase)methodInfo, new HarmonyMethod(typeof(MenuTextFix), "Prefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			Log.LogInfo((object)$"MenuTextFix: active for {bt.Metadata.Name} {bt.Metadata.Version} (enabled={Enabled.Value}).");
		}

		private static void Prefix(ref string word)
		{
			try
			{
				if (Enabled == null || !Enabled.Value || string.IsNullOrEmpty(word) || word[0] == '$')
				{
					return;
				}
				object obj = BeastTribesLocalization();
				if (obj == null)
				{
					return;
				}
				object obj2 = _contains.Invoke(obj, new object[2] { "English", word });
				if (obj2 is bool && (bool)obj2)
				{
					word = "$" + word;
					_fixed++;
					if (!_loggedFirst)
					{
						_loggedFirst = true;
						Log.LogInfo((object)("MenuTextFix: BeastTribes asked for '" + word.Substring(1) + "' without the '$' prefix; added it so Jotunn translates it."));
					}
				}
			}
			catch (Exception ex)
			{
				Log.LogWarning((object)("MenuTextFix: prefix failed, leaving the word alone: " + ex.GetType().Name + ": " + ex.Message));
			}
		}

		private static object BeastTribesLocalization()
		{
			if (_btLocalization != null)
			{
				return _btLocalization;
			}
			object obj = _managerInstance();
			if (obj == null)
			{
				return null;
			}
			if (_localizations.GetValue(obj) is IDictionary dictionary && dictionary.Contains(_btGuid))
			{
				_btLocalization = dictionary[_btGuid];
			}
			return _btLocalization;
		}
	}
	[BepInPlugin("community.beasttribes.compatshim", "BeastTribes Compat Shim", "0.4.2")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public class Plugin : BaseUnityPlugin
	{
		public const string GUID = "community.beasttribes.compatshim";

		public const string NAME = "BeastTribes Compat Shim";

		public const string VERSION = "0.4.2";

		public const string BT_GUID_2025_11 = "EosFoxx_DerSpieleentwickler_SnarlingFox_Jaryic_BeastTribes_Originals";

		public const string BT_GUID_1_2 = "EosFoxx_DerSpieleentwickler_BeastTribes";

		internal static ManualLogSource Log;

		private ConfigEntry<string> _targetGuids;

		private ConfigEntry<bool> _verbose;

		private void Awake()
		{
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			_targetGuids = ((BaseUnityPlugin)this).Config.Bind<string>("General", "TargetPluginGuids", "EosFoxx_DerSpieleentwickler_SnarlingFox_Jaryic_BeastTribes_Originals,EosFoxx_DerSpieleentwickler_BeastTribes", "Comma-separated BepInEx plugin GUIDs whose assemblies are scanned for calls to game methods that no longer exist and repaired in memory. Both known BeastTribes GUIDs are listed by default.");
			_verbose = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "VerboseLog", false, "Log every rewritten instruction and redirected call site (useful when reporting problems).");
			Harmony harmony = new Harmony("community.beasttribes.compatshim");
			int num = 0;
			foreach (string item in from s in _targetGuids.Value.Split(new char[1] { ',' })
				select s.Trim() into s
				where s.Length > 0
				select s)
			{
				if (!Chainloader.PluginInfos.TryGetValue(item, out var value))
				{
					Log.LogDebug((object)("Plugin '" + item + "' is not installed; skipping."));
					continue;
				}
				if ((Object)(object)value.Instance == (Object)null)
				{
					Log.LogWarning((object)("Plugin '" + item + "' is installed but has no instance (failed to load?); skipping."));
					continue;
				}
				num++;
				Assembly assembly = ((object)value.Instance).GetType().Assembly;
				try
				{
					RepairStats repairStats = AssemblyRepair.Run(assembly, harmony, Log, _verbose.Value);
					Log.LogInfo((object)($"{value.Metadata.Name} {value.Metadata.Version} ({assembly.GetName().Name}.dll): " + $"{repairStats.PoisonedMethods} broken method(s), {repairStats.RewrittenCalls} stale call(s) rewritten, " + $"{repairStats.RedirectedCallSites} call site(s) redirected, {repairStats.RepatchedHarmonyMethods} Harmony patch(es) re-registered" + ((repairStats.Unfixable > 0) ? $", {repairStats.Unfixable} NOT fixable (see errors above)" : "") + "."));
				}
				catch (Exception arg)
				{
					Log.LogError((object)$"Failed to repair {value.Metadata.Name} ({assembly.GetName().Name}.dll): {arg}");
				}
				try
				{
					TailPhysics.Init(((BaseUnityPlugin)this).Config, value, harmony, Log);
				}
				catch (Exception arg2)
				{
					Log.LogError((object)$"TailPhysics module failed to initialise: {arg2}");
				}
				try
				{
					AnimatorCompat.Init(((BaseUnityPlugin)this).Config, value, harmony, Log);
				}
				catch (Exception arg3)
				{
					Log.LogError((object)$"AnimatorCompat module failed to initialise: {arg3}");
				}
				try
				{
					MenuTargetFix.Init(((BaseUnityPlugin)this).Config, value, harmony, Log);
				}
				catch (Exception arg4)
				{
					Log.LogError((object)$"MenuTargetFix module failed to initialise: {arg4}");
				}
				try
				{
					RespawnRestore.Init(((BaseUnityPlugin)this).Config, value, harmony, Log);
				}
				catch (Exception arg5)
				{
					Log.LogError((object)$"RespawnRestore module failed to initialise: {arg5}");
				}
				try
				{
					CameraCompat.Init(((BaseUnityPlugin)this).Config, value, harmony, Log);
				}
				catch (Exception arg6)
				{
					Log.LogError((object)$"CameraCompat module failed to initialise: {arg6}");
				}
				try
				{
					ColorCompat.Init(((BaseUnityPlugin)this).Config, value, harmony, Log);
				}
				catch (Exception arg7)
				{
					Log.LogError((object)$"ColorCompat module failed to initialise: {arg7}");
				}
				try
				{
					MenuTextFix.Init(((BaseUnityPlugin)this).Config, value, harmony, Log);
				}
				catch (Exception arg8)
				{
					Log.LogError((object)$"MenuTextFix module failed to initialise: {arg8}");
				}
			}
			if (num == 0)
			{
				Log.LogWarning((object)"No target plugin found; nothing to do. Check TargetPluginGuids in the config if BeastTribes uses a new GUID.");
			}
		}
	}
	public sealed class RepairStats
	{
		public int PoisonedMethods;

		public int RewrittenCalls;

		public int RedirectedCallSites;

		public int RepatchedHarmonyMethods;

		public int Unfixable;
	}
	public static class AssemblyRepair
	{
		private sealed class MethodHandleComparer : IEqualityComparer<MethodBase>
		{
			public bool Equals(MethodBase a, MethodBase b)
			{
				if (a != null && b != null)
				{
					return a.MethodHandle.Value == b.MethodHandle.Value;
				}
				return false;
			}

			public int GetHashCode(MethodBase m)
			{
				return m.MethodHandle.Value.GetHashCode();
			}
		}

		private sealed class BrokenCall
		{
			public string Key;

			public MethodBase Replacement;

			public ParameterInfo[] Extras;
		}

		private sealed class LoadedAssemblyResolver : IAssemblyResolver, IDisposable
		{
			private readonly Dictionary<string, AssemblyDefinition> _cache = new Dictionary<string, AssemblyDefinition>();

			public AssemblyDefinition Resolve(AssemblyNameReference name)
			{
				//IL_0002: Unknown result type (might be due to invalid IL or missing references)
				//IL_0007: 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_001a: Expected O, but got Unknown
				return Resolve(name, new ReaderParameters
				{
					ReadingMode = (ReadingMode)2,
					AssemblyResolver = (IAssemblyResolver)(object)this
				});
			}

			public AssemblyDefinition Resolve(AssemblyNameReference name, ReaderParameters parameters)
			{
				//IL_0054: Unknown result type (might be due to invalid IL or missing references)
				if (_cache.TryGetValue(name.Name, out var value))
				{
					return value;
				}
				Assembly assembly = AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault((Assembly a) => a.GetName().Name == name.Name && !string.IsNullOrEmpty(a.Location) && File.Exists(a.Location));
				if (assembly == null)
				{
					throw new AssemblyResolutionException(name);
				}
				return _cache[name.Name] = AssemblyDefinition.ReadAssembly(assembly.Location, parameters);
			}

			public void Dispose()
			{
				foreach (AssemblyDefinition value in _cache.Values)
				{
					value.Dispose();
				}
				_cache.Clear();
			}
		}

		private static readonly Dictionary<MethodBase, MethodInfo> Redirects = new Dictionary<MethodBase, MethodInfo>(new MethodHandleComparer());

		private static int _redirectedThisPatch;

		public static RepairStats Run(Assembly asm, Harmony harmony, ManualLogSource log, bool verbose)
		{
			//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_004b: 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_005e: Expected O, but got Unknown
			//IL_0544: Unknown result type (might be due to invalid IL or missing references)
			//IL_0549: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00de: Unknown result type (might be due to invalid IL or missing references)
			//IL_0776: Unknown result type (might be due to invalid IL or missing references)
			//IL_077b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0369: Unknown result type (might be due to invalid IL or missing references)
			//IL_036e: Unknown result type (might be due to invalid IL or missing references)
			//IL_062d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0632: Unknown result type (might be due to invalid IL or missing references)
			//IL_051f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0526: Expected O, but got Unknown
			RepairStats repairStats = new RepairStats();
			string location = asm.Location;
			if (string.IsNullOrEmpty(location) || !File.Exists(location))
			{
				log.LogError((object)("Cannot locate the file for assembly " + asm.GetName().Name + "; unable to scan it."));
				return repairStats;
			}
			ReaderParameters val = new ReaderParameters
			{
				ReadingMode = (ReadingMode)2,
				ReadSymbols = false,
				AssemblyResolver = (IAssemblyResolver)(object)new LoadedAssemblyResolver()
			};
			ModuleDefinition val2 = ModuleDefinition.ReadModule(location, val);
			try
			{
				Dictionary<MethodDefinition, Dictionary<string, BrokenCall>> poisoned = new Dictionary<MethodDefinition, Dictionary<string, BrokenCall>>();
				Dictionary<string, BrokenCall> dictionary = new Dictionary<string, BrokenCall>();
				HashSet<string> hashSet = new HashSet<string>();
				HashSet<string> hashSet2 = new HashSet<string>();
				foreach (TypeDefinition type in val2.GetTypes())
				{
					Enumerator<MethodDefinition> enumerator2 = type.Methods.GetEnumerator();
					try
					{
						while (enumerator2.MoveNext())
						{
							MethodDefinition current = enumerator2.Current;
							if (!current.HasBody)
							{
								continue;
							}
							Enumerator<Instruction> enumerator3 = current.Body.Instructions.GetEnumerator();
							try
							{
								while (enumerator3.MoveNext())
								{
									Instruction current2 = enumerator3.Current;
									object operand = current2.Operand;
									FieldReference val3 = (FieldReference)((operand is FieldReference) ? operand : null);
									if (val3 != null && ((MemberReference)val3).DeclaringType.GetElementType().Scope is AssemblyNameReference && !(((MemberReference)val3).DeclaringType is GenericInstanceType))
									{
										if (!hashSet2.Contains(((MemberReference)val3).FullName) && FieldIsMissing(val3))
										{
											hashSet2.Add(((MemberReference)val3).FullName);
											log.LogError((object)(((MemberReference)current).FullName + " uses field " + ((MemberReference)val3).FullName + ", which no longer exists. Fields cannot be shimmed; this method will still throw."));
											repairStats.Unfixable++;
										}
										continue;
									}
									object operand2 = current2.Operand;
									MethodReference val4 = (MethodReference)((operand2 is MethodReference) ? operand2 : null);
									if (val4 == null || !(((MemberReference)val4).DeclaringType.GetElementType().Scope is AssemblyNameReference) || val4 is GenericInstanceMethod || ((MemberReference)val4).DeclaringType is GenericInstanceType)
									{
										continue;
									}
									string fullName = ((MemberReference)val4).FullName;
									if (!dictionary.TryGetValue(fullName, out var value))
									{
										value = (dictionary[fullName] = Inspect(val4, log, verbose, out var broken));
										if (broken && value == null)
										{
											hashSet.Add(fullName);
										}
									}
									if (value == null && !hashSet.Contains(fullName))
									{
										continue;
									}
									if (value == null)
									{
										log.LogError((object)(((MemberReference)current).FullName + " calls " + fullName + ", which no longer exists, and no compatible overload was found. This method will still throw."));
										repairStats.Unfixable++;
										continue;
									}
									if (!poisoned.TryGetValue(current, out var value2))
									{
										value2 = (poisoned[current] = new Dictionary<string, BrokenCall>());
									}
									value2[fullName] = value;
								}
							}
							finally
							{
								((IDisposable)enumerator3/*cast due to .constrained prefix*/).Dispose();
							}
						}
					}
					finally
					{
						((IDisposable)enumerator2/*cast due to .constrained prefix*/).Dispose();
					}
				}
				if (poisoned.Count == 0)
				{
					log.LogInfo((object)(asm.GetName().Name + ".dll: all game method references resolve; nothing to repair."));
					return repairStats;
				}
				repairStats.PoisonedMethods = poisoned.Count;
				Dictionary<int, MethodInfo> fixedByToken = new Dictionary<int, MethodInfo>();
				List<MethodDefinition> list = new List<MethodDefinition>(poisoned.Keys);
				Dictionary<MethodDefinition, MethodBase> dictionary3 = new Dictionary<MethodDefinition, MethodBase>();
				MetadataToken metadataToken;
				foreach (MethodDefinition item in list)
				{
					Module manifestModule = asm.ManifestModule;
					metadataToken = ((MemberReference)item).MetadataToken;
					dictionary3[item] = manifestModule.ResolveMethod(((MetadataToken)(ref metadataToken)).ToInt32());
				}
				bool flag = true;
				while (list.Count > 0 && flag)
				{
					flag = false;
					MethodDefinition[] array = list.ToArray();
					foreach (MethodDefinition def in array)
					{
						if (!((IEnumerable<Instruction>)def.Body.Instructions).Any(delegate(Instruction val6)
						{
							//IL_0037: Unknown result type (might be due to invalid IL or missing references)
							//IL_003c: Unknown result type (might be due to invalid IL or missing references)
							object operand3 = val6.Operand;
							MethodDefinition val7 = (MethodDefinition)((operand3 is MethodDefinition) ? operand3 : null);
							if (val7 != null && val7 != def && poisoned.ContainsKey(val7))
							{
								Dictionary<int, MethodInfo> dictionary5 = fixedByToken;
								MetadataToken metadataToken2 = ((MemberReference)val7).MetadataToken;
								return !dictionary5.ContainsKey(((MetadataToken)(ref metadataToken2)).ToInt32());
							}
							return false;
						}))
						{
							Build(def, dictionary3[def], poisoned[def], fixedByToken, log, verbose, repairStats);
							list.Remove(def);
							flag = true;
						}
					}
				}
				foreach (MethodDefinition item2 in list)
				{
					log.LogWarning((object)(((MemberReference)item2).FullName + " and another broken method call each other; fixing without redirecting the inner call."));
					Build(item2, dictionary3[item2], poisoned[item2], fixedByToken, log, verbose, repairStats);
				}
				HashSet<MethodDefinition> fixedDefs = new HashSet<MethodDefinition>(poisoned.Keys.Where(delegate(MethodDefinition d)
				{
					//IL_0007: 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)
					Dictionary<int, MethodInfo> dictionary5 = fixedByToken;
					MetadataToken metadataToken2 = ((MemberReference)d).MetadataToken;
					return dictionary5.ContainsKey(((MetadataToken)(ref metadataToken2)).ToInt32());
				}));
				HashSet<MethodDefinition> referenced = new HashSet<MethodDefinition>();
				HarmonyMethod val5 = new HarmonyMethod(typeof(AssemblyRepair), "RedirectTranspiler", (Type[])null);
				foreach (TypeDefinition type2 in val2.GetTypes())
				{
					Enumerator<MethodDefinition> enumerator2 = type2.Methods.GetEnumerator();
					try
					{
						while (enumerator2.MoveNext())
						{
							MethodDefinition current5 = enumerator2.Current;
							if (!current5.HasBody || poisoned.ContainsKey(current5))
							{
								continue;
							}
							List<MethodDefinition> list2 = (from d in ((IEnumerable<Instruction>)current5.Body.Instructions).Select(delegate(Instruction val6)
								{
									object operand3 = val6.Operand;
									return (MethodDefinition)((operand3 is MethodDefinition) ? operand3 : null);
								})
								where d != null && fixedDefs.Contains(d)
								select d).Distinct().ToList();
							if (list2.Count == 0)
							{
								continue;
							}
							foreach (MethodDefinition item3 in list2)
							{
								referenced.Add(item3);
							}
							Module manifestModule2 = asm.ManifestModule;
							metadataToken = ((MemberReference)current5).MetadataToken;
							MethodBase methodBase = manifestModule2.ResolveMethod(((MetadataToken)(ref metadataToken)).ToInt32());
							try
							{
								_redirectedThisPatch = 0;
								harmony.Patch(methodBase, (HarmonyMethod)null, (HarmonyMethod)null, val5, (HarmonyMethod)null, (HarmonyMethod)null);
								repairStats.RedirectedCallSites += _redirectedThisPatch;
								if (verbose)
								{
									log.LogInfo((object)$"  {((MemberReference)current5).FullName}: {_redirectedThisPatch} call site(s) redirected");
								}
							}
							catch (Exception arg)
							{
								log.LogError((object)string.Format("Could not patch {0} (calls {1}): {2}", ((MemberReference)current5).FullName, string.Join(", ", list2.Select((MethodDefinition t) => ((MemberReference)t).Name)), arg));
								repairStats.Unfixable++;
							}
						}
					}
					finally
					{
						((IDisposable)enumerator2/*cast due to .constrained prefix*/).Dispose();
					}
				}
				foreach (MethodDefinition item4 in fixedDefs.Where((MethodDefinition d) => !referenced.Contains(d)))
				{
					MethodBase poisoned2 = dictionary3[item4];
					Dictionary<int, MethodInfo> dictionary4 = fixedByToken;
					metadataToken = ((MemberReference)item4).MetadataToken;
					MethodInfo copy = dictionary4[((MetadataToken)(ref metadataToken)).ToInt32()];
					int num = RepatchHarmony(harmony, poisoned2, copy, log, verbose);
					repairStats.RepatchedHarmonyMethods += num;
					if (num == 0)
					{
						log.LogWarning((object)(((MemberReference)item4).FullName + " is broken but is not called from its own assembly (Unity message, reflection or delegate?). It has been fixed but the fix cannot be wired in; it will still throw if invoked."));
					}
				}
				return repairStats;
			}
			finally
			{
				((IDisposable)val2)?.Dispose();
			}
		}

		private static BrokenCall Inspect(MethodReference mref, ManualLogSource log, bool verbose, out bool broken)
		{
			broken = false;
			bool flag = ((MemberReference)mref).Name == ".ctor" || ((MemberReference)mref).Name == ".cctor";
			IEnumerable<MethodBase> source;
			try
			{
				Type type = ResolveType(((MemberReference)mref).DeclaringType);
				if (type == null)
				{
					return null;
				}
				source = (flag ? type.GetConstructors(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic).Cast<MethodBase>() : (from m in type.GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)
					where m.Name == ((MemberReference)mref).Name
					select m).Cast<MethodBase>());
				source = source.Where((MethodBase c) => GenericArity(c) == mref.GenericParameters.Count && ReturnMatches(mref, c)).ToList();
			}
			catch (Exception ex)
			{
				if (verbose)
				{
					log.LogDebug((object)("Cannot inspect " + ((MemberReference)mref).FullName + ": " + ex.GetType().Name + ": " + ex.Message));
				}
				return null;
			}
			if (source.Any((MethodBase c) => ParamsMatch(mref, c.GetParameters(), exact: true)))
			{
				return null;
			}
			broken = true;
			MethodBase methodBase = (from c in source
				where c.GetParameters().Length > mref.Parameters.Count && ParamsMatch(mref, c.GetParameters(), exact: false)
				orderby c.GetParameters().Length
				select c).FirstOrDefault();
			if (methodBase == null)
			{
				return null;
			}
			ParameterInfo[] array = methodBase.GetParameters().Skip(mref.Parameters.Count).ToArray();
			ParameterInfo[] array2 = array;
			foreach (ParameterInfo parameterInfo in array2)
			{
				if (!parameterInfo.HasDefaultValue)
				{
					log.LogWarning((object)(((MemberReference)mref).FullName + ": new parameter '" + parameterInfo.Name + "' (" + parameterInfo.ParameterType.Name + ") has no compiler default; passing zero/null."));
				}
			}
			log.LogInfo((object)("Stale reference " + ((MemberReference)mref).FullName + " → " + DescribeReplacement(methodBase, array)));
			return new BrokenCall
			{
				Key = ((MemberReference)mref).FullName,
				Replacement = methodBase,
				Extras = array
			};
		}

		private static string DescribeReplacement(MethodBase m, ParameterInfo[] extras)
		{
			return $"{m.DeclaringType.Name}.{m.Name}({m.GetParameters().Length} params) with " + string.Join(", ", extras.Select((ParameterInfo p) => string.Format("{0}={1}", p.Name, p.HasDefaultValue ? (p.DefaultValue ?? "null") : "default")));
		}

		private static bool FieldIsMissing(FieldReference fref)
		{
			try
			{
				Type type = ResolveType(((MemberReference)fref).DeclaringType);
				if (type == null)
				{
					return false;
				}
				return type.GetField(((MemberReference)fref).Name, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic) == null;
			}
			catch
			{
				return false;
			}
		}

		private static int GenericArity(MethodBase m)
		{
			if (!m.IsGenericMethodDefinition)
			{
				return 0;
			}
			return m.GetGenericArguments().Length;
		}

		private static bool ReturnMatches(MethodReference mref, MethodBase m)
		{
			if (m is MethodInfo methodInfo)
			{
				return TypeMatches(mref.ReturnType, methodInfo.ReturnType);
			}
			return ((MemberReference)mref.ReturnType).FullName == "System.Void";
		}

		private static bool ParamsMatch(MethodReference mref, ParameterInfo[] ps, bool exact)
		{
			if (exact ? (ps.Length != mref.Parameters.Count) : (ps.Length < mref.Parameters.Count))
			{
				return false;
			}
			for (int i = 0; i < mref.Parameters.Count; i++)
			{
				if (!TypeMatches(((ParameterReference)mref.Parameters[i]).ParameterType, ps[i].ParameterType))
				{
					return false;
				}
			}
			return true;
		}

		private static bool TypeMatches(TypeReference tr, Type t)
		{
			if (t == null)
			{
				return false;
			}
			ByReferenceType val = (ByReferenceType)(object)((tr is ByReferenceType) ? tr : null);
			if (val == null)
			{
				PointerType val2 = (PointerType)(object)((tr is PointerType) ? tr : null);
				if (val2 == null)
				{
					ArrayType val3 = (ArrayType)(object)((tr is ArrayType) ? tr : null);
					if (val3 == null)
					{
						IModifierType val4 = (IModifierType)(object)((tr is IModifierType) ? tr : null);
						if (val4 == null)
						{
							GenericInstanceType val5 = (GenericInstanceType)(object)((tr is GenericInstanceType) ? tr : null);
							if (val5 == null)
							{
								GenericParameter val6 = (GenericParameter)(object)((tr is GenericParameter) ? tr : null);
								if (val6 != null)
								{
									if (t.IsGenericParameter && t.GenericParameterPosition == val6.Position)
									{
										return val6.Owner is MethodReference == (t.DeclaringMethod != null);
									}
									return false;
								}
								return t.FullName == RuntimeName(tr);
							}
							if (!t.IsGenericType || t.IsGenericTypeDefinition || !TypeMatches(((TypeSpecification)val5).ElementType, t.GetGenericTypeDefinition()))
							{
								return false;
							}
							Type[] genericArguments = t.GetGenericArguments();
							if (genericArguments.Length != val5.GenericArguments.Count)
							{
								return false;
							}
							for (int i = 0; i < genericArguments.Length; i++)
							{
								if (!TypeMatches(val5.GenericArguments[i], genericArguments[i]))
								{
									return false;
								}
							}
							return true;
						}
						return TypeMatches(val4.ElementType, t);
					}
					if (t.IsArray && t.GetArrayRank() == val3.Rank)
					{
						return TypeMatches(((TypeSpecification)val3).ElementType, t.GetElementType());
					}
					return false;
				}
				if (t.IsPointer)
				{
					return TypeMatches(((TypeSpecification)val2).ElementType, t.GetElementType());
				}
				return false;
			}
			if (t.IsByRef)
			{
				return TypeMatches(((TypeSpecification)val).ElementType, t.GetElementType());
			}
			return false;
		}

		private static string RuntimeName(TypeReference tr)
		{
			return ((MemberReference)tr).FullName.Replace('/', '+');
		}

		private static Type ResolveType(TypeReference tr)
		{
			AssemblyNameReference scope = default(AssemblyNameReference);
			ref AssemblyNameReference reference = ref scope;
			IMetadataScope scope2 = tr.GetElementType().Scope;
			reference = (AssemblyNameReference)(object)((scope2 is AssemblyNameReference) ? scope2 : null);
			if (scope == null)
			{
				return null;
			}
			return AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault((Assembly a) => a.GetName().Name == scope.Name)?.GetType(RuntimeName(tr), throwOnError: false);
		}

		private static void Build(MethodDefinition def, MethodBase original, Dictionary<string, BrokenCall> fixes, Dictionary<int, MethodInfo> fixedByToken, ManualLogSource log, bool verbose, RepairStats stats)
		{
			//IL_00ce: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d4: Expected O, but got Unknown
			//IL_017b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0180: 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_01ac: Expected O, but got Unknown
			//IL_01de: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e3: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f0: Unknown result type (might be due to invalid IL or missing references)
			//IL_0200: Unknown result type (might be due to invalid IL or missing references)
			//IL_0263: Unknown result type (might be due to invalid IL or missing references)
			//IL_0268: Unknown result type (might be due to invalid IL or missing references)
			//IL_04c1: Unknown result type (might be due to invalid IL or missing references)
			//IL_04c6: Unknown result type (might be due to invalid IL or missing references)
			//IL_04df: Unknown result type (might be due to invalid IL or missing references)
			//IL_04e4: Unknown result type (might be due to invalid IL or missing references)
			//IL_04e9: Unknown result type (might be due to invalid IL or missing references)
			//IL_02e5: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ec: Expected O, but got Unknown
			//IL_03ef: Unknown result type (might be due to invalid IL or missing references)
			//IL_03f4: Unknown result type (might be due to invalid IL or missing references)
			//IL_050d: Unknown result type (might be due to invalid IL or missing references)
			//IL_040b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0531: Unknown result type (might be due to invalid IL or missing references)
			//IL_0625: Unknown result type (might be due to invalid IL or missing references)
			//IL_062a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0746: Unknown result type (might be due to invalid IL or missing references)
			//IL_074b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0555: Unknown result type (might be due to invalid IL or missing references)
			//IL_066e: Unknown result type (might be due to invalid IL or missing references)
			//IL_063d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0642: Unknown result type (might be due to invalid IL or missing references)
			//IL_0579: Unknown result type (might be due to invalid IL or missing references)
			//IL_059d: Unknown result type (might be due to invalid IL or missing references)
			//IL_05c2: Expected O, but got Unknown
			try
			{
				if (original is ConstructorInfo)
				{
					log.LogError((object)(((MemberReference)def).FullName + " is a constructor; constructors cannot be redirected. Not fixed."));
					stats.Unfixable++;
					return;
				}
				List<Type> list = new List<Type>();
				if (!original.IsStatic)
				{
					list.Add(original.DeclaringType.IsValueType ? original.DeclaringType.MakeByRefType() : original.DeclaringType);
				}
				list.AddRange(from p in original.GetParameters()
					select p.ParameterType);
				DynamicMethodDefinition val = new DynamicMethodDefinition("BTShim_" + ((MemberReference)def.DeclaringType).Name + "_" + ((MemberReference)def).Name, ((MethodInfo)original).ReturnType, list.ToArray());
				MethodDefinition definition = val.Definition;
				ModuleDefinition module = val.Module;
				int num = ((!original.IsStatic) ? 1 : 0);
				if (num == 1)
				{
					((ParameterReference)((MethodReference)definition).Parameters[0]).Name = "this";
				}
				for (int num2 = 0; num2 < ((MethodReference)def).Parameters.Count; num2++)
				{
					((ParameterReference)((MethodReference)definition).Parameters[num2 + num]).Name = ((ParameterReference)((MethodReference)def).Parameters[num2]).Name;
				}
				MethodBody body = def.Body;
				MethodBody body2 = definition.Body;
				body2.InitLocals = body.InitLocals;
				body2.Variables.Clear();
				Enumerator<VariableDefinition> enumerator = body.Variables.GetEnumerator();
				try
				{
					while (enumerator.MoveNext())
					{
						VariableDefinition current = enumerator.Current;
						body2.Variables.Add(new VariableDefinition(module.ImportReference(((VariableReference)current).VariableType)));
					}
				}
				finally
				{
					((IDisposable)enumerator/*cast due to .constrained prefix*/).Dispose();
				}
				Dictionary<Instruction, Instruction> map = new Dictionary<Instruction, Instruction>();
				List<(Instruction, BrokenCall)> list2 = new List<(Instruction, BrokenCall)>();
				Enumerator<Instruction> enumerator2 = body.Instructions.GetEnumerator();
				try
				{
					while (enumerator2.MoveNext())
					{
						Instruction current2 = enumerator2.Current;
						Instruction val2 = Instruction.Create(OpCodes.Nop);
						val2.OpCode = current2.OpCode;
						val2.Operand = current2.Operand;
						val2.Offset = current2.Offset;
						body2.Instructions.Add(val2);
						map[current2] = val2;
					}
				}
				finally
				{
					((IDisposable)enumerator2/*cast due to .constrained prefix*/).Dispose();
				}
				enumerator2 = body2.Instructions.GetEnumerator();
				MetadataToken metadataToken;
				try
				{
					while (enumerator2.MoveNext())
					{
						Instruction current3 = enumerator2.Current;
						object operand = current3.Operand;
						Instruction val3 = (Instruction)((operand is Instruction) ? operand : null);
						if (val3 == null)
						{
							if (!(operand is Instruction[] array))
							{
								VariableDefinition val4 = (VariableDefinition)((operand is VariableDefinition) ? operand : null);
								if (val4 == null)
								{
									ParameterDefinition val5 = (ParameterDefinition)((operand is ParameterDefinition) ? operand : null);
									if (val5 == null)
									{
										MethodDefinition val6 = (MethodDefinition)((operand is MethodDefinition) ? operand : null);
										MethodReference val7;
										if (val6 == null)
										{
											val7 = (MethodReference)((operand is MethodReference) ? operand : null);
											if (val7 == null)
											{
												FieldReference val8 = (FieldReference)((operand is FieldReference) ? operand : null);
												if (val8 == null)
												{
													TypeReference val9 = (TypeReference)((operand is TypeReference) ? operand : null);
													if (val9 == null)
													{
														CallSite val10 = (CallSite)((operand is CallSite) ? operand : null);
														if (val10 != null)
														{
															CallSite val11 = val10;
															current3.Operand = Extensions.ImportReference(module, (IMetadataTokenProvider)(object)val11);
														}
													}
													else
													{
														TypeReference val12 = val9;
														current3.Operand = module.ImportReference(val12);
													}
												}
												else
												{
													FieldReference val13 = val8;
													current3.Operand = module.ImportReference(val13);
												}
												continue;
											}
										}
										else
										{
											MethodDefinition val14 = val6;
											if (val14 != def)
											{
												metadataToken = ((MemberReference)val14).MetadataToken;
												if (fixedByToken.TryGetValue(((MetadataToken)(ref metadataToken)).ToInt32(), out var value))
												{
													current3.OpCode = OpCodes.Call;
													current3.Operand = module.ImportReference((MethodBase)value);
													continue;
												}
											}
											val7 = (MethodReference)operand;
										}
										MethodReference val15 = val7;
										if (fixes.TryGetValue(((MemberReference)val15).FullName, out var value2))
										{
											list2.Add((current3, value2));
										}
										current3.Operand = module.ImportReference(val15);
									}
									else
									{
										ParameterDefinition val16 = val5;
										current3.Operand = ((((ParameterReference)val16).Index < 0) ? ((MethodReference)definition).Parameters[0] : ((MethodReference)definition).Parameters[((ParameterReference)val16).Index + num]);
									}
								}
								else
								{
									VariableDefinition val17 = val4;
									current3.Operand = body2.Variables[((VariableReference)val17).Index];
								}
							}
							else
							{
								Instruction[] source = array;
								current3.Operand = source.Select((Instruction t) => map[t]).ToArray();
							}
						}
						else
						{
							Instruction key = val3;
							current3.Operand = map[key];
						}
					}
				}
				finally
				{
					((IDisposable)enumerator2/*cast due to .constrained prefix*/).Dispose();
				}
				Enumerator<ExceptionHandler> enumerator3 = body.ExceptionHandlers.GetEnumerator();
				try
				{
					while (enumerator3.MoveNext())
					{
						ExceptionHandler current4 = enumerator3.Current;
						body2.ExceptionHandlers.Add(new ExceptionHandler(current4.HandlerType)
						{
							TryStart = ((current4.TryStart == null) ? null : map[current4.TryStart]),
							TryEnd = ((current4.TryEnd == null) ? null : map[current4.TryEnd]),
							HandlerStart = ((current4.HandlerStart == null) ? null : map[current4.HandlerStart]),
							HandlerEnd = ((current4.HandlerEnd == null) ? null : map[current4.HandlerEnd]),
							FilterStart = ((current4.FilterStart == null) ? null : map[current4.FilterStart]),
							CatchType = ((current4.CatchType == null) ? null : module.ImportReference(current4.CatchType))
						});
					}
				}
				finally
				{
					((IDisposable)enumerator3/*cast due to .constrained prefix*/).Dispose();
				}
				ILProcessor iLProcessor = body2.GetILProcessor();
				MethodBodyRocks.SimplifyMacros(body2);
				int num3 = 0;
				foreach (var (val18, brokenCall) in list2)
				{
					if (val18.Previous != null && (val18.Previous.OpCode == OpCodes.Constrained || val18.Previous.OpCode == OpCodes.Tail))
					{
						log.LogError((object)$"{((MemberReference)def).FullName}: stale call at IL_{val18.Offset:X4} has a '{val18.Previous.OpCode}' prefix; not rewritten.");
						stats.Unfixable++;
						continue;
					}
					if (verbose)
					{
						log.LogInfo((object)$"  {((MemberReference)def).FullName} IL_{val18.Offset:X4}: {brokenCall.Key} → {brokenCall.Replacement.DeclaringType.Name}.{brokenCall.Replacement.Name}({brokenCall.Replacement.GetParameters().Length})");
					}
					RewriteCall(iLProcessor, val18, brokenCall, module);
					num3++;
				}
				MethodInfo value3 = DMDGenerator<DMDCecilGenerator>.Generate(val, (object)null);
				metadataToken = ((MemberReference)def).MetadataToken;
				fixedByToken[((MetadataToken)(ref metadataToken)).ToInt32()] = value3;
				Redirects[original] = value3;
				stats.RewrittenCalls += num3;
				log.LogInfo((object)$"Fixed {((MemberReference)def).FullName} ({num3} stale call(s))");
			}
			catch (Exception arg)
			{
				log.LogError((object)$"Could not build a fixed copy of {((MemberReference)def).FullName}: {arg}");
				stats.Unfixable++;
			}
		}

		private static void RewriteCall(ILProcessor il, Instruction call, BrokenCall fix, ModuleDefinition module)
		{
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_0065: Unknown result type (might be due to invalid IL or missing references)
			List<Instruction> list = new List<Instruction>();
			ParameterInfo[] extras = fix.Extras;
			foreach (ParameterInfo p in extras)
			{
				EmitDefault(il, module, p, list);
			}
			Instruction val = il.Create(call.OpCode, module.ImportReference(fix.Replacement));
			if (list.Count == 0)
			{
				call.Operand = val.Operand;
				return;
			}
			call.OpCode = list[0].OpCode;
			call.Operand = list[0].Operand;
			Instruction val2 = call;
			foreach (Instruction item in list.Skip(1))
			{
				il.InsertAfter(val2, item);
				val2 = item;
			}
			il.InsertAfter(val2, val);
		}

		private static void EmitDefault(ILProcessor il, ModuleDefinition module, ParameterInfo p, List<Instruction> loads)
		{
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			//IL_0086: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fb: 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_01e2: Unknown result type (might be due to invalid IL or missing references)
			//IL_0225: Unknown result type (might be due to invalid IL or missing references)
			//IL_0236: Unknown result type (might be due to invalid IL or missing references)
			//IL_024d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0253: Expected O, but got Unknown
			//IL_0266: Unknown result type (might be due to invalid IL or missing references)
			//IL_0278: Unknown result type (might be due to invalid IL or missing references)
			//IL_028f: Unknown result type (might be due to invalid IL or missing references)
			Type parameterType = p.ParameterType;
			object obj = (p.HasDefaultValue ? p.DefaultValue : null);
			if (obj == DBNull.Value || obj == Type.Missing)
			{
				obj = null;
			}
			if (!parameterType.IsValueType)
			{
				loads.Add((obj is string text) ? il.Create(OpCodes.Ldstr, text) : il.Create(OpCodes.Ldnull));
				return;
			}
			Type type = (parameterType.IsEnum ? Enum.GetUnderlyingType(parameterType) : parameterType);
			if (type == typeof(bool))
			{
				OpCode ldc_I = OpCodes.Ldc_I4;
				bool flag = default(bool);
				int num;
				if (obj is bool)
				{
					flag = (bool)obj;
					num = 1;
				}
				else
				{
					num = 0;
				}
				loads.Add(il.Create(ldc_I, (((uint)num & (flag ? 1u : 0u)) != 0) ? 1 : 0));
			}
			else if (type == typeof(float))
			{
				loads.Add(il.Create(OpCodes.Ldc_R4, (obj == null) ? 0f : Convert.ToSingle(obj)));
			}
			else if (type == typeof(double))
			{
				loads.Add(il.Create(OpCodes.Ldc_R8, (obj == null) ? 0.0 : Convert.ToDouble(obj)));
			}
			else if (type == typeof(long) || type == typeof(ulong))
			{
				loads.Add(il.Create(OpCodes.Ldc_I8, (obj == null) ? 0 : Convert.ToInt64(obj)));
			}
			else if (type == typeof(int) || type == typeof(uint) || type == typeof(short) || type == typeof(ushort) || type == typeof(byte) || type == typeof(sbyte) || type == typeof(char))
			{
				loads.Add(il.Create(OpCodes.Ldc_I4, (int)((obj != null) ? Convert.ToInt64(obj) : 0)));
			}
			else if (type == typeof(IntPtr) || type == typeof(UIntPtr))
			{
				loads.Add(il.Create(OpCodes.Ldc_I4_0));
				loads.Add(il.Create(OpCodes.Conv_I));
			}
			else
			{
				VariableDefinition val = new VariableDefinition(module.ImportReference(parameterType));
				il.Body.Variables.Add(val);
				loads.Add(il.Create(OpCodes.Ldloca, val));
				loads.Add(il.Create(OpCodes.Initobj, ((VariableReference)val).VariableType));
				loads.Add(il.Create(OpCodes.Ldloc, val));
			}
		}

		private static IEnumerable<CodeInstruction> RedirectTranspiler(IEnumerable<CodeInstruction> instructions)
		{
			foreach (CodeInstruction instruction in instructions)
			{
				if (instruction.operand is MethodBase key && Redirects.TryGetValue(key, out var value))
				{
					if (instruction.opcode == OpCodes.Call || instruction.opcode == OpCodes.Callvirt)
					{
						instruction.opcode = OpCodes.Call;
						instruction.operand = value;
						_redirectedThisPatch++;
					}
					else if (instruction.opcode == OpCodes.Ldftn || instruction.opcode == OpCodes.Ldvirtftn)
					{
						instruction.opcode = OpCodes.Ldftn;
						instruction.operand = value;
						_redirectedThisPatch++;
					}
				}
				yield return instruction;
			}
		}

		private static int RepatchHarmony(Harmony harmony, MethodBase poisoned, MethodInfo copy, ManualLogSource log, bool verbose)
		{
			//IL_00fc: Unknown result type (might be due to invalid IL or missing references)
			//IL_0101: Unknown result type (might be due to invalid IL or missing references)
			//IL_010e: Unknown result type (might be due to invalid IL or missing references)
			//IL_011b: Unknown result type (might be due to invalid IL or missing references)
			//IL_012a: Expected O, but got Unknown
			int num = 0;
			foreach (MethodBase item in Harmony.GetAllPatchedMethods().ToList())
			{
				Patches patchInfo = Harmony.GetPatchInfo(item);
				if (patchInfo == null)
				{
					continue;
				}
				(ReadOnlyCollection<Patch>, int)[] array = new(ReadOnlyCollection<Patch>, int)[4]
				{
					(patchInfo.Prefixes, 0),
					(patchInfo.Postfixes, 1),
					(patchInfo.Transpilers, 2),
					(patchInfo.Finalizers, 3)
				};
				for (int i = 0; i < array.Length; i++)
				{
					(ReadOnlyCollection<Patch>, int) tuple = array[i];
					foreach (Patch item2 in tuple.Item1.Where((Patch p) => p.PatchMethod != null && p.PatchMethod.MethodHandle.Value == poisoned.MethodHandle.Value).ToList())
					{
						harmony.Unpatch(item, item2.PatchMethod);
						HarmonyMethod val = new HarmonyMethod(copy)
						{
							priority = item2.priority,
							before = item2.before,
							after = item2.after
						};
						switch (tuple.Item2)
						{
						case 0:
							harmony.Patch(item, val, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
							break;
						case 1:
							harmony.Patch(item, (HarmonyMethod)null, val, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
							break;
						case 2:
							harmony.Patch(item, (HarmonyMethod)null, (HarmonyMethod)null, val, (HarmonyMethod)null, (HarmonyMethod)null);
							break;
						default:
							harmony.Patch(item, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, val, (HarmonyMethod)null);
							break;
						}
						num++;
						log.LogInfo((object)("Re-registered Harmony patch " + poisoned.DeclaringType?.Name + "." + poisoned.Name + " on " + item.DeclaringType?.Name + "." + item.Name + " with its fixed copy."));
					}
				}
			}
			return num;
		}
	}
	internal static class RespawnRestore
	{
		private static ManualLogSource Log;

		private static ConfigEntry<bool> Enabled;

		private static Type _ctrlType;

		private static FieldInfo _ctrlInventoryItem;

		private static MethodInfo _getActiveBeast;

		private static MethodInfo _setEquipped;

		private static int _beastSlot = 100;

		private static MethodInfo _getInventory;

		private static MethodInfo _getAllItems;

		private static MethodInfo _equipItem;

		private static FieldInfo _itemShared;

		private static FieldInfo _sharedName;

		private static FieldInfo _sharedType;

		public static void Init(ConfigFile config, PluginInfo bt, Harmony harmony, ManualLogSource log)
		{
			//IL_0367: Unknown result type (might be due to invalid IL or missing references)
			//IL_0374: Expected O, but got Unknown
			Log = log;
			if (!GameRefs.Init(log))
			{
				Log.LogInfo((object)"RespawnRestore: game types not resolved; module inactive.");
				return;
			}
			Type type = ((object)bt.Instance).GetType();
			_ctrlType = AccessTools.Field(type, "ctrl")?.FieldType;
			_ctrlInventoryItem = ((_ctrlType == null) ? null : (AccessTools.Field(_ctrlType, "RaceInventoryItem") ?? AccessTools.Field(_ctrlType, "InventoryItem")));
			_getActiveBeast = AccessTools.Method(type, "GetActiveBeast", (Type[])null, (Type[])null);
			string[] array = new string[4] { "SetRaceEquippedItem", "SetRaceEquipedItem", "SetTribeEquippedItem", "SetTribeEquipedItem" };
			foreach (string text in array)
			{
				if ((_setEquipped = AccessTools.Method(type, text, (Type[])null, (Type[])null)) != null)
				{
					break;
				}
			}
			FieldInfo fieldInfo = AccessTools.Field(type, "BeastItemSlot");
			if (fieldInfo != null && fieldInfo.IsStatic)
			{
				try
				{
					_beastSlot = Convert.ToInt32(fieldInfo.GetValue(null));
				}
				catch
				{
				}
			}
			_getInventory = AccessTools.Method(GameRefs.Humanoid, "GetInventory", (Type[])null, (Type[])null);
			Type type2 = _getInventory?.ReturnType;
			_getAllItems = ((type2 == null) ? null : AccessTools.Method(type2, "GetAllItems", (Type[])null, (Type[])null));
			Type type3 = _getAllItems?.ReturnType;
			Type type4 = ((type3 != null && type3.IsGenericType) ? type3.GetGenericArguments()[0] : null);
			_itemShared = ((type4 == null) ? null : AccessTools.Field(type4, "m_shared"));
			_sharedName = ((_itemShared == null) ? null : AccessTools.Field(_itemShared.FieldType, "m_name"));
			_sharedType = ((_itemShared == null) ? null : AccessTools.Field(_itemShared.FieldType, "m_itemType"));
			_equipItem = ((type4 == null) ? null : AccessTools.Method(GameRefs.Humanoid, "EquipItem", new Type[2]
			{
				type4,
				typeof(bool)
			}, (Type[])null));
			MethodInfo methodInfo = AccessTools.Method(GameRefs.Player, "OnSpawned", (Type[])null, (Type[])null);
			string text2 = null;
			if (_ctrlType == null || _ctrlInventoryItem == null)
			{
				text2 = "controller inventory-item field";
			}
			else if (_getActiveBeast == null)
			{
				text2 = "GetActiveBeast";
			}
			else if (_setEquipped == null)
			{
				text2 = "Set*EquippedItem";
			}
			else if (_getInventory == null || _getAllItems == null || _itemShared == null || _sharedName == null || _sharedType == null)
			{
				text2 = "inventory item members";
			}
			else if (_equipItem == null)
			{
				text2 = "Humanoid.EquipItem";
			}
			else if (methodInfo == null)
			{
				text2 = "Player.OnSpawned";
			}
			if (text2 != null)
			{
				Log.LogInfo((object)("RespawnRestore: " + text2 + " not found; module inactive."));
				return;
			}
			Enabled = config.Bind<bool>("RespawnRestore", "Enabled", true, "After spawning or respawning, re-equip the species named in BeastTribes' own config when BeastTribes did not restore it (e.g. you came back as a Viking after dying as a beast). Choose 'none' in the Beast menu to stay human. Off = BeastTribes' original behaviour.");
			harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, new HarmonyMethod(typeof(RespawnRestore), "SpawnedPostfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			Log.LogInfo((object)$"RespawnRestore: active for {bt.Metadata.Name} {bt.Metadata.Version} (enabled={Enabled.Value}, via {_setEquipped.Name}).");
		}

		private static void SpawnedPostfix(object __instance)
		{
			try
			{
				if (Enabled == null || !Enabled.Value)
				{
					return;
				}
				Component val = (Component)((__instance is Component) ? __instance : null);
				if ((Object)(object)val == (Object)null)
				{
					return;
				}
				Component val2 = GameRefs.LocalPlayer();
				if ((Object)(object)val2 == (Object)null || (Object)(object)val2.gameObject != (Object)(object)val.gameObject)
				{
					return;
				}
				Component component = val.GetComponent(_ctrlType);
				if ((Object)(object)component == (Object)null)
				{
					Log.LogDebug((object)"RespawnRestore: spawned player has no BeastTribes controller; nothing to restore.");
				}
				else
				{
					if (_ctrlInventoryItem.GetValue(component) != null)
					{
						return;
					}
					string text = ActiveBeast();
					if (string.IsNullOrEmpty(text) || IsHuman(text))
					{
						return;
					}
					object obj = null;
					object obj2 = _getInventory.Invoke(val, null);
					IEnumerable enumerable = ((obj2 == null) ? null : (_getAllItems.Invoke(obj2, null) as IEnumerable));
					if (enumerable != null)
					{
						foreach (object item in enumerable)
						{
							object value = _itemShared.GetValue(item);
							if (value != null && Convert.ToInt32(_sharedType.GetValue(value)) == _beastSlot && !(RaceFromItemName(_sharedName.GetValue(value) as string) != text))
							{
								obj = item;
								break;
							}
						}
					}
					if (obj != null)
					{
						Log.LogInfo((object)("RespawnRestore: BeastTribes did not re-apply '" + text + "' on spawn; equipping the '" + text + "' item from the inventory."));
						_equipItem.Invoke(val, new object[2] { obj, false });
					}
					else
					{
						Log.LogInfo((object)("RespawnRestore: no '" + text + "' item in the inventory after spawn (lost on death?); asking BeastTribes to re-create it."));
						_setEquipped.Invoke(null, new object[2] { val, text });
					}
				}
			}
			catch (Exception arg)
			{
				Log.LogWarning((object)$"RespawnRestore: failed, species left as BeastTribes set it: {arg}");
			}
		}

		private static string ActiveBeast()
		{
			object? obj = _getActiveBeast.Invoke(null, null);
			object? obj2 = ((obj is ConfigEntryBase) ? obj : null);
			return ((obj2 != null) ? ((ConfigEntryBase)obj2).BoxedValue : null) as string;
		}

		internal static bool IsHuman(string race)
		{
			if (!race.Equals("none", StringComparison.OrdinalIgnoreCase))
			{
				return race.Equals("BTnone", StringComparison.OrdinalIgnoreCase);
			}
			return true;
		}

		internal static string RaceFromItemName(string name)
		{
			if (string.IsNullOrEmpty(name))
			{
				return null;
			}
			string text = name.TrimStart(new char[1] { '$' });
			if (text.StartsWith("BeastRace", StringComparison.Ordinal))
			{
				text = text.Substring("BeastRace".Length);
			}
			if (text.EndsWith("Name", StringComparison.Ordinal))
			{
				text = text.Substring(0, text.Length - "Name".Length);
			}
			return text.TrimEnd(new char[1] { '_' });
		}
	}
	internal static class TailPhysics
	{
		private sealed class ChainConfig
		{
			public ConfigEntry<float> Damping;

			public ConfigEntry<float> Elasticity;

			public ConfigEntry<float> Stiffness;

			public ConfigEntry<float> Inert;

			public ConfigEntry<float> Radius;

			public ConfigEntry<float> EndLength;

			public ConfigEntry<float> ForceY;

			public ConfigEntry<float> DampingTip;

			public ConfigEntry<float> ElasticityTip;

			public ConfigEntry<float> StiffnessTip;

			public ConfigEntry<float> InertTip;

			public ConfigEntry<float> GradientShape;

			public IEnumerable<ConfigEntry<float>> All => new ConfigEntry<float>[12]
			{
				Damping, Elasticity, Stiffness, Inert, Radius, EndLength, ForceY, DampingTip, ElasticityTip, StiffnessTip,
				InertTip, GradientShape
			};
		}

		private sealed class Owned
		{
			public GameObject Body;

			public List<Component> Components = new List<Component>();
		}

		private static ManualLogSource Log;

		private static ConfigEntry<bool> Enabled;

		private static ConfigEntry<string> TailRoots;

		private static ConfigEntry<string> EarRoots;

		private static ConfigEntry<bool> AddColliders;

		private static ConfigEntry<float> ReferenceScale;

		private static ConfigEntry<float> SpineColliderSkipWithin;

		private static ConfigEntry<float> HipColliderRadius;

		private static ChainConfig Tail;

		private static ChainConfig Ear;

		private static ChainConfig Wing;

		private static ConfigEntry<bool> WingChains;

		private static ConfigEntry<bool> WingsInFlight;

		private static ConfigEntry<string> WingRoots;

		private static FieldInfo _additionalItemField;

		private static FieldInfo _wingAnimatorField;

		private static FieldInfo _flightEnabledField;

		private static readonly Dictionary<object, Owned> _wingsByCtrl = new Dictionary<object, Owned>();

		private const float Pct = 100f;

		private static Type _ctrlType;

		private static Type _dynBoneType;

		private static Type _colliderType;

		private static FieldInfo _bodyItemField;

		private static MethodInfo _setupParticles;

		private static FieldInfo _playerField;

		private static FieldInfo _visEquipField;

		private static FieldInfo _bodyModelField;

		private static readonly Dictionary<Transform, Owned> _byRoot = new Dictionary<Transform, Owned>();

		private static readonly HashSet<Component> _ours = new HashSet<Component>();

		private static readonly HashSet<string> _loggedBodies = new HashSet<string>();

		private static ConfigEntry<bool> SkipBaseBone;

		private static readonly (string bone, float radius, float halfLength, float centerY)[] ColliderLayout = new(string, float, float, float)[3]
		{
			("Hips", 0.285f, 20f, -0.285f),
			("LeftUpLeg", 0.1425f, 0f, 0.19f),
			("RightUpLeg", 0.1425f, 0f, 0.19f)
		};

		private const float UiScale = 10000f;

		private static readonly Regex WingName = new Regex("wing$", RegexOptions.IgnoreCase);

		private static readonly Regex TailName = new Regex("tail\\d*$", RegexOptions.IgnoreCase);

		private static readonly Regex EarName = new Regex("^ear(\\.|_|\\d|$)", RegexOptions.IgnoreCase);

		public static void Init(ConfigFile config, PluginInfo bt, Harmony harmony, ManualLogSource log)
		{
			//IL_032d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0334: Expected O, but got Unknown
			//IL_03aa: Unknown result type (might be due to invalid IL or missing references)
			//IL_03b1: Expected O, but got Unknown
			Log = log;
			if (bt.Metadata.GUID != "EosFoxx_DerSpieleentwickler_BeastTribes" || bt.Metadata.Version < new Version(1, 2))
			{
				Log.LogDebug((object)$"TailPhysics: {bt.Metadata.Name} {bt.Metadata.Version} is not BeastTribes 1.2+; module inactive.");
				return;
			}
			Assembly assembly = ((object)bt.Instance).GetType().Assembly;
			_ctrlType = assembly.GetType("BeastRacesMod.BeastRacesMod+BeastRaceController");
			_dynBoneType = assembly.GetType("DynamicBone");
			_colliderType = assembly.GetType("DynamicBoneCollider");
			_bodyItemField = ((_ctrlType == null) ? null : (AccessTools.Field(_ctrlType, "RaceBodyItem") ?? AccessTools.Field(_ctrlType, "BodyItem")));
			_setupParticles = ((_dynBoneType == null) ? null : AccessTools.Method(_dynBoneType, "SetupParticles", (Type[])null, (Type[])null));
			_playerField = ((_ctrlType == null) ? null : (AccessTools.Field(_ctrlType, "beastPlayer") ?? AccessTools.Field(_ctrlType, "BTPlayer")));
			_visEquipField = ((_playerField == null) ? null : AccessTools.Field(_playerField.FieldType, "m_visEquipment"));
			_bodyModelField = ((_visEquipField == null) ? null : AccessTools.Field(_visEquipField.FieldType, "m_bodyModel"));
			MethodInfo methodInfo = ((_ctrlType == null) ? null : AccessTools.Method(_ctrlType, "UpdateRaceWornItem", (Type[])null, (Type[])null));
			MethodInfo methodInfo2 = ((_ctrlType == null) ? null : AccessTools.Method(_ctrlType, "MenuBeastPreview", (Type[])null, (Type[])null));
			_additionalItemField = ((_ctrlType == null) ? null : AccessTools.Field(_ctrlType, "AdditionalRaceBodyItem"));
			_wingAnimatorField = ((_ctrlType == null) ? null : AccessTools.Field(_ctrlType, "BeastAdditionalItemAnimator"));
			_flightEnabledField = ((_ctrlType == null) ? null : AccessTools.Field(_ctrlType, "flightEnabled"));
			MethodInfo methodInfo3 = ((_ctrlType == null) ? null : AccessTools.Method(_ctrlType, "RefreshAdditionalWornItems", (Type[])null, (Type[])null));
			MethodInfo methodInfo4 = ((_ctrlType == null) ? null : AccessTools.Method(_ctrlType, "UpdateStates", (Type[])null, (Type[])null));
			if (_ctrlType == null || _dynBoneType == null || _bodyItemField == null || methodInfo == null)
			{
				Log.LogWarning((object)("TailPhysics: BeastTribes 1.2 internals not found; module inactive. " + $"controller={_ctrlType != null} DynamicBone={_dynBoneType != null} BodyItem={_bodyItemField != null} UpdateRaceWornItem={methodInfo != null}"));
				return;
			}
			BindConfig(config);
			HarmonyMethod val = new HarmonyMethod(typeof(TailPhysics), "OnBodyRefreshed", (Type[])null);
			harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, val, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			if (methodInfo2 != null)
			{
				harmony.Patch((MethodBase)methodInfo2, (HarmonyMethod)null, val, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			}
			bool flag = _additionalItemField != null && _additionalItemField.FieldType == typeof(GameObject) && methodInfo3 != null && methodInfo4 != null;
			if (flag)
			{
				HarmonyMethod val2 = new HarmonyMethod(typeof(TailPhysics), "OnWingsTick", (Type[])null);
				harmony.Patch((MethodBase)methodInfo3, (HarmonyMethod)null, val2, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				harmony.Patch((MethodBase)methodInfo4, (HarmonyMethod)null, val2, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			}
			else
			{
				Log.LogInfo((object)"TailPhysics: AdditionalRaceBodyItem / RefreshAdditionalWornItems / UpdateStates not found; wing chains unavailable.");
			}
			Log.LogInfo((object)(string.Format("TailPhysics: active for BeastTribes {0} (enabled={1}, wings={2}, ", bt.Metadata.Version, Enabled.Value, flag ? WingChains.Value.ToString() : "n/a") + "skeleton via " + ((_bodyModelField != null) ? "VisEquipment.m_bodyModel" : "name heuristic") + "). Toggle 'TailPhysics.Enabled' in the config / Configuration Manager."));
		}

		private static void BindConfig(ConfigFile config)
		{
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Expected O, but got Unknown
			//IL_0079: Unknown result type (might be due to invalid IL or missing references)
			//IL_0083: Expected O, but got Unknown
			//IL_00c5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cf: Expected O, but got Unknown
			//IL_0101: Unknown result type (might be due to invalid IL or missing references)
			//IL_010b: Expected O, but got Unknown
			//IL_013d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0147: Expected O, but got Unknown
			//IL_0189: Unknown result type (might be due to invalid IL or missing references)
			//IL_0193: Expected O, but got Unknown
			//IL_01e3: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ed: Expected O, but got Unknown
			//IL_023d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0247: Expected O, but got Unknown
			//IL_02fd: Unknown result type (might be due to invalid IL or missing references)
			//IL_0307: Expected O, but got Unknown
			//IL_0339: Unknown result type (might be due to invalid IL or missing references)
			//IL_0343: Expected O, but got Unknown
			//IL_0385: Unknown result type (might be due to invalid IL or missing references)
			//IL_038f: Expected O, but got Unknown
			Enabled = config.Bind<bool>("TailPhysics (BeastTribes 1.2 only)", "Enabled", false, new ConfigDescription("Add tail and ear physics (DynamicBone) to BeastTribes 1.2 bodies, which ship without it. Applies immediately.", (AcceptableValueBase)null, new object[1]
			{
				new ConfigurationManagerAttributes
				{
					Order = 100
				}
			}));
			TailRoots = config.Bind<string>("TailPhysics (BeastTribes 1.2 only)", "TailRoots", "auto", new ConfigDescription("'auto' = every bone whose name ends in Tail<number> and whose parent does not (FoxTail1, VixenTail1, Tail0, PantherTail1 ...). Or a comma-separated list of exact bone names.", (AcceptableValueBase)null, new object[1]
			{
				new ConfigurationManagerAttributes
				{
					Order = 90,
					IsAdvanced = true
				}
			}));
			EarRoots = config.Bind<string>("TailPhysics (BeastTribes 1.2 only)", "EarRoots", "auto", new ConfigDescription("'auto' = every bone whose name starts with Ear and whose parent does not (Ear.L, Ear.R). Or a comma-separated list of exact bone names.", (AcceptableValueBase)null, new object[1]
			{
				new ConfigurationManagerAttributes