Decompiled source of AnriServerFixes v1.9.3

AnriDiagnostics.dll

Decompiled a day ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Steamworks;
using UnityEngine;
using UnityEngine.SceneManagement;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: AssemblyCompany("AnriDiagnostics")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.1.1.0")]
[assembly: AssemblyInformationalVersion("1.1.1")]
[assembly: AssemblyProduct("AnriDiagnostics")]
[assembly: AssemblyTitle("AnriDiagnostics")]
[assembly: AssemblyVersion("1.1.1.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 AnriDiagnostics
{
	[BepInPlugin("anri.diagnostics", "AnriDiagnostics", "1.1.3")]
	public class Plugin : BaseUnityPlugin
	{
		[HarmonyPatch(typeof(Terminal), "InitTerminal")]
		private static class RegisterCommands
		{
			[Serializable]
			[CompilerGenerated]
			private sealed class <>c
			{
				public static readonly <>c <>9 = new <>c();

				public static ConsoleEvent <>9__0_0;

				public static ConsoleEvent <>9__0_1;

				public static ConsoleEvent <>9__0_2;

				internal void <Postfix>b__0_0(ConsoleEventArgs args)
				{
					DumpDuplicates((args.Length > 1) ? args[1] : null);
				}

				internal void <Postfix>b__0_1(ConsoleEventArgs args)
				{
					Out(BuildStats(0, 0f, 0f));
				}

				internal void <Postfix>b__0_2(ConsoleEventArgs args)
				{
					DumpPatchOwners();
				}
			}

			private static void Postfix()
			{
				//IL_0033: Unknown result type (might be due to invalid IL or missing references)
				//IL_001e: Unknown result type (might be due to invalid IL or missing references)
				//IL_0023: Unknown result type (might be due to invalid IL or missing references)
				//IL_0029: Expected O, but got Unknown
				//IL_006c: Unknown result type (might be due to invalid IL or missing references)
				//IL_0057: Unknown result type (might be due to invalid IL or missing references)
				//IL_005c: Unknown result type (might be due to invalid IL or missing references)
				//IL_0062: Expected O, but got Unknown
				//IL_00a5: Unknown result type (might be due to invalid IL or missing references)
				//IL_0090: Unknown result type (might be due to invalid IL or missing references)
				//IL_0095: Unknown result type (might be due to invalid IL or missing references)
				//IL_009b: Expected O, but got Unknown
				object obj = <>c.<>9__0_0;
				if (obj == null)
				{
					ConsoleEvent val = delegate(ConsoleEventArgs args)
					{
						DumpDuplicates((args.Length > 1) ? args[1] : null);
					};
					<>c.<>9__0_0 = val;
					obj = (object)val;
				}
				new ConsoleCommand("dupeitems", "List duplicate ObjectDB item entries (optional filter text)", (ConsoleEvent)obj, false, false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false);
				object obj2 = <>c.<>9__0_1;
				if (obj2 == null)
				{
					ConsoleEvent val2 = delegate
					{
						Out(BuildStats(0, 0f, 0f));
					};
					<>c.<>9__0_1 = val2;
					obj2 = (object)val2;
				}
				new ConsoleCommand("serverstats", "Print ZDO count, players and connection quality", (ConsoleEvent)obj2, false, false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false);
				object obj3 = <>c.<>9__0_2;
				if (obj3 == null)
				{
					ConsoleEvent val3 = delegate
					{
						DumpPatchOwners();
					};
					<>c.<>9__0_2 = val3;
					obj3 = (object)val3;
				}
				new ConsoleCommand("patchowners", "List Harmony patch owners on ObjectDB/ZNetScene setup", (ConsoleEvent)obj3, false, false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false);
			}
		}

		public const string GUID = "anri.diagnostics";

		public const string NAME = "AnriDiagnostics";

		public const string VERSION = "1.1.3";

		internal static ManualLogSource Log;

		internal static ConfigEntry<int> StatsInterval;

		private float _frameMax;

		private float _frameSum;

		private int _frames;

		private float _nextStats;

		private static readonly FieldRef<ZSteamSocket, HSteamNetConnection> SteamCon = AccessTools.FieldRefAccess<ZSteamSocket, HSteamNetConnection>("m_con");

		private static readonly Dictionary<Type, FieldInfo> InnerSocketFields = new Dictionary<Type, FieldInfo>();

		private void Awake()
		{
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			Log = ((BaseUnityPlugin)this).Logger;
			StatsInterval = ((BaseUnityPlugin)this).Config.Bind<int>("ServerStats", "IntervalSeconds", 60, "On a dedicated server, log world/network statistics every N seconds (ZDO count, players, per-peer ping and bandwidth, server tick times). 0 disables.");
			new Harmony("anri.diagnostics").PatchAll(typeof(Plugin).Assembly);
			Log.LogInfo((object)"AnriDiagnostics 1.1.3 loaded. Console: dupeitems, patchowners, serverstats");
		}

		private void Update()
		{
			float unscaledDeltaTime = Time.unscaledDeltaTime;
			_frames++;
			_frameSum += unscaledDeltaTime;
			if (unscaledDeltaTime > _frameMax)
			{
				_frameMax = unscaledDeltaTime;
			}
			if (StatsInterval.Value > 0 && !((Object)(object)ZNet.instance == (Object)null) && ZNet.instance.IsServer() && ZNet.instance.IsDedicated() && !(Time.unscaledTime < _nextStats))
			{
				_nextStats = Time.unscaledTime + (float)StatsInterval.Value;
				try
				{
					Log.LogInfo((object)BuildStats(_frames, _frameSum, _frameMax));
				}
				catch (Exception ex)
				{
					Log.LogWarning((object)("stats failed: " + ex.Message));
				}
				_frames = 0;
				_frameSum = 0f;
				_frameMax = 0f;
			}
		}

		internal static string BuildStats(int frames, float frameSum, float frameMax)
		{
			StringBuilder stringBuilder = new StringBuilder();
			ZDOMan instance = ZDOMan.instance;
			ZNet instance2 = ZNet.instance;
			stringBuilder.Append("[Stats] ");
			if (instance != null)
			{
				stringBuilder.Append($"ZDOs={instance.NrOfObjects()} sentZDO/s={instance.GetSentZDOs()} recvZDO/s={instance.GetRecvZDOs()} ");
			}
			if ((Object)(object)instance2 != (Object)null)
			{
				stringBuilder.Append($"players={instance2.GetNrOfPlayers()} ");
				if (frames > 0)
				{
					stringBuilder.Append($"tick avg={frameSum / (float)frames * 1000f:0.0}ms max={frameMax * 1000f:0}ms ");
				}
				foreach (ZNetPeer peer in instance2.GetPeers())
				{
					if (peer == null || peer.m_socket == null)
					{
						continue;
					}
					try
					{
						ConnectionQuality(peer.m_socket, out var lq, out var rq, out var ping, out var outB, out var inB);
						stringBuilder.Append($"| {peer.m_playerName}: ping={ping}ms out={outB / 1024f:0.0}KB/s in={inB / 1024f:0.0}KB/s q={lq:0.00}/{rq:0.00} queue={peer.m_socket.GetSendQueueSize()} ");
					}
					catch (Exception ex)
					{
						string name = ((object)peer.m_socket).GetType().Name;
						string name2 = ex.GetType().Name;
						string text = ex.Message.Replace("\r\n", " ").Replace('\r', ' ').Replace('\n', ' ');
						string text2 = "unknown";
						try
						{
							MethodBase methodBase = new StackTrace(ex).GetFrame(0)?.GetMethod();
							if (methodBase != null)
							{
								text2 = methodBase.DeclaringType?.Name + "." + methodBase.Name;
							}
						}
						catch
						{
						}
						stringBuilder.Append("| " + peer.m_playerName + ": quality n/a (socket=" + name + "; " + name2 + ": " + text + "; at " + text2 + ") ");
					}
				}
			}
			return stringBuilder.ToString();
		}

		private static ZSteamSocket FindSteamSocket(ISocket socket)
		{
			int num = 0;
			while (num < 8 && socket != null)
			{
				ZSteamSocket val = (ZSteamSocket)(object)((socket is ZSteamSocket) ? socket : null);
				if (val != null)
				{
					return val;
				}
				Type type = ((object)socket).GetType();
				if (!InnerSocketFields.TryGetValue(type, out var value))
				{
					Type type2 = type;
					while (type2 != null && value == null)
					{
						foreach (FieldInfo declaredField in AccessTools.GetDeclaredFields(type2))
						{
							if (!declaredField.IsStatic)
							{
								object? value2 = declaredField.GetValue(socket);
								ISocket val2 = (ISocket)((value2 is ISocket) ? value2 : null);
								if (val2 != null && val2 != socket)
								{
									value = declaredField;
									break;
								}
							}
						}
						type2 = type2.BaseType;
					}
					InnerSocketFields[type] = value;
				}
				if (!(value == null))
				{
					object? value3 = value.GetValue(socket);
					ISocket val3 = (ISocket)((value3 is ISocket) ? value3 : null);
					if (val3 != null && val3 != socket)
					{
						socket = val3;
						num++;
						continue;
					}
				}
				return null;
			}
			return null;
		}

		internal static void ConnectionQuality(ISocket socket, out float lq, out float rq, out int ping, out float outB, out float inB)
		{
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: 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_004e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: Invalid comparison between Unknown and I4
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0067: Unknown result type (might be due to invalid IL or missing references)
			//IL_0070: Unknown result type (might be due to invalid IL or missing references)
			//IL_0079: Unknown result type (might be due to invalid IL or missing references)
			ZSteamSocket val = FindSteamSocket(socket);
			if (val != null && (Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsDedicated())
			{
				SteamNetConnectionRealTimeStatus_t val2 = default(SteamNetConnectionRealTimeStatus_t);
				SteamNetConnectionRealTimeLaneStatus_t val3 = default(SteamNetConnectionRealTimeLaneStatus_t);
				if ((int)SteamGameServerNetworkingSockets.GetConnectionRealTimeStatus(SteamCon.Invoke(val), ref val2, 0, ref val3) == 1)
				{
					lq = val2.m_flConnectionQualityLocal;
					rq = val2.m_flConnectionQualityRemote;
					ping = val2.m_nPing;
					outB = val2.m_flOutBytesPerSec;
					inB = val2.m_flInBytesPerSec;
				}
				else
				{
					lq = (rq = 0f);
					ping = 0;
					outB = (inB = 0f);
				}
			}
			else
			{
				socket.GetConnectionQuality(ref lq, ref rq, ref ping, ref outB, ref inB);
			}
		}

		internal static void Out(string s)
		{
			Log.LogInfo((object)s);
			if ((Object)(object)Console.instance != (Object)null)
			{
				Console.instance.Print(s);
			}
		}

		private static void DumpDuplicates(string filter)
		{
			//IL_027a: Unknown result type (might be due to invalid IL or missing references)
			//IL_027f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0293: Unknown result type (might be due to invalid IL or missing references)
			//IL_0298: Unknown result type (might be due to invalid IL or missing references)
			//IL_0312: Unknown result type (might be due to invalid IL or missing references)
			ObjectDB instance = ObjectDB.instance;
			if ((Object)(object)instance == (Object)null)
			{
				Out("ObjectDB not ready.");
				return;
			}
			Dictionary<string, List<GameObject>> dictionary = new Dictionary<string, List<GameObject>>();
			ItemDrop val = default(ItemDrop);
			foreach (GameObject item in instance.m_items)
			{
				if ((Object)(object)item == (Object)null)
				{
					Out("  <null entry in ObjectDB.m_items>");
				}
				else
				{
					if (!item.TryGetComponent<ItemDrop>(ref val) || val.m_itemData?.m_shared == null)
					{
						continue;
					}
					string name = val.m_itemData.m_shared.m_name;
					if (filter == null || name.IndexOf(filter, StringComparison.OrdinalIgnoreCase) >= 0 || ((Object)item).name.IndexOf(filter, StringComparison.OrdinalIgnoreCase) >= 0)
					{
						if (!dictionary.TryGetValue(name, out var value))
						{
							value = (dictionary[name] = new List<GameObject>());
						}
						value.Add(item);
					}
				}
			}
			int num = 0;
			Out($"ObjectDB.m_items count: {instance.m_items.Count}");
			foreach (KeyValuePair<string, List<GameObject>> item2 in from g in dictionary
				where g.Value.Count > 1
				orderby g.Key
				select g)
			{
				num++;
				Out($"== {item2.Key} x{item2.Value.Count}");
				foreach (GameObject item3 in item2.Value)
				{
					SharedData shared = item3.GetComponent<ItemDrop>().m_itemData.m_shared;
					int stableHashCode = StringExtensionMethods.GetStableHashCode(((Object)item3).name);
					Dictionary<int, GameObject> value2 = Traverse.Create((object)instance).Field("m_itemByHash").GetValue<Dictionary<int, GameObject>>();
					GameObject value3;
					bool flag = value2 != null && value2.TryGetValue(stableHashCode, out value3) && (Object)(object)value3 == (Object)(object)item3;
					Dictionary<int, GameObject> dictionary2 = (((Object)(object)ZNetScene.instance != (Object)null) ? Traverse.Create((object)ZNetScene.instance).Field("m_namedPrefabs").GetValue<Dictionary<int, GameObject>>() : null);
					GameObject value4;
					bool flag2 = dictionary2 != null && dictionary2.TryGetValue(stableHashCode, out value4) && (Object)(object)value4 == (Object)(object)item3;
					string text = (((Object)(object)item3.transform.parent != (Object)null) ? ((Object)item3.transform.parent).name : "-");
					Scene scene = item3.scene;
					object obj;
					if (!((Scene)(ref scene)).IsValid())
					{
						obj = "(asset)";
					}
					else
					{
						scene = item3.scene;
						obj = ((Scene)(ref scene)).name;
					}
					string text2 = (string)obj;
					Out($"   prefab='{((Object)item3).name}' id={((Object)item3).GetInstanceID()} set='{shared.m_setName}' sharedId={((object)shared).GetHashCode()} " + $"byHash={flag} znet={flag2} parent={text} scene={text2} hideFlags={((Object)item3).hideFlags} " + "dropPrefab=" + (((Object)(object)item3.GetComponent<ItemDrop>().m_itemData.m_dropPrefab != (Object)null) ? ((Object)item3.GetComponent<ItemDrop>().m_itemData.m_dropPrefab).name : "null"));
				}
			}
			Out($"{num} duplicated display name(s).");
		}

		private static void DumpPatchOwners()
		{
			(Type, string)[] array = new(Type, string)[9]
			{
				(typeof(ObjectDB), "Awake"),
				(typeof(ObjectDB), "CopyOtherDB"),
				(typeof(ObjectDB), "UpdateRegisters"),
				(typeof(ZNetScene), "Awake"),
				(typeof(FejdStartup), "Awake"),
				(typeof(FejdStartup), "SetupObjectDB"),
				(typeof(Game), "Start"),
				(typeof(ItemDrop), "Awake"),
				(typeof(ItemDrop), "Start")
			};
			for (int i = 0; i < array.Length; i++)
			{
				(Type, string) tuple = array[i];
				Type item = tuple.Item1;
				string item2 = tuple.Item2;
				MethodInfo methodInfo = AccessTools.Method(item, item2, (Type[])null, (Type[])null);
				if (methodInfo == null)
				{
					Out(item.Name + "." + item2 + ": not found");
					continue;
				}
				Patches patchInfo = Harmony.GetPatchInfo((MethodBase)methodInfo);
				if (patchInfo == null)
				{
					Out(item.Name + "." + item2 + ": no patches");
					continue;
				}
				Out(item.Name + "." + item2 + ": prefix[" + Fmt(patchInfo.Prefixes) + "] postfix[" + Fmt(patchInfo.Postfixes) + "] transpiler[" + Fmt(patchInfo.Transpilers) + "]");
			}
			static string Fmt(IEnumerable<Patch> ps)
			{
				return string.Join(", ", ps.Select((Patch p) => p.owner + ":" + p.PatchMethod.DeclaringType?.Name + "." + p.PatchMethod.Name));
			}
		}
	}
}

BossKillScaling.dll

Decompiled a day ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
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: AssemblyCompany("BossKillScaling")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("BossKillScaling")]
[assembly: AssemblyTitle("BossKillScaling")]
[assembly: AssemblyVersion("1.0.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace BossKillScaling
{
	[BepInPlugin("anri.bosskillscaling", "BossKillScaling", "1.5.0")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public class Plugin : BaseUnityPlugin
	{
		[Serializable]
		[CompilerGenerated]
		private sealed class <>c
		{
			public static readonly <>c <>9 = new <>c();

			public static Func<KeyValuePair<string, int>, bool> <>9__33_0;

			public static Func<string, string> <>9__42_0;

			public static Func<string, string> <>9__42_1;

			public static Func<string, string> <>9__44_0;

			public static Func<string, string> <>9__44_1;

			public static Func<string, bool> <>9__45_1;

			public static Func<string, string> <>9__45_2;

			public static ConsoleEvent <>9__45_0;

			internal bool <ProgressCap>b__33_0(KeyValuePair<string, int> e)
			{
				return e.Key.Equals("none", StringComparison.OrdinalIgnoreCase);
			}

			internal string <BeforeGenerateDropList>b__42_0(string e)
			{
				return e.Trim();
			}

			internal string <BeforeGenerateDropList>b__42_1(string e)
			{
				return e.Trim();
			}

			internal string <AfterGenerateDropList>b__44_0(string e)
			{
				return e.Trim();
			}

			internal string <AfterGenerateDropList>b__44_1(string e)
			{
				return e.Trim();
			}

			internal void <RegisterCommand>b__45_0(ConsoleEventArgs args)
			{
				double result;
				int result2;
				if ((Object)(object)ZoneSystem.instance == (Object)null)
				{
					args.Context.AddString("No world loaded.");
				}
				else if (args.Args.Length == 1)
				{
					foreach (string item in from k in ZoneSystem.instance.GetGlobalKeys()
						where k.StartsWith("anri_bosskills_", StringComparison.OrdinalIgnoreCase)
						orderby k
						select k)
					{
						args.Context.AddString(item);
					}
					string key;
					int num = ProgressCap(out key);
					args.Context.AddString($"Progress cap: {num} stars ({key}).");
				}
				else if (args.Args.Length == 4 && args.Args[1].Equals("preview", StringComparison.OrdinalIgnoreCase) && double.TryParse(args.Args[3], NumberStyles.Float, CultureInfo.InvariantCulture, out result) && result >= 0.0 && !double.IsInfinity(result))
				{
					args.Context.AddString($"{Prefab(args.Args[2])}: kills={Count(args.Args[2])}, distance={result.ToString(CultureInfo.InvariantCulture)}m, next spawn={Stars(args.Args[2], result)} stars.");
				}
				else if (args.Args.Length == 3 && int.TryParse(args.Args[2], NumberStyles.Integer, CultureInfo.InvariantCulture, out result2) && result2 >= 0)
				{
					ZoneSystem.instance.SetGlobalKey("anri_bosskills_" + Prefab(args.Args[1]) + " " + result2.ToString(CultureInfo.InvariantCulture));
					args.Context.AddString($"Requested {Prefab(args.Args[1])} counter = {result2}.");
				}
				else
				{
					args.Context.AddString("Usage: bosskills | bosskills <prefab> <n> | bosskills preview <prefab> <distance>");
				}
			}

			internal bool <RegisterCommand>b__45_1(string k)
			{
				return k.StartsWith("anri_bosskills_", StringComparison.OrdinalIgnoreCase);
			}

			internal string <RegisterCommand>b__45_2(string k)
			{
				return k;
			}
		}

		public const string GUID = "anri.bosskillscaling";

		public const string NAME = "BossKillScaling";

		public const string VERSION = "1.5.0";

		private const string SLS_GUID = "MidnightsFX.StarLevelSystem";

		private const string CounterPrefix = "anri_bosskills_";

		private const string DefaultRings = "0:0,750:1,1200:2,2000:3,3000:4,4000:5,5000:6,6000:7,7000:8,8000:9,9100:10";

		private const string DefaultProgress = "none:1,defeated_eikthyr:2,defeated_gdking:4,defeated_bonemass:5,defeated_dragon:6,defeated_goblinking:8,defeated_queen:9,defeated_fader:10,defeated_frozenking_p3:20";

		internal static ManualLogSource Log;

		private static readonly Dictionary<int, string> BossPrefabs = new Dictionary<int, string>();

		private static bool ZdoDestroyWarningLogged;

		private static bool DropChanceWarningLogged;

		private static readonly FieldRef<CharacterDrop, Character> DropCharacter = AccessTools.FieldRefAccess<CharacterDrop, Character>("m_character");

		private static ConfigEntry<bool> CapBossEquipment;

		private static ConfigEntry<int> BossEquipmentMaxCount;

		private static ConfigEntry<float> BossEquipmentChanceMultiplier;

		private static ConfigEntry<string> BossEquipmentExtraPrefabs;

		private static ConfigEntry<string> BossEquipmentIgnorePrefabs;

		private static ConfigEntry<bool> Enabled;

		private static ConfigEntry<string> LadderMode;

		private static ConfigEntry<bool> CountCappedKills;

		private static ConfigEntry<bool> ApplyProgressCapToAllCreatures;

		private static ConfigEntry<int> MaxStars;

		private static ConfigEntry<bool> Verbose;

		private static volatile SortedDictionary<double, int> Rings = new SortedDictionary<double, int>();

		private static volatile List<KeyValuePair<string, int>> Progress = new List<KeyValuePair<string, int>>();

		private void Awake()
		{
			//IL_02c4: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ca: Expected O, but got Unknown
			//IL_04b3: Unknown result type (might be due to invalid IL or missing references)
			//IL_04c8: Unknown result type (might be due to invalid IL or missing references)
			//IL_04d5: Expected O, but got Unknown
			//IL_04d5: Expected O, but got Unknown
			//IL_0505: Unknown result type (might be due to invalid IL or missing references)
			//IL_0512: Expected O, but got Unknown
			//IL_053a: Unknown result type (might be due to invalid IL or missing references)
			//IL_054f: Unknown result type (might be due to invalid IL or missing references)
			//IL_055c: Expected O, but got Unknown
			//IL_055c: Expected O, but got Unknown
			//IL_0585: Unknown result type (might be due to invalid IL or missing references)
			//IL_058a: Unknown result type (might be due to invalid IL or missing references)
			//IL_059d: Expected O, but got Unknown
			//IL_05c5: Unknown result type (might be due to invalid IL or missing references)
			//IL_05d3: Expected O, but got Unknown
			//IL_05fc: Unknown result type (might be due to invalid IL or missing references)
			//IL_0609: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			Enabled = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enabled", true, AdminOnly("Master switch for the deterministic boss star ladder and its caps. Off = StarLevelSystem's own rolls."));
			LadderMode = ((BaseUnityPlugin)this).Config.Bind<string>("General", "LadderMode", "Sqrt", AdminOnly("Sqrt = star n costs ceil(sqrt(n)) kills, cumulative: 1,3,5,7,10,13,16,19,22,26 total kills for stars 1-10. Blocks = the original ladder in blocks of nine kills (0,1,1,2,2,2,2,2,3 then +3 per block).", (AcceptableValueBase)(object)new AcceptableValueList<string>(new string[2] { "Sqrt", "Blocks" })));
			CountCappedKills = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "CountCappedKills", true, AdminOnly("Every boss kill advances the counter, even when a distance or progress cap lowered that fight's stars. Off = only fights that spawned at the full ladder value count."));
			ApplyProgressCapToAllCreatures = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "ApplyProgressCapToAllCreatures", true, AdminOnly("Also clip every creature's star level to the boss-progress cap through StarLevelSystem's GetMaxCreatureLevel. Off = bosses only."));
			ConfigEntry<string> rings = ((BaseUnityPlugin)this).Config.Bind<string>("General", "RingMaxStars", "0:0,750:1,1200:2,2000:3,3000:4,4000:5,5000:6,6000:7,7000:8,8000:9,9100:10", AdminOnly("Distance in metres:max stars pairs. Reparsed when changed."));
			ConfigEntry<string> progress = ((BaseUnityPlugin)this).Config.Bind<string>("General", "ProgressCaps", "none:1,defeated_eikthyr:2,defeated_gdking:4,defeated_bonemass:5,defeated_dragon:6,defeated_goblinking:8,defeated_queen:9,defeated_fader:10,defeated_frozenking_p3:20", AdminOnly("Ordered global key:max stars ladder; last present key wins. Reparsed when changed."));
			MaxStars = ((BaseUnityPlugin)this).Config.Bind<int>("General", "MaxStars", 10, AdminOnly("Absolute boss star ceiling.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 2147483646)));
			Verbose = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Verbose", false, "Log boss level selection, counted/skipped boss deaths, capped boss equipment drops, and equipment drop chance adjustments.");
			CapBossEquipment = ((BaseUnityPlugin)this).Config.Bind<bool>("BossDrops", "CapBossEquipment", true, AdminOnly("Cap equipment drops per boss kill."));
			BossEquipmentMaxCount = ((BaseUnityPlugin)this).Config.Bind<int>("BossDrops", "BossEquipmentMaxCount", 1, AdminOnly("Maximum total copies of each equipment prefab per boss kill.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, int.MaxValue)));
			BossEquipmentChanceMultiplier = ((BaseUnityPlugin)this).Config.Bind<float>("BossDrops", "BossEquipmentChanceMultiplier", 2f, AdminOnly("Multiplies the base drop chance of equipment drops from bosses before the vanilla star multiplier. 1 = unchanged.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 100f)));
			BossEquipmentExtraPrefabs = ((BaseUnityPlugin)this).Config.Bind<string>("BossDrops", "BossEquipmentExtraPrefabs", "", AdminOnly("Comma-separated prefab names to cap regardless of item type."));
			BossEquipmentIgnorePrefabs = ((BaseUnityPlugin)this).Config.Bind<string>("BossDrops", "BossEquipmentIgnorePrefabs", "", AdminOnly("Comma-separated prefab names never capped; overrides extra prefabs."));
			rings.SettingChanged += delegate
			{
				ParseRings(rings.Value);
			};
			progress.SettingChanged += delegate
			{
				ParseProgress(progress.Value);
			};
			ParseRings(rings.Value);
			ParseProgress(progress.Value);
			if (!Chainloader.PluginInfos.ContainsKey("MidnightsFX.StarLevelSystem"))
			{
				Log.LogInfo((object)"BossKillScaling idle: StarLevelSystem not loaded.");
				return;
			}
			Harmony val = new Harmony("anri.bosskillscaling");
			Assembly slsAssembly = ((object)Chainloader.PluginInfos["MidnightsFX.StarLevelSystem"].Instance).GetType().Assembly;
			Type type = FindType("LevelSelection");
			Type type2 = FindType("DataObjects");
			Type type3 = type2?.GetNestedType("CreatureSpecificSetting");
			Type type4 = type2?.GetNestedType("BiomeSpecificSetting");
			if (type == null || type3 == null || type4 == null)
			{
				Log.LogWarning((object)$"SLS types not found (LevelSelection={type != null}, DataObjects={type2 != null}, Creature={type3 != null}, Biome={type4 != null}).");
			}
			MethodInfo methodInfo = ((type == null || type3 == null || type4 == null) ? null : AccessTools.Method(type, "DetermineLevel", new Type[7]
			{
				typeof(Character),
				typeof(ZDO),
				type3,
				type4,
				typeof(Biome),
				typeof(int),
				typeof(bool)
			}, (Type[])null));
			MethodInfo methodInfo2 = ((type == null || type3 == null || type4 == null) ? null : AccessTools.Method(type, "GetMaxCreatureLevel", new Type[5]
			{
				typeof(Character),
				type3,
				type4,
				typeof(Biome),
				typeof(bool?)
			}, (Type[])null));
			if (methodInfo == null)
			{
				Log.LogWarning((object)"SLS LevelSelection.DetermineLevel signature missing; boss override disabled.");
			}
			else
			{
				val.Patch((MethodBase)methodInfo, new HarmonyMethod(typeof(Plugin), "BeforeDetermine", (Type[])null), new HarmonyMethod(typeof(Plugin), "AfterDetermine", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			}
			if (methodInfo2 == null)
			{
				Log.LogWarning((object)"SLS LevelSelection.GetMaxCreatureLevel signature missing; progress cap disabled.");
			}
			else
			{
				val.Patch((MethodBase)methodInfo2, (HarmonyMethod)null, new HarmonyMethod(typeof(Plugin), "AfterMaximum", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			}
			val.Patch((MethodBase)AccessTools.Method(typeof(CharacterDrop), "GenerateDropList", (Type[])null, (Type[])null), new HarmonyMethod(typeof(Plugin), "BeforeGenerateDropList", (Type[])null), new HarmonyMethod(typeof(Plugin), "RestoreDropChances", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			val.Patch((MethodBase)AccessTools.Method(typeof(CharacterDrop), "GenerateDropList", (Type[])null, (Type[])null), (HarmonyMethod)null, new HarmonyMethod(typeof(Plugin), "AfterGenerateDropList", (Type[])null)
			{
				priority = 200
			}, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			val.Patch((MethodBase)AccessTools.Method(typeof(ZDOMan), "HandleDestroyedZDO", (Type[])null, (Type[])null), new HarmonyMethod(typeof(Plugin), "BeforeZdoDestroyed", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			val.Patch((MethodBase)AccessTools.Method(typeof(Terminal), "InitTerminal", (Type[])null, (Type[])null), (HarmonyMethod)null, new HarmonyMethod(typeof(Plugin), "RegisterCommand", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			Log.LogInfo((object)string.Format("{0} {1} active with StarLevelSystem {2}.", "BossKillScaling", "1.5.0", Chainloader.PluginInfos["MidnightsFX.StarLevelSystem"].Metadata.Version));
			Type FindType(string name)
			{
				try
				{
					return slsAssembly.GetTypes().FirstOrDefault((Type t) => t.Name == name);
				}
				catch (ReflectionTypeLoadException ex)
				{
					return ex.Types.FirstOrDefault((Type t) => t != null && t.Name == name);
				}
			}
		}

		private static ConfigDescription AdminOnly(string description, AcceptableValueBase range = null)
		{
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Expected O, but got Unknown
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Expected O, but got Unknown
			return new ConfigDescription(description, range, new object[1] { (object)new ConfigurationManagerAttributes
			{
				IsAdminOnly = true
			} });
		}

		private static void ParseRings(string text)
		{
			SortedDictionary<double, int> sortedDictionary = new SortedDictionary<double, int>();
			string[] array = text.Split(new char[1] { ',' });
			foreach (string text2 in array)
			{
				string[] array2 = text2.Split(new char[1] { ':' });
				if (array2.Length == 2 && double.TryParse(array2[0], NumberStyles.Float, CultureInfo.InvariantCulture, out var result) && !double.IsNaN(result) && !double.IsInfinity(result) && result >= 0.0 && TryCap(array2[1], out var cap))
				{
					sortedDictionary[result] = cap;
				}
				else
				{
					Log.LogWarning((object)("Ignoring invalid RingMaxStars entry '" + text2 + "'."));
				}
			}
			if (!sortedDictionary.ContainsKey(0.0))
			{
				sortedDictionary[0.0] = 0;
			}
			Rings = sortedDictionary;
		}

		private static void ParseProgress(string text)
		{
			List<KeyValuePair<string, int>> list = new List<KeyValuePair<string, int>>();
			string[] array = text.Split(new char[1] { ',' });
			foreach (string text2 in array)
			{
				string[] array2 = text2.Split(new char[1] { ':' });
				if (array2.Length == 2 && !string.IsNullOrWhiteSpace(array2[0]) && TryCap(array2[1], out var cap))
				{
					list.Add(new KeyValuePair<string, int>(array2[0].Trim(), cap));
				}
				else
				{
					Log.LogWarning((object)("Ignoring invalid ProgressCaps entry '" + text2 + "'."));
				}
			}
			Progress = list;
		}

		private static bool TryCap(string text, out int cap)
		{
			if (int.TryParse(text, NumberStyles.Integer, CultureInfo.InvariantCulture, out cap) && cap >= 0)
			{
				return cap < int.MaxValue;
			}
			return false;
		}

		private static string Prefab(string name)
		{
			return name.Replace("(Clone)", "").Trim();
		}

		private static int Count(string prefab)
		{
			string s = default(string);
			if ((Object)(object)ZoneSystem.instance != (Object)null && ZoneSystem.instance.GetGlobalKey("anri_bosskills_" + Prefab(prefab), ref s) && int.TryParse(s, NumberStyles.Integer, CultureInfo.InvariantCulture, out var result))
			{
				return Math.Max(0, result);
			}
			return 0;
		}

		private static int Formula(int k)
		{
			if (LadderMode.Value == "Blocks")
			{
				int num = k % 9 + 1;
				return 3 * (k / 9) + ((num != 1) ? ((int)Math.Floor(Math.Sqrt(num))) : 0);
			}
			int num2 = 0;
			int result = 0;
			for (int i = 1; i <= 1000; i++)
			{
				num2 += (int)Math.Ceiling(Math.Sqrt(i));
				if (num2 > k)
				{
					break;
				}
				result = i;
			}
			return result;
		}

		private static int ProgressCap(out string key)
		{
			int result = 1;
			key = "none";
			List<KeyValuePair<string, int>> progress = Progress;
			foreach (KeyValuePair<string, int> item in progress.Where((KeyValuePair<string, int> e) => e.Key.Equals("none", StringComparison.OrdinalIgnoreCase)))
			{
				result = item.Value;
			}
			foreach (KeyValuePair<string, int> item2 in progress)
			{
				if (!item2.Key.Equals("none", StringComparison.OrdinalIgnoreCase) && (Object)(object)ZoneSystem.instance != (Object)null && ZoneSystem.instance.GetGlobalKey(item2.Key))
				{
					result = item2.Value;
					key = item2.Key;
				}
			}
			return result;
		}

		private static int RingCap(double distance)
		{
			int result = 0;
			foreach (KeyValuePair<double, int> ring in Rings)
			{
				if (ring.Key > distance)
				{
					break;
				}
				result = ring.Value;
			}
			return result;
		}

		private static int Stars(string prefab, double distance)
		{
			string key;
			return Math.Min(Formula(Count(prefab)), Math.Min(RingCap(distance), Math.Min(ProgressCap(out key), MaxStars.Value)));
		}

		private static void BeforeDetermine(ZDO __1, out bool __state)
		{
			__state = __1 != null && __1.GetInt(ZDOVars.s_level, 0) > 0;
		}

		private static void AfterDetermine(Character __0, ZDO __1, int __5, bool __6, bool __state, ref int __result)
		{
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_0060: Unknown result type (might be due to invalid IL or missing references)
			//IL_0068: Unknown result type (might be due to invalid IL or missing references)
			//IL_006f: Unknown result type (might be due to invalid IL or missing references)
			if (!(!Enabled.Value || (Object)(object)__0 == (Object)null || __1 == null || __state) && __0.IsBoss() && !__0.IsTamed() && __6 && __5 == 0)
			{
				Vector3 position = ((Component)__0).transform.position;
				string text = Prefab(((Object)((Component)__0).gameObject).name);
				__result = 1 + Stars(text, Math.Sqrt((double)position.x * (double)position.x + (double)position.z * (double)position.z));
				if (Verbose.Value)
				{
					Log.LogInfo((object)$"{text}: kills={Count(text)}, next spawn stars={__result - 1}.");
				}
			}
		}

		private static void AfterMaximum(ref int __result)
		{
			if (Enabled.Value && ApplyProgressCapToAllCreatures.Value)
			{
				__result = Math.Min(__result, ProgressCap(out var _) + 1);
			}
		}

		private static void BeforeZdoDestroyed(ZDOID uid)
		{
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				if ((Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer() || (Object)(object)ZoneSystem.instance == (Object)null || (Object)(object)ZNetScene.instance == (Object)null || ZDOMan.instance == null)
				{
					return;
				}
				ZDO zDO = ZDOMan.instance.GetZDO(uid);
				if (zDO == null)
				{
					return;
				}
				int prefab = zDO.GetPrefab();
				if (!BossPrefabs.TryGetValue(prefab, out var value))
				{
					GameObject prefab2 = ZNetScene.instance.GetPrefab(prefab);
					Character val = (((Object)(object)prefab2 == (Object)null) ? null : prefab2.GetComponent<Character>());
					value = (((Object)(object)val != (Object)null && val.m_boss) ? Prefab(((Object)prefab2).name) : null);
					BossPrefabs[prefab] = value;
				}
				if (value != null && !zDO.GetBool(ZDOVars.s_tamed, false))
				{
					int num = Math.Max(0, zDO.GetInt(ZDOVars.s_level, 1) - 1);
					int num2 = Count(value);
					int num3 = Formula(num2);
					bool flag = (CountCappedKills.Value || num >= num3) && num2 < int.MaxValue;
					if (flag)
					{
						ZoneSystem.instance.SetGlobalKey("anri_bosskills_" + value + " " + (num2 + 1).ToString(CultureInfo.InvariantCulture));
					}
					if (Verbose.Value)
					{
						Log.LogInfo((object)($"{value} destroyed on server: stars={num}, uncapped requirement={num3}, counter={num2}; " + (flag ? (((num < num3) ? "counted (capped fight); " : "counted; ") + $"increment requested to {num2 + 1}.") : "skipped (clipped or counter full).")));
					}
				}
			}
			catch (Exception ex)
			{
				if (!ZdoDestroyWarningLogged)
				{
					ZdoDestroyWarningLogged = true;
					Log.LogWarning((object)("Server boss kill counting failed: " + ex.Message));
				}
			}
		}

		private static bool Equipment(ItemType type)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: Expected I4, but got Unknown
			switch (type - 3)
			{
			case 0:
			case 1:
			case 2:
			case 3:
			case 4:
			case 8:
			case 11:
			case 12:
			case 14:
			case 15:
			case 16:
			case 17:
			case 19:
				return true;
			default:
				return false;
			}
		}

		private static bool CappedPrefab(GameObject prefab, HashSet<string> extra, HashSet<string> ignore)
		{
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			ItemDrop val = (((Object)(object)prefab == (Object)null) ? null : prefab.GetComponent<ItemDrop>());
			string item = (((Object)(object)prefab == (Object)null) ? "" : Prefab(((Object)prefab).name));
			if ((Object)(object)val != (Object)null && !ignore.Contains(item))
			{
				if (!extra.Contains(item))
				{
					return Equipment(val.m_itemData.m_shared.m_itemType);
				}
				return true;
			}
			return false;
		}

		private static void BeforeGenerateDropList(CharacterDrop __instance, out List<KeyValuePair<Drop, float>> __state)
		{
			__state = null;
			try
			{
				float value = BossEquipmentChanceMultiplier.Value;
				if (!CapBossEquipment.Value || value == 1f)
				{
					return;
				}
				Character val = DropCharacter.Invoke(__instance);
				if ((Object)(object)val == (Object)null || !val.IsBoss() || __instance.m_drops == null)
				{
					return;
				}
				HashSet<string> extra = new HashSet<string>(from e in BossEquipmentExtraPrefabs.Value.Split(new char[1] { ',' })
					select e.Trim(), StringComparer.Ordinal);
				HashSet<string> ignore = new HashSet<string>(from e in BossEquipmentIgnorePrefabs.Value.Split(new char[1] { ',' })
					select e.Trim(), StringComparer.Ordinal);
				__state = new List<KeyValuePair<Drop, float>>();
				foreach (Drop drop in __instance.m_drops)
				{
					if (CappedPrefab(drop.m_prefab, extra, ignore))
					{
						__state.Add(new KeyValuePair<Drop, float>(drop, drop.m_chance));
					}
				}
				foreach (KeyValuePair<Drop, float> item in __state)
				{
					item.Key.m_chance = Mathf.Clamp01(item.Value * value);
				}
				if (!Verbose.Value)
				{
					return;
				}
				foreach (KeyValuePair<Drop, float> item2 in __state)
				{
					Log.LogInfo((object)$"{Prefab(((Object)((Component)val).gameObject).name)} equipment drop chance {Prefab(((Object)item2.Key.m_prefab).name)}: {item2.Value} -> {item2.Key.m_chance}.");
				}
			}
			catch (Exception ex)
			{
				RestoreDropChances(__state);
				__state = null;
				if (!DropChanceWarningLogged)
				{
					DropChanceWarningLogged = true;
					Log.LogWarning((object)("Boss equipment drop chance adjustment failed: " + ex.Message));
				}
			}
		}

		private static void RestoreDropChances(List<KeyValuePair<Drop, float>> __state)
		{
			if (__state == null)
			{
				return;
			}
			foreach (KeyValuePair<Drop, float> item in __state)
			{
				item.Key.m_chance = item.Value;
			}
		}

		private static void AfterGenerateDropList(CharacterDrop __instance, List<KeyValuePair<GameObject, int>> __result)
		{
			Character val = DropCharacter.Invoke(__instance);
			if (!CapBossEquipment.Value || (Object)(object)val == (Object)null || !val.IsBoss() || __result == null)
			{
				return;
			}
			HashSet<string> extra = new HashSet<string>(from e in BossEquipmentExtraPrefabs.Value.Split(new char[1] { ',' })
				select e.Trim(), StringComparer.Ordinal);
			HashSet<string> ignore = new HashSet<string>(from e in BossEquipmentIgnorePrefabs.Value.Split(new char[1] { ',' })
				select e.Trim(), StringComparer.Ordinal);
			Dictionary<string, int> dictionary = new Dictionary<string, int>(StringComparer.Ordinal);
			Dictionary<string, long> dictionary2 = new Dictionary<string, long>(StringComparer.Ordinal);
			List<KeyValuePair<GameObject, int>> list = new List<KeyValuePair<GameObject, int>>();
			int num = Math.Max(0, BossEquipmentMaxCount.Value);
			foreach (KeyValuePair<GameObject, int> item in __result)
			{
				string key = (((Object)(object)item.Key == (Object)null) ? "" : Prefab(((Object)item.Key).name));
				if (!CappedPrefab(item.Key, extra, ignore))
				{
					list.Add(item);
					continue;
				}
				if (!dictionary.TryGetValue(key, out var value))
				{
					value = (dictionary[key] = list.Count);
					dictionary2[key] = 0L;
					list.Add(new KeyValuePair<GameObject, int>(item.Key, 0));
				}
				dictionary2[key] += item.Value;
				list[value] = new KeyValuePair<GameObject, int>(list[value].Key, (int)Math.Min(num, dictionary2[key]));
			}
			__result.Clear();
			__result.AddRange(list);
			if (!Verbose.Value)
			{
				return;
			}
			foreach (KeyValuePair<string, long> item2 in dictionary2)
			{
				if (item2.Value > num)
				{
					Log.LogInfo((object)$"{Prefab(((Object)((Component)val).gameObject).name)} equipment drop {item2.Key}: {item2.Value} -> {num}.");
				}
			}
		}

		private static void RegisterCommand()
		{
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Expected O, but got Unknown
			object obj = <>c.<>9__45_0;
			if (obj == null)
			{
				ConsoleEvent val = delegate(ConsoleEventArgs args)
				{
					double result;
					int result2;
					if ((Object)(object)ZoneSystem.instance == (Object)null)
					{
						args.Context.AddString("No world loaded.");
					}
					else if (args.Args.Length == 1)
					{
						foreach (string item in from k in ZoneSystem.instance.GetGlobalKeys()
							where k.StartsWith("anri_bosskills_", StringComparison.OrdinalIgnoreCase)
							orderby k
							select k)
						{
							args.Context.AddString(item);
						}
						string key;
						int num = ProgressCap(out key);
						args.Context.AddString($"Progress cap: {num} stars ({key}).");
					}
					else if (args.Args.Length == 4 && args.Args[1].Equals("preview", StringComparison.OrdinalIgnoreCase) && double.TryParse(args.Args[3], NumberStyles.Float, CultureInfo.InvariantCulture, out result) && result >= 0.0 && !double.IsInfinity(result))
					{
						args.Context.AddString($"{Prefab(args.Args[2])}: kills={Count(args.Args[2])}, distance={result.ToString(CultureInfo.InvariantCulture)}m, next spawn={Stars(args.Args[2], result)} stars.");
					}
					else if (args.Args.Length == 3 && int.TryParse(args.Args[2], NumberStyles.Integer, CultureInfo.InvariantCulture, out result2) && result2 >= 0)
					{
						ZoneSystem.instance.SetGlobalKey("anri_bosskills_" + Prefab(args.Args[1]) + " " + result2.ToString(CultureInfo.InvariantCulture));
						args.Context.AddString($"Requested {Prefab(args.Args[1])} counter = {result2}.");
					}
					else
					{
						args.Context.AddString("Usage: bosskills | bosskills <prefab> <n> | bosskills preview <prefab> <distance>");
					}
				};
				<>c.<>9__45_0 = val;
				obj = (object)val;
			}
			new ConsoleCommand("bosskills", "bosskills [<prefab> <n> | preview <prefab> <distance>]", (ConsoleEvent)obj, false, false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false);
		}
	}
}

ChestButlerPullFix.dll

Decompiled a day ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
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: AssemblyCompany("ChestButlerPullFix")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("ChestButlerPullFix")]
[assembly: AssemblyTitle("ChestButlerPullFix")]
[assembly: AssemblyVersion("1.0.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace ChestButlerPullFix
{
	[BepInPlugin("anri.chestbutlerpullfix", "ChestButlerPullFix", "1.0.0")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public class Plugin : BaseUnityPlugin
	{
		public const string GUID = "anri.chestbutlerpullfix";

		public const string NAME = "ChestButlerPullFix";

		public const string VERSION = "1.0.0";

		private static ConfigEntry<bool> Enabled;

		private static ConfigEntry<bool> Verbose;

		private static ManualLogSource Log;

		private static bool _loggedException;

		private void Awake()
		{
			//IL_00be: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c4: Expected O, but got Unknown
			//IL_00d4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00da: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			Enabled = ((BaseUnityPlugin)this).Config.Bind<bool>("PullFix", "Enabled", true, "Claim ownership of the destination chest before a ChestButler pull so the pulled items are saved; prevents item loss when another player owned the chest.");
			Verbose = ((BaseUnityPlugin)this).Config.Bind<bool>("PullFix", "Verbose", false, "Log each destination chest ownership claim.");
			if (!Chainloader.PluginInfos.ContainsKey("eksolutions.chestbutler"))
			{
				Log.LogInfo((object)"ChestButlerPullFix: ChestButler is not loaded; skipping pull fixes.");
				return;
			}
			Assembly assembly = ((object)Chainloader.PluginInfos["eksolutions.chestbutler"].Instance).GetType().Assembly;
			MethodInfo methodInfo = AccessTools.Method(assembly.GetType("ChestButler.Core.PullerStorage"), "Pull", (Type[])null, (Type[])null);
			MethodInfo methodInfo2 = AccessTools.Method(assembly.GetType("ChestButler.Core.Puller"), "PullInto", (Type[])null, (Type[])null);
			Harmony val = new Harmony("anri.chestbutlerpullfix");
			HarmonyMethod val2 = new HarmonyMethod(typeof(Plugin), "ClaimDest", (Type[])null);
			List<string> list = new List<string>();
			if (methodInfo == null)
			{
				Log.LogWarning((object)"ChestButler.Core.PullerStorage.Pull not found; skipping patch.");
			}
			else
			{
				val.Patch((MethodBase)methodInfo, val2, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				list.Add("ChestButler.Core.PullerStorage.Pull");
			}
			if (methodInfo2 == null)
			{
				Log.LogWarning((object)"ChestButler.Core.Puller.PullInto not found; skipping patch.");
			}
			else
			{
				val.Patch((MethodBase)methodInfo2, val2, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				list.Add("ChestButler.Core.Puller.PullInto");
			}
			Log.LogInfo((object)("ChestButlerPullFix 1.0.0 loaded. Patched: " + ((list.Count == 0) ? "none" : string.Join(", ", list)) + "."));
		}

		private static void ClaimDest(Container dest)
		{
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Expected O, but got Unknown
			try
			{
				if (!Enabled.Value || (Object)(object)dest == (Object)null)
				{
					return;
				}
				ZNetView val = (ZNetView)AccessTools.Field(typeof(Container), "m_nview").GetValue(dest);
				if (!((Object)(object)val == (Object)null) && val.IsValid() && !val.IsOwner())
				{
					val.ClaimOwnership();
					if (Verbose.Value)
					{
						Log.LogInfo((object)"Claimed destination chest ownership before ChestButler pull.");
					}
				}
			}
			catch (Exception arg)
			{
				if (!_loggedException)
				{
					_loggedException = true;
					Log.LogWarning((object)$"ChestButler destination ownership claim failed: {arg}");
				}
			}
		}
	}
}

EnchantTableChestFix.dll

Decompiled a day ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using EpicLoot_UnityLib;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using TMPro;
using UnityEngine;
using UnityEngine.UI;

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

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace EnchantTableChestFix
{
	[BepInPlugin("anri.enchanttablechestfix", "EnchantTableChestFix", "1.4.0")]
	[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 = "anri.enchanttablechestfix";

		public const string NAME = "EnchantTableChestFix";

		public const string VERSION = "1.4.0";

		private const string AZU_GUID = "Azumatt.AzuCraftyBoxes";

		private const string MUC_GUID = "com.maxsch.valheim.MultiUserChest";

		private const string EPICLOOT_GUID = "randyknapp.mods.epicloot";

		internal static ManualLogSource Log;

		internal static ConfigEntry<float> FallbackRange;

		internal static ConfigEntry<bool> Verbose;

		internal static ConfigEntry<bool> MatchChest;

		internal static ConfigEntry<string> SourcePrefab;

		internal static ConfigEntry<bool> CopyHealth;

		internal static ConfigEntry<bool> FixBaseLevelRefund;

		internal static ConfigEntry<bool> CostBelowIcon;

		internal static ConfigEntry<bool> CostOutline;

		internal static ConfigEntry<float> CostFontScale;

		private static bool CostLabelsWarned;

		private static bool CostClippingLogged;

		private static bool DurabilityWarned;

		private static bool RefundWarned;

		[ThreadStatic]
		private static bool _inTableRefund;

		private static readonly FieldRef<Container, ZNetView> ContainerNView = AccessTools.FieldRefAccess<Container, ZNetView>("m_nview");

		private void Awake()
		{
			//IL_0141: Unknown result type (might be due to invalid IL or missing references)
			//IL_014b: Expected O, but got Unknown
			//IL_01b7: Unknown result type (might be due to invalid IL or missing references)
			//IL_01bd: Expected O, but got Unknown
			//IL_01e5: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ea: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f9: Expected O, but got Unknown
			//IL_0338: Unknown result type (might be due to invalid IL or missing references)
			//IL_0346: Expected O, but got Unknown
			//IL_036f: Unknown result type (might be due to invalid IL or missing references)
			//IL_037d: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			FallbackRange = ((BaseUnityPlugin)this).Config.Bind<float>("General", "FallbackRange", 30f, "Search radius (m) for chests to claim, used only if AzuCraftyBoxes' own Container Range cannot be read.");
			Verbose = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Verbose", false, "Log every chest that gets claimed.");
			MatchChest = ((BaseUnityPlugin)this).Config.Bind<bool>("TableDurability", "MatchChest", true, "Copy the damage resistances of a vanilla chest onto EpicLoot's enchanting table so poison clouds and the like cannot damage it.");
			SourcePrefab = ((BaseUnityPlugin)this).Config.Bind<string>("TableDurability", "SourcePrefab", "piece_chest_wood", "Prefab whose WearNTear damage modifiers are copied.");
			CopyHealth = ((BaseUnityPlugin)this).Config.Bind<bool>("TableDurability", "CopyHealth", false, "Also copy the source prefab's WearNTear health.");
			FixBaseLevelRefund = ((BaseUnityPlugin)this).Config.Bind<bool>("TableRefund", "FixBaseLevelRefund", true, "When an enchanting table is deconstructed or destroyed, do not refund the cost of a feature's free starting level (EpicLoot 0.15.0 refunds Sacrifice level 1 although it is never paid).");
			CostBelowIcon = ((BaseUnityPlugin)this).Config.Bind<bool>("CostLabels", "BelowIcon", true, "Move cost amounts below their icons (client-side).");
			CostOutline = ((BaseUnityPlugin)this).Config.Bind<bool>("CostLabels", "Outline", true, "Give cost amounts a dark outline and bold text (client-side).");
			CostFontScale = ((BaseUnityPlugin)this).Config.Bind<float>("CostLabels", "FontScale", 1f, new ConfigDescription("Scale cost amount text (client-side).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.5f, 2f), Array.Empty<object>()));
			Log.LogInfo((object)$"Cost labels: below icon={CostBelowIcon.Value}, outline={CostOutline.Value}, scale={CostFontScale.Value}");
			if (!Chainloader.PluginInfos.ContainsKey("randyknapp.mods.epicloot"))
			{
				Log.LogInfo((object)"EnchantTableChestFix idle: EpicLoot not loaded.");
				return;
			}
			Harmony val = new Harmony("anri.enchanttablechestfix");
			val.Patch((MethodBase)AccessTools.Method(typeof(ZNetScene), "Awake", (Type[])null, (Type[])null), (HarmonyMethod)null, new HarmonyMethod(typeof(Plugin), "MatchTableDurability", (Type[])null)
			{
				priority = 0
			}, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			PatchCostLabels(val);
			PatchTableRefund(val);
			PatchSetPieceDedupe(val);
			List<string> list = new List<string>();
			string[] array = new string[2] { "Azumatt.AzuCraftyBoxes", "com.maxsch.valheim.MultiUserChest" };
			foreach (string text in array)
			{
				if (!Chainloader.PluginInfos.ContainsKey(text))
				{
					list.Add(text);
				}
			}
			if (list.Count > 0)
			{
				Log.LogInfo((object)("EnchantTableChestFix: chest-removal fix idle without " + string.Join(", ", list) + "; set-piece dedupe and table durability patch still active."));
				return;
			}
			Log.LogInfo((object)(string.Format("Detected AzuCraftyBoxes {0}, ", Chainloader.PluginInfos["Azumatt.AzuCraftyBoxes"].Metadata.Version) + string.Format("MultiUserChest {0}, ", Chainloader.PluginInfos["com.maxsch.valheim.MultiUserChest"].Metadata.Version) + string.Format("EpicLoot {0}.", Chainloader.PluginInfos["randyknapp.mods.epicloot"].Metadata.Version)));
			MethodInfo methodInfo = AccessTools.Method("AzuCraftyBoxes.Compatibility.EpicLootCompat:RemoveExactItem", (Type[])null, (Type[])null);
			if (methodInfo == null)
			{
				Log.LogWarning((object)"AzuCraftyBoxes EpicLootCompat.RemoveExactItem not found; the mod's internals changed, fix disabled.");
				return;
			}
			val.Patch((MethodBase)methodInfo, new HarmonyMethod(typeof(Plugin), "BeforeRemoveExactItem", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			MethodInfo methodInfo2 = AccessTools.Method("AzuCraftyBoxes.Compatibility.EpicLootCompat:RemoveItem", (Type[])null, (Type[])null);
			if (methodInfo2 != null)
			{
				val.Patch((MethodBase)methodInfo2, new HarmonyMethod(typeof(Plugin), "BeforeRemoveItemByName", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			}
			Log.LogInfo((object)"EnchantTableChestFix 1.4.0 active: claiming chest ownership before enchanting-table removals.");
		}

		private static void PatchCostLabels(Harmony harmony)
		{
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Expected O, but got Unknown
			try
			{
				harmony.Patch((MethodBase)AccessTools.Method(typeof(MultiSelectItemListElement), "Refresh", Type.EmptyTypes, (Type[])null), (HarmonyMethod)null, new HarmonyMethod(typeof(Plugin), "RefreshCostLabel", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			}
			catch (Exception ex)
			{
				WarnCostLabels("Cost label patch failed: " + ex.Message);
			}
		}

		private static void RefreshCostLabel(MultiSelectItemListElement __instance)
		{
			//IL_013e: Unknown result type (might be due to invalid IL or missing references)
			//IL_014a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0156: Unknown result type (might be due to invalid IL or missing references)
			//IL_0162: Unknown result type (might be due to invalid IL or missing references)
			//IL_016e: Unknown result type (might be due to invalid IL or missing references)
			//IL_017a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0192: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ad: Unknown result type (might be due to invalid IL or missing references)
			//IL_00be: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d3: Unknown result type (might be due to invalid IL or missing references)
			//IL_0102: Unknown result type (might be due to invalid IL or missing references)
			//IL_0107: Unknown result type (might be due to invalid IL or missing references)
			//IL_027a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0130: Unknown result type (might be due to invalid IL or missing references)
			//IL_0252: Unknown result type (might be due to invalid IL or missing references)
			//IL_025e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0264: Unknown result type (might be due to invalid IL or missing references)
			//IL_0228: Unknown result type (might be due to invalid IL or missing references)
			//IL_0232: Expected O, but got Unknown
			try
			{
				if (!__instance.ReadOnly || (Object)(object)__instance.ItemTotalQuantity == (Object)null || (Object)(object)__instance.ItemIcon == (Object)null)
				{
					return;
				}
				TMP_Text itemTotalQuantity = __instance.ItemTotalQuantity;
				CostLabelState costLabelState = ((Component)itemTotalQuantity).GetComponent<CostLabelState>();
				if ((Object)(object)costLabelState == (Object)null)
				{
					costLabelState = ((Component)itemTotalQuantity).gameObject.AddComponent<CostLabelState>();
					costLabelState.Capture(itemTotalQuantity);
				}
				itemTotalQuantity.fontSize = costLabelState.FontSize * CostFontScale.Value;
				if (CostBelowIcon.Value != costLabelState.BelowIcon)
				{
					RectTransform rectTransform = itemTotalQuantity.rectTransform;
					if (CostBelowIcon.Value)
					{
						Vector2 anchorMin = (rectTransform.anchorMax = new Vector2(0.5f, 0f));
						rectTransform.anchorMin = anchorMin;
						rectTransform.pivot = new Vector2(0.5f, 1f);
						rectTransform.anchoredPosition = new Vector2(0f, -2f);
						itemTotalQuantity.alignment = (TextAlignmentOptions)514;
						itemTotalQuantity.enableWordWrapping = false;
						itemTotalQuantity.overflowMode = (TextOverflowModes)0;
						Rect rect = ((Graphic)__instance.ItemIcon).rectTransform.rect;
						rectTransform.sizeDelta = new Vector2(((Rect)(ref rect)).width + 24f, (itemTotalQuantity.preferredHeight > 0f) ? itemTotalQuantity.preferredHeight : 20f);
					}
					else
					{
						rectTransform.anchorMin = costLabelState.AnchorMin;
						rectTransform.anchorMax = costLabelState.AnchorMax;
						rectTransform.pivot = costLabelState.Pivot;
						rectTransform.anchoredPosition = costLabelState.Position;
						rectTransform.sizeDelta = costLabelState.Size;
						itemTotalQuantity.alignment = costLabelState.Alignment;
						itemTotalQuantity.enableWordWrapping = costLabelState.WordWrapping;
						itemTotalQuantity.overflowMode = costLabelState.Overflow;
					}
					costLabelState.BelowIcon = CostBelowIcon.Value;
				}
				if (CostBelowIcon.Value && !CostClippingLogged && ((Object)(object)((Component)__instance).GetComponentInParent<RectMask2D>(true) != (Object)null || (Object)(object)((Component)__instance).GetComponentInParent<Mask>(true) != (Object)null))
				{
					CostClippingLogged = true;
					Log.LogInfo((object)"Cost labels: a parent Mask/RectMask2D is present; clipping may occur below the icon.");
				}
				if (CostOutline.Value == costLabelState.Outlined)
				{
					return;
				}
				if (CostOutline.Value)
				{
					if ((Object)(object)costLabelState.Material == (Object)null)
					{
						costLabelState.Material = new Material(itemTotalQuantity.fontSharedMaterial);
					}
					itemTotalQuantity.fontSharedMaterial = costLabelState.Material;
					itemTotalQuantity.outlineWidth = 0.25f;
					itemTotalQuantity.outlineColor = new Color32((byte)0, (byte)0, (byte)0, (byte)220);
					itemTotalQuantity.fontStyle = (FontStyles)(itemTotalQuantity.fontStyle | 1);
				}
				else
				{
					itemTotalQuantity.fontSharedMaterial = costLabelState.OriginalMaterial;
					itemTotalQuantity.fontStyle = costLabelState.Style;
				}
				costLabelState.Outlined = CostOutline.Value;
			}
			catch (Exception ex)
			{
				WarnCostLabels("Cost label refresh failed: " + ex.Message);
			}
		}

		private static void WarnCostLabels(string message)
		{
			if (!CostLabelsWarned)
			{
				CostLabelsWarned = true;
				Log.LogWarning((object)message);
			}
		}

		private static void PatchTableRefund(Harmony harmony)
		{
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: Expected O, but got Unknown
			//IL_007c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0091: Unknown result type (might be due to invalid IL or missing references)
			//IL_0096: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ae: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b9: Expected O, but got Unknown
			//IL_00b9: Expected O, but got Unknown
			//IL_00b9: Expected O, but got Unknown
			try
			{
				harmony.Patch((MethodBase)AccessTools.Method(typeof(EnchantingTableUpgrades), "GetUpgradeCost", new Type[2]
				{
					typeof(EnchantingFeature),
					typeof(int)
				}, (Type[])null), new HarmonyMethod(typeof(Plugin), "BeforeGetUpgradeCost", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				harmony.Patch((MethodBase)AccessTools.Method(typeof(Piece), "DropResources", (Type[])null, (Type[])null), new HarmonyMethod(typeof(Plugin), "BeforeTableRefund", (Type[])null), new HarmonyMethod(typeof(Plugin), "EndTableRefund", (Type[])null)
				{
					priority = 0
				}, (HarmonyMethod)null, new HarmonyMethod(typeof(Plugin), "EndTableRefund", (Type[])null), (HarmonyMethod)null);
			}
			catch (Exception ex)
			{
				WarnTableRefund("Table refund patch failed: " + ex.Message);
			}
		}

		private static void BeforeTableRefund(Piece __instance)
		{
			_inTableRefund = false;
			try
			{
				_inTableRefund = FixBaseLevelRefund.Value && (Object)(object)((Component)__instance).GetComponent<EnchantingTable>() != (Object)null;
			}
			catch (Exception ex)
			{
				_inTableRefund = false;
				WarnTableRefund("Table refund scope failed: " + ex.Message);
			}
		}

		private static void EndTableRefund()
		{
			_inTableRefund = false;
		}

		private static bool BeforeGetUpgradeCost(EnchantingFeature feature, int level, ref List<InventoryItemListElement> __result)
		{
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				if (!_inTableRefund || level != EnchantingTable.GetDefaultFeatureLevel(feature) + 1)
				{
					return true;
				}
				List<InventoryItemListElement> list = new List<InventoryItemListElement>();
				if (Verbose.Value)
				{
					Log.LogInfo((object)$"Table refund: skipped free starting level {level} for {feature}.");
				}
				__result = list;
				return false;
			}
			catch (Exception ex)
			{
				WarnTableRefund("Table refund suppression failed: " + ex.Message);
			}
			return true;
		}

		private static void WarnTableRefund(string message)
		{
			if (!RefundWarned)
			{
				RefundWarned = true;
				Log.LogWarning((object)message);
			}
		}

		private static void MatchTableDurability()
		{
			//IL_0082: Unknown result type (might be due to invalid IL or missing references)
			//IL_0087: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d6: Unknown result type (might be due to invalid IL or missing references)
			if (!MatchChest.Value)
			{
				return;
			}
			try
			{
				GameObject prefab = ZNetScene.instance.GetPrefab("piece_enchantingtable");
				WearNTear val = ((prefab != null) ? prefab.GetComponent<WearNTear>() : null);
				GameObject prefab2 = ZNetScene.instance.GetPrefab(SourcePrefab.Value);
				WearNTear val2 = ((prefab2 != null) ? prefab2.GetComponent<WearNTear>() : null);
				if ((Object)(object)val == (Object)null || (Object)(object)val2 == (Object)null)
				{
					WarnTableDurability("Table durability: prefab or WearNTear missing for piece_enchantingtable or " + SourcePrefab.Value + ".");
					return;
				}
				val.m_damages = val2.m_damages;
				if (CopyHealth.Value)
				{
					val.m_health = val2.m_health;
				}
				Log.LogInfo((object)string.Format("Table durability: copied damage resistances{0} from {1} to piece_enchantingtable; poison={2}.", CopyHealth.Value ? " and health" : "", SourcePrefab.Value, val.m_damages.m_poison));
			}
			catch (Exception ex)
			{
				WarnTableDurability("Table durability copy failed: " + ex.Message);
			}
		}

		private static void WarnTableDurability(string message)
		{
			if (!DurabilityWarned)
			{
				DurabilityWarned = true;
				Log.LogWarning((object)message);
			}
		}

		private static void BeforeRemoveExactItem(object __0)
		{
			ItemData item = (ItemData)((__0 is ItemData) ? __0 : null);
			if (item == null)
			{
				return;
			}
			ClaimChestsWhere(delegate(Inventory inv)
			{
				foreach (ItemData allItem in inv.GetAllItems())
				{
					if (allItem == item)
					{
						return true;
					}
					if (allItem.m_shared.m_name == item.m_shared.m_name && allItem.m_quality == item.m_quality && allItem.m_variant == item.m_variant)
					{
						return true;
					}
				}
				return false;
			});
		}

		private static void BeforeRemoveItemByName(object __0)
		{
			string name = __0 as string;
			if (name == null || string.IsNullOrEmpty(name))
			{
				return;
			}
			ClaimChestsWhere(delegate(Inventory inv)
			{
				foreach (ItemData allItem in inv.GetAllItems())
				{
					if (allItem.m_shared.m_name == name)
					{
						return true;
					}
				}
				return false;
			});
		}

		private static void ClaimChestsWhere(Func<Inventory, bool> holds)
		{
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d1: Unknown result type (might be due to invalid IL or missing references)
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null)
			{
				return;
			}
			float num = ReadAzuRange();
			Vector3 position = ((Component)localPlayer).transform.position;
			int num2 = 0;
			Container[] array = Object.FindObjectsByType<Container>((FindObjectsSortMode)0);
			foreach (Container val in array)
			{
				if ((Object)(object)val == (Object)null || Vector3.Distance(((Component)val).transform.position, position) > num)
				{
					continue;
				}
				ZNetView val2 = ContainerNView.Invoke(val);
				if ((Object)(object)val2 == (Object)null || !val2.IsValid() || val2.IsOwner())
				{
					continue;
				}
				Inventory inventory = val.GetInventory();
				if (inventory != null && holds(inventory))
				{
					val2.ClaimOwnership();
					num2++;
					if (Verbose.Value)
					{
						Log.LogInfo((object)$"Claimed ownership of {((Object)val).name} at {((Component)val).transform.position}");
					}
				}
			}
			if (num2 > 0 && Verbose.Value)
			{
				Log.LogInfo((object)$"Claimed {num2} chest(s) before enchanting-table removal.");
			}
		}

		private static void PatchSetPieceDedupe(Harmony harmony)
		{
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: Expected O, but got Unknown
			MethodInfo methodInfo = AccessTools.Method("EpicLoot.ItemDataExtensions:GetMundaneSetPieces", (Type[])null, (Type[])null);
			if (methodInfo == null)
			{
				Log.LogWarning((object)"EpicLoot ItemDataExtensions.GetMundaneSetPieces not found; set-piece dedupe disabled.");
				return;
			}
			harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, new HarmonyMethod(typeof(Plugin), "DedupeSetPieces", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			Log.LogInfo((object)"Set-piece tooltip de-duplication active.");
		}

		private static void DedupeSetPieces(ref List<string> __result)
		{
			if (__result == null || __result.Count < 2)
			{
				return;
			}
			HashSet<string> hashSet = new HashSet<string>();
			List<string> list = new List<string>(__result.Count);
			foreach (string item in __result)
			{
				if (hashSet.Add(item))
				{
					list.Add(item);
				}
			}
			__result = list;
		}

		private static float ReadAzuRange()
		{
			try
			{
				if ((AccessTools.TypeByName("AzuCraftyBoxes.AzuCraftyBoxesPlugin")?.GetField("mRange", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic))?.GetValue(null) is ConfigEntry<float> val)
				{
					return val.Value + 1f;
				}
			}
			catch (Exception)
			{
			}
			return FallbackRange.Value + 1f;
		}
	}
	internal sealed class CostLabelState : MonoBehaviour
	{
		internal Vector2 AnchorMin;

		internal Vector2 AnchorMax;

		internal Vector2 Pivot;

		internal Vector2 Position;

		internal Vector2 Size;

		internal float FontSize;

		internal TextAlignmentOptions Alignment;

		internal bool WordWrapping;

		internal bool BelowIcon;

		internal bool Outlined;

		internal TextOverflowModes Overflow;

		internal FontStyles Style;

		internal Material OriginalMaterial;

		internal Material Material;

		internal void Capture(TMP_Text label)
		{
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			//IL_0069: 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_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)
			RectTransform rectTransform = label.rectTransform;
			AnchorMin = rectTransform.anchorMin;
			AnchorMax = rectTransform.anchorMax;
			Pivot = rectTransform.pivot;
			Position = rectTransform.anchoredPosition;
			Size = rectTransform.sizeDelta;
			FontSize = label.fontSize;
			Alignment = label.alignment;
			WordWrapping = label.enableWordWrapping;
			Overflow = label.overflowMode;
			Style = label.fontStyle;
			OriginalMaterial = label.fontSharedMaterial;
		}

		private void OnDestroy()
		{
			if ((Object)(object)Material != (Object)null)
			{
				Object.Destroy((Object)(object)Material);
			}
		}
	}
}

EpicLoot.dll

Decompiled a day ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Serialization.Formatters.Binary;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Cryptography;
using System.Security.Permissions;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using Auga;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using Common;
using EpicLoot;
using EpicLoot.Abilities;
using EpicLoot.Adventure;
using EpicLoot.Adventure.Feature;
using EpicLoot.Biomes;
using EpicLoot.Compatibility;
using EpicLoot.Compendium;
using EpicLoot.Config;
using EpicLoot.Crafting;
using EpicLoot.CraftingV2;
using EpicLoot.Data;
using EpicLoot.GatedItemType;
using EpicLoot.General;
using EpicLoot.LegendarySystem;
using EpicLoot.LootBeams;
using EpicLoot.Magic;
using EpicLoot.Magic.MagicItemEffects;
using EpicLoot.Magic.MagicItemEffects.Helpers;
using EpicLoot.MagicItemEffects;
using EpicLoot.MagicItemEffects.Helpers;
using EpicLoot.MagicItemEffects.Shards;
using EpicLoot.Patching;
using EpicLoot.QuickConfig;
using EpicLoot.ShardStones;
using EpicLoot.Transformation;
using EpicLoot.src.Magic.MagicItemEffects.Helpers;
using EpicLoot.src.Magic.MagicItemEffects.Shards;
using EpicLoot_UnityLib;
using GUIFramework;
using HarmonyLib;
using JetBrains.Annotations;
using Jotunn;
using Jotunn.Configs;
using Jotunn.Entities;
using Jotunn.Extensions;
using Jotunn.Managers;
using Jotunn.Utils;
using Microsoft.CodeAnalysis;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using Newtonsoft.Json.Linq;
using SimpleJson;
using Splatform;
using StarLevelSystem;
using TMPro;
using UnityEngine;
using UnityEngine.Audio;
using UnityEngine.EventSystems;
using UnityEngine.Events;
using UnityEngine.Rendering;
using UnityEngine.SceneManagement;
using UnityEngine.TextCore;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: AssemblyTitle("EpicLoot")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("Randy Knapp Mods")]
[assembly: AssemblyProduct("EpicLoot")]
[assembly: AssemblyCopyright("Copyright © Randy Knapp 2023")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("963cf4f8-ba02-49fc-8884-97e781e6bc18")]
[assembly: AssemblyFileVersion("0.13.0")]
[assembly: IgnoresAccessChecksTo("assembly_googleanalytics")]
[assembly: IgnoresAccessChecksTo("assembly_guiutils")]
[assembly: IgnoresAccessChecksTo("assembly_lux")]
[assembly: IgnoresAccessChecksTo("assembly_postprocessing")]
[assembly: IgnoresAccessChecksTo("assembly_simplemeshcombine")]
[assembly: IgnoresAccessChecksTo("assembly_sunshafts")]
[assembly: IgnoresAccessChecksTo("assembly_utils")]
[assembly: IgnoresAccessChecksTo("assembly_valheim")]
[assembly: IgnoresAccessChecksTo("gui_framework")]
[assembly: IgnoresAccessChecksTo("SoftReferenceableAssets")]
[assembly: IgnoresAccessChecksTo("Splatform")]
[assembly: TargetFramework(".NETFramework,Version=v4.8.1", FrameworkDisplayName = ".NET Framework 4.8.1")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("0.13.0.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

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

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

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
	[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;
		}
	}
}
public static class PlayerExtensions
{
	public static ZDO GetZDO(this Player player)
	{
		return ((Character)player).m_nview.GetZDO();
	}
}
namespace Common
{
	public class ConfigPositionedElement : MonoBehaviour
	{
		public ConfigEntry<TextAnchor> AnchorConfig;

		public ConfigEntry<Vector2> PositionConfig;

		protected RectTransform _rt;

		protected TextAnchor _currentAnchor;

		public virtual void Awake()
		{
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Expected O, but got Unknown
			_rt = (RectTransform)((Component)this).transform;
			EnsureCorrectPosition();
		}

		public virtual void Update()
		{
			EnsureCorrectPosition();
		}

		public virtual void EnsureCorrectPosition()
		{
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0061: Unknown result type (might be due to invalid IL or missing references)
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			//IL_0067: Unknown result type (might be due to invalid IL or missing references)
			//IL_0068: Unknown result type (might be due to invalid IL or missing references)
			//IL_0069: Unknown result type (might be due to invalid IL or missing references)
			//IL_0093: Expected I4, but got Unknown
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fc: Unknown result type (might be due to invalid IL or missing references)
			//IL_0104: Unknown result type (might be due to invalid IL or missing references)
			//IL_0106: Unknown result type (might be due to invalid IL or missing references)
			//IL_0107: 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_013d: 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_0147: Unknown result type (might be due to invalid IL or missing references)
			//IL_0148: Unknown result type (might be due to invalid IL or missing references)
			//IL_014f: Unknown result type (might be due to invalid IL or missing references)
			//IL_017e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0186: Unknown result type (might be due to invalid IL or missing references)
			//IL_0188: Unknown result type (might be due to invalid IL or missing references)
			//IL_0189: Unknown result type (might be due to invalid IL or missing references)
			//IL_0190: Unknown result type (might be due to invalid IL or missing references)
			//IL_01bf: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c7: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c9: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ca: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d1: Unknown result type (might be due to invalid IL or missing references)
			//IL_0200: Unknown result type (might be due to invalid IL or missing references)
			//IL_0208: Unknown result type (might be due to invalid IL or missing references)
			//IL_020a: Unknown result type (might be due to invalid IL or missing references)
			//IL_020b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0212: Unknown result type (might be due to invalid IL or missing references)
			//IL_0241: Unknown result type (might be due to invalid IL or missing references)
			//IL_0249: Unknown result type (might be due to invalid IL or missing references)
			//IL_024b: Unknown result type (might be due to invalid IL or missing references)
			//IL_024c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0253: Unknown result type (might be due to invalid IL or missing references)
			//IL_027f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0287: Unknown result type (might be due to invalid IL or missing references)
			//IL_0289: Unknown result type (might be due to invalid IL or missing references)
			//IL_028a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0291: Unknown result type (might be due to invalid IL or missing references)
			//IL_02bd: Unknown result type (might be due to invalid IL or missing references)
			//IL_02c5: Unknown result type (might be due to invalid IL or missing references)
			//IL_02c7: Unknown result type (might be due to invalid IL or missing references)
			//IL_02c8: Unknown result type (might be due to invalid IL or missing references)
			//IL_02cf: Unknown result type (might be due to invalid IL or missing references)
			//IL_02e4: Unknown result type (might be due to invalid IL or missing references)
			if (AnchorConfig != null && PositionConfig != null && (_currentAnchor != AnchorConfig.Value || !(_rt.anchoredPosition == PositionConfig.Value)))
			{
				_currentAnchor = AnchorConfig.Value;
				TextAnchor currentAnchor = _currentAnchor;
				TextAnchor val = currentAnchor;
				Vector2 val2 = default(Vector2);
				switch ((int)val)
				{
				case 0:
				{
					RectTransform rt25 = _rt;
					RectTransform rt26 = _rt;
					RectTransform rt27 = _rt;
					((Vector2)(ref val2))..ctor(0f, 1f);
					rt27.anchorMax = val2;
					Vector2 pivot = (rt26.anchorMin = val2);
					rt25.pivot = pivot;
					break;
				}
				case 1:
				{
					RectTransform rt22 = _rt;
					RectTransform rt23 = _rt;
					RectTransform rt24 = _rt;
					((Vector2)(ref val2))..ctor(0.5f, 1f);
					rt24.anchorMax = val2;
					Vector2 pivot = (rt23.anchorMin = val2);
					rt22.pivot = pivot;
					break;
				}
				case 2:
				{
					RectTransform rt19 = _rt;
					RectTransform rt20 = _rt;
					RectTransform rt21 = _rt;
					((Vector2)(ref val2))..ctor(1f, 1f);
					rt21.anchorMax = val2;
					Vector2 pivot = (rt20.anchorMin = val2);
					rt19.pivot = pivot;
					break;
				}
				case 3:
				{
					RectTransform rt16 = _rt;
					RectTransform rt17 = _rt;
					RectTransform rt18 = _rt;
					((Vector2)(ref val2))..ctor(0f, 0.5f);
					rt18.anchorMax = val2;
					Vector2 pivot = (rt17.anchorMin = val2);
					rt16.pivot = pivot;
					break;
				}
				case 4:
				{
					RectTransform rt13 = _rt;
					RectTransform rt14 = _rt;
					RectTransform rt15 = _rt;
					((Vector2)(ref val2))..ctor(0.5f, 0.5f);
					rt15.anchorMax = val2;
					Vector2 pivot = (rt14.anchorMin = val2);
					rt13.pivot = pivot;
					break;
				}
				case 5:
				{
					RectTransform rt10 = _rt;
					RectTransform rt11 = _rt;
					RectTransform rt12 = _rt;
					((Vector2)(ref val2))..ctor(1f, 0.5f);
					rt12.anchorMax = val2;
					Vector2 pivot = (rt11.anchorMin = val2);
					rt10.pivot = pivot;
					break;
				}
				case 6:
				{
					RectTransform rt7 = _rt;
					RectTransform rt8 = _rt;
					RectTransform rt9 = _rt;
					((Vector2)(ref val2))..ctor(0f, 0f);
					rt9.anchorMax = val2;
					Vector2 pivot = (rt8.anchorMin = val2);
					rt7.pivot = pivot;
					break;
				}
				case 7:
				{
					RectTransform rt4 = _rt;
					RectTransform rt5 = _rt;
					RectTransform rt6 = _rt;
					((Vector2)(ref val2))..ctor(0.5f, 0f);
					rt6.anchorMax = val2;
					Vector2 pivot = (rt5.anchorMin = val2);
					rt4.pivot = pivot;
					break;
				}
				case 8:
				{
					RectTransform rt = _rt;
					RectTransform rt2 = _rt;
					RectTransform rt3 = _rt;
					((Vector2)(ref val2))..ctor(1f, 0f);
					rt3.anchorMax = val2;
					Vector2 pivot = (rt2.anchorMin = val2);
					rt.pivot = pivot;
					break;
				}
				}
				_rt.anchoredPosition = PositionConfig.Value;
			}
		}
	}
	internal static class ConfigUI
	{
		internal class ConfigUIInputGuard : MonoBehaviour
		{
			private bool held;

			internal void Hold()
			{
				if (!held)
				{
					held = true;
					PushInputBlock();
				}
			}

			public void OnDestroy()
			{
				if (held)
				{
					held = false;
					PopInputBlock();
				}
			}
		}

		internal const float RowHeight = 34f;

		internal const float SubRowHeight = 26f;

		internal const float RowGap = 4f;

		internal const float CloseXSize = 28f;

		private static int inputBlockDepth;

		internal static void PushInputBlock()
		{
			if (!((Object)(object)Player.m_localPlayer == (Object)null))
			{
				inputBlockDepth++;
				if (inputBlockDepth == 1)
				{
					GUIManager.BlockInput(true);
				}
			}
		}

		internal static void PopInputBlock()
		{
			if (inputBlockDepth > 0)
			{
				inputBlockDepth--;
				if (inputBlockDepth == 0)
				{
					GUIManager.BlockInput(false);
				}
			}
		}

		internal static GameObject NewUI(string name, Transform parent, params Type[] components)
		{
			//IL_0003: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Expected O, but got Unknown
			GameObject val = new GameObject(name, components)
			{
				layer = 5
			};
			if ((Object)(object)val.GetComponent<RectTransform>() == (Object)null)
			{
				val.AddComponent<RectTransform>();
			}
			val.transform.SetParent(parent, false);
			return val;
		}

		internal static GameObject NewRect(string name, Transform parent, float x, float y, float w, float h)
		{
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Expected O, but got Unknown
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			//IL_006f: Unknown result type (might be due to invalid IL or missing references)
			//IL_007e: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = NewUI(name, parent, typeof(RectTransform));
			RectTransform val2 = (RectTransform)val.transform;
			val2.anchorMin = new Vector2(0f, 1f);
			val2.anchorMax = new Vector2(0f, 1f);
			val2.pivot = new Vector2(0f, 1f);
			val2.sizeDelta = new Vector2(w, h);
			val2.anchoredPosition = new Vector2(x, 0f - y);
			return val;
		}

		internal static GameObject NewRow(Transform parent, float width, float height)
		{
			return NewRect("Row", parent, 0f, 0f, width, height);
		}

		internal static GameObject NewLayoutRow(Transform content, float width, float height)
		{
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Expected O, but got Unknown
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0070: Unknown result type (might be due to invalid IL or missing references)
			//IL_007e: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = NewUI("LayoutRow", content, typeof(RectTransform), typeof(LayoutElement));
			RectTransform val2 = (RectTransform)val.transform;
			val2.anchorMin = new Vector2(0f, 1f);
			val2.anchorMax = new Vector2(0f, 1f);
			val2.pivot = new Vector2(0f, 1f);
			val2.sizeDelta = new Vector2(width, height);
			LayoutElement component = val.GetComponent<LayoutElement>();
			component.minHeight = height;
			component.preferredHeight = height;
			component.minWidth = width;
			component.preferredWidth = width;
			return val;
		}

		internal static void LayoutColumn(List<GameObject> rows, float x, float startY, float gap = 4f)
		{
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: Expected O, but got Unknown
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			float num = startY;
			foreach (GameObject row in rows)
			{
				if (!((Object)(object)row == (Object)null) && row.activeSelf)
				{
					RectTransform val = (RectTransform)row.transform;
					val.anchoredPosition = new Vector2(x, 0f - num);
					num += val.sizeDelta.y + gap;
				}
			}
		}

		internal static void PositionRow(GameObject row, float x, float y)
		{
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)row == (Object)null))
			{
				((RectTransform)row.transform).anchoredPosition = new Vector2(x, 0f - y);
			}
		}

		internal static GameObject CreatePanel(string title, float w, float h, out Transform body)
		{
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_0071: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = GUIManager.Instance.CreateWoodpanel(GUIManager.CustomGUIFront.transform, new Vector2(0.5f, 0.5f), new Vector2(0.5f, 0.5f), new Vector2(0f, 0f), w, h, true);
			val.AddComponent<ConfigUIInputGuard>().Hold();
			AddText(val.transform, 0f, 16f, w, 34f, title, 22, (TextAnchor)4, GUIManager.Instance.ValheimYellow);
			body = val.transform;
			return val;
		}

		internal static GameObject CreateScroll(Transform parent, float x, float y, float w, float h, out Transform content, out float contentWidth)
		{
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = NewRect("ScrollHolder", parent, x, y, w, h);
			GameObject val2 = GUIManager.Instance.CreateScrollView(val.transform, false, true, 8f, 4f, GUIManager.Instance.ValheimScrollbarHandleColorBlock, new Color(0f, 0f, 0f, 0.5f), w, h);
			content = val2.transform.Find("Scroll View/Viewport/Content");
			contentWidth = w - 16f;
			ScrollRect componentInChildren = val2.GetComponentInChildren<ScrollRect>();
			if ((Object)(object)componentInChildren != (Object)null)
			{
				componentInChildren.scrollSensitivity = 200f;
			}
			return val2;
		}

		internal static string L(string text)
		{
			if (string.IsNullOrEmpty(text))
			{
				return "";
			}
			return (Localization.instance != null) ? Localization.instance.Localize(text) : text;
		}

		internal static Text AddText(Transform parent, float x, float y, float w, float h, string text, int fontSize, TextAnchor anchor, Color? color = null)
		{
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_0060: 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_007b: Expected O, but got Unknown
			//IL_0086: Unknown result type (might be due to invalid IL or missing references)
			//IL_0095: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a8: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = GUIManager.Instance.CreateText(L(text), parent, new Vector2(0f, 1f), new Vector2(0f, 1f), new Vector2(x, 0f - y), GUIManager.Instance.AveriaSerifBold, fontSize, (Color)(((??)color) ?? GUIManager.Instance.ValheimBeige), true, Color.black, w, h, false);
			RectTransform val2 = (RectTransform)val.transform;
			val2.pivot = new Vector2(0f, 1f);
			val2.anchoredPosition = new Vector2(x, 0f - y);
			Text component = val.GetComponent<Text>();
			component.alignment = anchor;
			component.horizontalOverflow = (HorizontalWrapMode)0;
			component.verticalOverflow = (VerticalWrapMode)0;
			return component;
		}

		internal static GameObject AddHeaderRow(Transform parent, float colWidth, string text, TextAnchor anchor = (TextAnchor)3)
		{
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = NewRow(parent, colWidth, 34f);
			AddText(val.transform, 0f, 0f, colWidth, 34f, text, 18, anchor, GUIManager.Instance.ValheimYellow);
			return val;
		}

		internal static GameObject AddTextRow(Transform parent, float colWidth, float height, string text, int fontSize, Color color, TextAnchor anchor = (TextAnchor)0)
		{
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = NewRow(parent, colWidth, height);
			AddText(val.transform, 0f, 0f, colWidth, height, text, fontSize, anchor, color);
			return val;
		}

		internal static GameObject AddSpacerRow(Transform parent, float colWidth, float height)
		{
			return NewRow(parent, colWidth, height);
		}

		internal static GameObject AddDividerRow(Transform parent, float colWidth, float height = 12f)
		{
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Expected O, but got Unknown
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0071: Unknown result type (might be due to invalid IL or missing references)
			//IL_0083: Unknown result type (might be due to invalid IL or missing references)
			//IL_009c: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c3: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = NewRow(parent, colWidth, height);
			GameObject val2 = NewUI("Divider", val.transform, typeof(Image));
			RectTransform val3 = (RectTransform)val2.transform;
			val3.anchorMin = new Vector2(0f, 1f);
			val3.anchorMax = new Vector2(0f, 1f);
			val3.pivot = new Vector2(0f, 1f);
			val3.sizeDelta = new Vector2(colWidth, 2f);
			val3.anchoredPosition = new Vector2(0f, 0f - height * 0.5f);
			Image component = val2.GetComponent<Image>();
			((Graphic)component).color = new Color(0.6f, 0.5f, 0.35f, 0.6f);
			((Graphic)component).raycastTarget = false;
			return val;
		}

		internal static void SetMessages(Text target, IList<string> errors, IList<string> warnings)
		{
			if ((Object)(object)target == (Object)null)
			{
				return;
			}
			List<string> list = new List<string>();
			if (errors != null)
			{
				foreach (string error in errors)
				{
					list.Add("<color=#F87171>" + error + "</color>");
				}
			}
			if (warnings != null)
			{
				foreach (string warning in warnings)
				{
					list.Add("<color=#FBBF24>" + warning + "</color>");
				}
			}
			target.text = string.Join("\n", list.ToArray());
		}

		internal static GameObject AddButton(Transform parent, float x, float y, float w, string text, UnityAction onClick, float h = 40f)
		{
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: Expected O, but got Unknown
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_0063: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = GUIManager.Instance.CreateButton(L(text), parent, new Vector2(0f, 1f), new Vector2(0f, 1f), new Vector2(x, 0f - y), w, h);
			RectTransform val2 = (RectTransform)val.transform;
			val2.pivot = new Vector2(0f, 1f);
			val2.anchoredPosition = new Vector2(x, 0f - y);
			if (onClick != null)
			{
				((UnityEvent)val.GetComponent<Button>().onClick).AddListener(onClick);
			}
			return val;
		}

		internal static GameObject AddCloseX(Transform panel, float panelWidth, UnityAction onClick)
		{
			return AddButton(panel, panelWidth - 28f - 16f, 12f, 28f, "X", onClick, 28f);
		}

		internal static Toggle AddToggle(Transform parent, float x, float y, float size, bool value, Action<bool> onChange)
		{
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Expected O, but got Unknown
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0064: 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_0089: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = GUIManager.Instance.CreateToggle(parent, size, size);
			val.transform.SetParent(parent, false);
			RectTransform val2 = (RectTransform)val.transform;
			((Transform)val2).localScale = Vector3.one;
			val2.anchorMin = new Vector2(0f, 1f);
			val2.anchorMax = new Vector2(0f, 1f);
			val2.pivot = new Vector2(0f, 1f);
			val2.anchoredPosition = new Vector2(x, 0f - y);
			Toggle component = val.GetComponent<Toggle>();
			component.isOn = value;
			if (onChange != null)
			{
				((UnityEvent<bool>)(object)component.onValueChanged).AddListener((UnityAction<bool>)delegate(bool b)
				{
					onChange(b);
				});
			}
			return component;
		}

		internal static GameObject AddToggleRow(Transform parent, float colWidth, float labelW, string label, bool value, Action<bool> onChange, bool toggleOnLeft = false)
		{
			GameObject val = NewRow(parent, colWidth, 34f);
			float x = (toggleOnLeft ? 0f : (labelW + 6f));
			float x2 = (toggleOnLeft ? 34f : 0f);
			AddText(val.transform, x2, 0f, labelW, 34f, label, 15, (TextAnchor)3);
			AddToggle(val.transform, x, 3f, 26f, value, onChange);
			return val;
		}

		internal static string Fmt(float v, bool whole)
		{
			return whole ? ((int)Mathf.Round(v)).ToString(CultureInfo.InvariantCulture) : v.ToString("0.00", CultureInfo.InvariantCulture);
		}

		internal static Slider BuildSlider(Transform parent, float x, float y, float width, float min, float max, float value, bool wholeNumbers)
		{
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Expected O, but got Unknown
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0070: Unknown result type (might be due to invalid IL or missing references)
			//IL_0082: Unknown result type (might be due to invalid IL or missing references)
			//IL_0091: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cc: Expected O, but got Unknown
			//IL_00d7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ed: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f9: Unknown result type (might be due to invalid IL or missing references)
			//IL_0105: Unknown result type (might be due to invalid IL or missing references)
			//IL_012a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0161: Unknown result type (might be due to invalid IL or missing references)
			//IL_0168: Expected O, but got Unknown
			//IL_0174: Unknown result type (might be due to invalid IL or missing references)
			//IL_018b: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a2: Unknown result type (might be due to invalid IL or missing references)
			//IL_01af: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e7: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ee: Expected O, but got Unknown
			//IL_01fa: Unknown result type (might be due to invalid IL or missing references)
			//IL_0220: Unknown result type (might be due to invalid IL or missing references)
			//IL_0257: Unknown result type (might be due to invalid IL or missing references)
			//IL_025e: Expected O, but got Unknown
			//IL_0260: Unknown result type (might be due to invalid IL or missing references)
			//IL_026d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0284: Unknown result type (might be due to invalid IL or missing references)
			//IL_0291: Unknown result type (might be due to invalid IL or missing references)
			//IL_02c9: Unknown result type (might be due to invalid IL or missing references)
			//IL_02d0: Expected O, but got Unknown
			//IL_02dc: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = NewUI("Slider", parent, typeof(RectTransform), typeof(Slider));
			RectTransform val2 = (RectTransform)val.transform;
			val2.anchorMin = new Vector2(0f, 1f);
			val2.anchorMax = new Vector2(0f, 1f);
			val2.pivot = new Vector2(0f, 1f);
			val2.sizeDelta = new Vector2(width, 20f);
			val2.anchoredPosition = new Vector2(x, 0f - y);
			GameObject val3 = NewUI("Background", val.transform, typeof(Image));
			RectTransform val4 = (RectTransform)val3.transform;
			val4.anchorMin = new Vector2(0f, 0.25f);
			val4.anchorMax = new Vector2(1f, 0.75f);
			val4.sizeDelta = Vector2.zero;
			val4.anchoredPosition = Vector2.zero;
			((Graphic)val3.GetComponent<Image>()).color = new Color(0f, 0f, 0f, 0.6f);
			GameObject val5 = NewUI("Fill Area", val.transform, typeof(RectTransform));
			RectTransform val6 = (RectTransform)val5.transform;
			val6.anchorMin = new Vector2(0f, 0.25f);
			val6.anchorMax = new Vector2(1f, 0.75f);
			val6.sizeDelta = new Vector2(-20f, 0f);
			val6.anchoredPosition = Vector2.zero;
			GameObject val7 = NewUI("Fill", val5.transform, typeof(Image));
			RectTransform val8 = (RectTransform)val7.transform;
			val8.sizeDelta = new Vector2(10f, 0f);
			((Graphic)val7.GetComponent<Image>()).color = new Color(0.7f, 0.6f, 0.4f, 0.9f);
			GameObject val9 = NewUI("Handle Slide Area", val.transform, typeof(RectTransform));
			RectTransform val10 = (RectTransform)val9.transform;
			val10.anchorMin = Vector2.zero;
			val10.anchorMax = Vector2.one;
			val10.sizeDelta = new Vector2(-20f, 0f);
			val10.anchoredPosition = Vector2.zero;
			GameObject val11 = NewUI("Handle", val9.transform, typeof(Image));
			RectTransform val12 = (RectTransform)val11.transform;
			val12.sizeDelta = new Vector2(20f, 0f);
			Image component = val11.GetComponent<Image>();
			component.sprite = GUIManager.Instance.GetSprite("checkbox_marker");
			component.type = (Type)1;
			Slider component2 = val.GetComponent<Slider>();
			component2.fillRect = val8;
			component2.handleRect = val12;
			((Selectable)component2).targetGraphic = (Graphic)(object)component;
			component2.direction = (Direction)0;
			component2.minValue = min;
			component2.maxValue = max;
			component2.wholeNumbers = wholeNumbers;
			component2.value = Mathf.Clamp(value, min, max);
			GUIManager.Instance.ApplySliderStyle(component2);
			return component2;
		}

		internal static GameObject AddSliderRow(Transform parent, float colWidth, float labelW, float sliderW, float valueW, string label, float min, float max, float value, bool wholeNumbers, Action<float> onChange)
		{
			//IL_00aa: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00eb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f2: Expected O, but got Unknown
			//IL_00fe: Unknown result type (might be due to invalid IL or missing references)
			//IL_0111: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = NewRow(parent, colWidth, 34f);
			AddText(val.transform, 0f, 0f, labelW, 34f, label, 15, (TextAnchor)3);
			Slider slider = BuildSlider(val.transform, labelW, 7f, sliderW, min, max, value, wholeNumbers);
			float num = labelW + sliderW + 10f;
			GameObject val2 = GUIManager.Instance.CreateInputField(val.transform, new Vector2(0f, 1f), new Vector2(0f, 1f), new Vector2(num, -3f), (ContentType)(wholeNumbers ? 2 : 3), (string)null, 15, valueW, 28f);
			RectTransform val3 = (RectTransform)val2.transform;
			val3.pivot = new Vector2(0f, 1f);
			val3.anchoredPosition = new Vector2(num, -3f);
			InputField box = val2.GetComponent<InputField>();
			box.SetTextWithoutNotify(Fmt(slider.value, wholeNumbers));
			((UnityEvent<float>)(object)slider.onValueChanged).AddListener((UnityAction<float>)delegate(float v)
			{
				if (wholeNumbers)
				{
					v = Mathf.Round(v);
				}
				box.SetTextWithoutNotify(Fmt(v, wholeNumbers));
				onChange?.Invoke(v);
			});
			((UnityEvent<string>)(object)box.onEndEdit).AddListener((UnityAction<string>)delegate(string str)
			{
				if (!float.TryParse(str.Replace(',', '.'), NumberStyles.Float, CultureInfo.InvariantCulture, out var result))
				{
					result = slider.value;
				}
				result = Mathf.Clamp(result, min, max);
				if (wholeNumbers)
				{
					result = Mathf.Round(result);
				}
				box.SetTextWithoutNotify(Fmt(result, wholeNumbers));
				if (slider.value != result)
				{
					slider.value = result;
				}
				else
				{
					onChange?.Invoke(result);
				}
			});
			return val;
		}

		internal static InputField AddTextField(Transform parent, float x, float y, float w, string value, Action<string> onCommit, ContentType contentType = (ContentType)0, string placeholder = null, int charLimit = 0)
		{
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: Expected O, but got Unknown
			//IL_0064: 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)
			GameObject val = GUIManager.Instance.CreateInputField(parent, new Vector2(0f, 1f), new Vector2(0f, 1f), new Vector2(x, 0f - y), contentType, placeholder, 15, w, 28f);
			RectTransform val2 = (RectTransform)val.transform;
			val2.pivot = new Vector2(0f, 1f);
			val2.anchoredPosition = new Vector2(x, 0f - y);
			InputField component = val.GetComponent<InputField>();
			if (charLimit > 0)
			{
				component.characterLimit = charLimit;
			}
			component.SetTextWithoutNotify(value ?? "");
			if (onCommit != null)
			{
				((UnityEvent<string>)(object)component.onEndEdit).AddListener((UnityAction<string>)delegate(string s)
				{
					onCommit(s);
				});
			}
			return component;
		}

		internal static GameObject AddTextFieldRow(Transform parent, float colWidth, float labelW, float fieldW, string label, string value, Action<string> onCommit, string placeholder = null, int charLimit = 0)
		{
			GameObject val = NewRow(parent, colWidth, 34f);
			AddText(val.transform, 0f, 0f, labelW, 34f, label, 15, (TextAnchor)3);
			AddTextField(val.transform, labelW + 6f, 3f, fieldW, value, onCommit, (ContentType)0, placeholder, charLimit);
			return val;
		}

		internal static GameObject AddEnumCycleRow(Transform parent, float colWidth, float labelW, float ctrlW, string label, string[] options, int currentIndex, Action<int> onChange)
		{
			//IL_009f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ae: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00dc: Expected O, but got Unknown
			//IL_00e7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ff: Unknown result type (might be due to invalid IL or missing references)
			//IL_0128: Unknown result type (might be due to invalid IL or missing references)
			//IL_0132: Expected O, but got Unknown
			GameObject val = NewRow(parent, colWidth, 34f);
			AddText(val.transform, 0f, 0f, labelW, 34f, label, 15, (TextAnchor)3);
			int idx = Mathf.Clamp(currentIndex, 0, Math.Max(0, options.Length - 1));
			GameObject val2 = GUIManager.Instance.CreateButton((options.Length != 0) ? options[idx] : "", val.transform, new Vector2(0f, 1f), new Vector2(0f, 1f), new Vector2(labelW + 6f, -2f), ctrlW, 28f);
			RectTransform val3 = (RectTransform)val2.transform;
			val3.pivot = new Vector2(0f, 1f);
			val3.anchoredPosition = new Vector2(labelW + 6f, -2f);
			Text caption = val2.GetComponentInChildren<Text>();
			((UnityEvent)val2.GetComponent<Button>().onClick).AddListener((UnityAction)delegate
			{
				if (options.Length != 0)
				{
					idx = (idx + 1) % options.Length;
					caption.text = options[idx];
					onChange?.Invoke(idx);
				}
			});
			return val;
		}

		internal static GameObject AddEnumFlagsRow(Transform parent, float colWidth, float labelW, string label, string[] names, Func<int, bool> isOn, Action<int, bool> set)
		{
			GameObject val = NewRow(parent, colWidth, 34f);
			AddText(val.transform, 0f, 0f, labelW, 34f, label, 15, (TextAnchor)3);
			float num = labelW + 6f;
			for (int i = 0; i < names.Length; i++)
			{
				int index = i;
				AddToggle(val.transform, num, 4f, 22f, isOn(index), delegate(bool on)
				{
					set(index, on);
				});
				AddText(val.transform, num + 26f, 0f, 90f, 34f, names[index], 13, (TextAnchor)3);
				num += 120f;
			}
			return val;
		}

		internal static GameObject AddPickerRow(Transform parent, float colWidth, float labelW, float ctrlW, string label, string current, Func<IList<string>> options, Action<string> onPick, Func<string, bool> isKnown = null)
		{
			//IL_00d5: Unknown result type (might be due to invalid IL or missing references)
			//IL_014f: Unknown result type (might be due to invalid IL or missing references)
			//IL_015e: Expected O, but got Unknown
			GameObject val = NewRow(parent, colWidth, 34f);
			AddText(val.transform, 0f, 0f, labelW, 34f, label, 15, (TextAnchor)3);
			float num = ctrlW - 40f;
			InputField field = AddTextField(val.transform, labelW + 6f, 3f, num, current, delegate(string s)
			{
				onPick?.Invoke(s);
			}, (ContentType)0);
			Text marker = AddText(val.transform, labelW + 6f + num + 44f, 0f, 20f, 34f, "", 15, (TextAnchor)3, (Color?)new Color(0.98f, 0.75f, 0.14f));
			Action refreshMarker = delegate
			{
				bool flag = isKnown != null && !string.IsNullOrEmpty(field.text) && !isKnown(field.text);
				marker.text = (flag ? "!" : "");
			};
			refreshMarker();
			((UnityEvent<string>)(object)field.onEndEdit).AddListener((UnityAction<string>)delegate
			{
				refreshMarker();
			});
			AddButton(val.transform, labelW + 6f + num + 4f, 3f, 34f, "...", (UnityAction)delegate
			{
				string title = label;
				IList<string> options2;
				if (options == null)
				{
					IList<string> list = new List<string>();
					options2 = list;
				}
				else
				{
					options2 = options();
				}
				ConfigUIPicker.ShowPicker(title, options2, field.text, delegate(string picked)
				{
					field.SetTextWithoutNotify(picked);
					refreshMarker();
					onPick?.Invoke(picked);
				});
			}, 28f);
			return val;
		}

		internal static GameObject AddStringListEditor(Transform content, float width, string label, List<string> items, Action onChanged, Func<IList<string>> options = null, Func<string, bool> isKnown = null)
		{
			//IL_0064: Unknown result type (might be due to invalid IL or missing references)
			//IL_0097: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a6: Expected O, but got Unknown
			//IL_014e: Unknown result type (might be due to invalid IL or missing references)
			//IL_020b: Unknown result type (might be due to invalid IL or missing references)
			//IL_021a: Expected O, but got Unknown
			//IL_01d6: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e5: Expected O, but got Unknown
			GameObject val = NewLayoutRow(content, width, 26f);
			AddText(val.transform, 0f, 0f, width - 90f, 26f, label, 14, (TextAnchor)3, GUIManager.Instance.ValheimOrange);
			AddButton(val.transform, width - 84f, 0f, 80f, "Add", (UnityAction)delegate
			{
				items.Add("");
				onChanged?.Invoke();
			}, 24f);
			for (int num = 0; num < items.Count; num++)
			{
				int index = num;
				GameObject val2 = NewLayoutRow(content, width, 26f);
				InputField field = AddTextField(val2.transform, 12f, 0f, width - 92f, items[index], delegate(string s)
				{
					items[index] = s;
				}, (ContentType)0);
				Text marker = AddText(val2.transform, width - 74f, 0f, 16f, 26f, "", 14, (TextAnchor)3, (Color?)new Color(0.98f, 0.75f, 0.14f));
				Action refreshMarker = delegate
				{
					bool flag = isKnown != null && !string.IsNullOrEmpty(field.text) && !isKnown(field.text);
					marker.text = (flag ? "!" : "");
				};
				refreshMarker();
				((UnityEvent<string>)(object)field.onEndEdit).AddListener((UnityAction<string>)delegate
				{
					refreshMarker();
				});
				if (options != null)
				{
					AddButton(val2.transform, width - 56f, 0f, 26f, "...", (UnityAction)delegate
					{
						ConfigUIPicker.ShowPicker(label, options(), field.text, delegate(string picked)
						{
							field.SetTextWithoutNotify(picked);
							items[index] = picked;
							refreshMarker();
						});
					}, 24f);
				}
				AddButton(val2.transform, width - 26f, 0f, 24f, "x", (UnityAction)delegate
				{
					items.RemoveAt(index);
					onChanged?.Invoke();
				}, 24f);
			}
			return val;
		}
	}
	internal enum FirstRunMode
	{
		Auto,
		ShowNextLaunch,
		Never
	}
	internal static class ConfigUIFirstRun
	{
		internal const string FolderName = "ModQuickConfig";

		internal const string FileName = "FirstRun.cfg";

		internal const string Section = "FirstRun";

		internal const string QueueKeySuffix = ".FirstRun";

		private const string LegacyPlaceholder = "\u0001";

		private static string recordPath;

		private static readonly char[] InvalidKeyChars = new char[8] { '=', '\n', '\t', '\\', '"', '\'', '[', ']' };

		internal static string RecordPath
		{
			get
			{
				if (recordPath == null)
				{
					recordPath = Path.Combine(Path.Combine(Application.persistentDataPath, "ModQuickConfig"), "FirstRun.cfg");
				}
				return recordPath;
			}
		}

		internal static bool ShouldShow(string modKey, int revision, ConfigEntry<FirstRunMode> mode)
		{
			if (GUIManager.IsHeadless())
			{
				return false;
			}
			return ModeOf(mode) switch
			{
				FirstRunMode.Never => false, 
				FirstRunMode.ShowNextLaunch => true, 
				_ => SeenRevision(modKey) < revision, 
			};
		}

		internal static int SeenRevision(string modKey)
		{
			if (GUIManager.IsHeadless() || !IsValidKey(modKey))
			{
				return int.MaxValue;
			}
			string text = null;
			try
			{
				text = RecordPath;
				if (!File.Exists(text))
				{
					return 0;
				}
				return Math.Max(0, BindSeen(OpenRecord(), modKey).Value);
			}
			catch (Exception ex)
			{
				ModLogger.LogWarning("Could not read the first-run record at " + text + ", so the '" + modKey + "' first-run popup is treated as seen: " + ex.Message);
				return int.MaxValue;
			}
		}

		internal static void MarkSeen(string modKey, int revision)
		{
			if (GUIManager.IsHeadless() || !IsValidKey(modKey) || revision <= 0)
			{
				return;
			}
			string text = null;
			try
			{
				text = RecordPath;
				ConfigFile val = OpenRecord();
				ConfigEntry<int> val2 = BindSeen(val, modKey);
				if (val2.Value < revision)
				{
					val2.Value = revision;
					val.Save();
				}
			}
			catch (Exception ex)
			{
				ModLogger.LogWarning("Could not write the first-run record at " + text + ": " + ex.Message);
			}
		}

		internal static bool QueueFirstRunPopup(string modKey, int revision, ConfigEntry<FirstRunMode> mode, int order, Func<bool> open, Func<bool> isOpen)
		{
			if (open == null || isOpen == null || !ShouldShow(modKey, revision, mode))
			{
				return false;
			}
			return ConfigUIStartupPopups.Enqueue(modKey + ".FirstRun", order, () => OpenFirstRun(modKey, revision, mode, open), isOpen);
		}

		private static bool OpenFirstRun(string modKey, int revision, ConfigEntry<FirstRunMode> mode, Func<bool> open)
		{
			if (!ShouldShow(modKey, revision, mode))
			{
				return false;
			}
			bool flag = false;
			if (mode != null && mode.Value == FirstRunMode.ShowNextLaunch)
			{
				mode.Value = FirstRunMode.Auto;
				flag = true;
			}
			bool flag2 = false;
			try
			{
				flag2 = open();
			}
			finally
			{
				if (!flag2 && flag)
				{
					mode.Value = FirstRunMode.ShowNextLaunch;
				}
			}
			return flag2;
		}

		internal static bool TakeLegacyEntry(ConfigFile cfg, string section, string key, out string raw)
		{
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: Expected O, but got Unknown
			//IL_006e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0078: Expected O, but got Unknown
			raw = null;
			if (cfg == null || string.IsNullOrEmpty(section) || string.IsNullOrEmpty(key))
			{
				return false;
			}
			bool saveOnConfigSet = cfg.SaveOnConfigSet;
			cfg.SaveOnConfigSet = false;
			try
			{
				ConfigDefinition val = new ConfigDefinition(section, key);
				if (cfg.ContainsKey(val))
				{
					raw = cfg[val].GetSerializedValue();
				}
				else
				{
					string value = cfg.Bind<string>(val, "\u0001", new ConfigDescription("Retired.", (AcceptableValueBase)null, Array.Empty<object>())).Value;
					raw = ((value == "\u0001") ? null : value);
				}
				cfg.Remove(val);
				if (saveOnConfigSet)
				{
					cfg.Save();
				}
			}
			catch (Exception ex)
			{
				ModLogger.LogWarning("Could not read the retired setting [" + section + "] " + key + ": " + ex.Message);
				raw = null;
			}
			finally
			{
				cfg.SaveOnConfigSet = saveOnConfigSet;
			}
			return raw != null;
		}

		private static ConfigFile OpenRecord()
		{
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Expected O, but got Unknown
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Expected O, but got Unknown
			BepInPlugin val = new BepInPlugin("ModQuickConfig.FirstRun", "ModQuickConfig first-run record - delete this file to see every mod's first-run popup again", "1.0.0");
			return new ConfigFile(RecordPath, false, val)
			{
				SaveOnConfigSet = false
			};
		}

		private static ConfigEntry<int> BindSeen(ConfigFile record, string modKey)
		{
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Expected O, but got Unknown
			return record.Bind<int>("FirstRun", modKey, 0, new ConfigDescription("The newest revision of this mod's first-run popup you have seen. 0 has not seen it; delete the line to see it again.", (AcceptableValueBase)null, Array.Empty<object>()));
		}

		private static FirstRunMode ModeOf(ConfigEntry<FirstRunMode> mode)
		{
			if (mode == null)
			{
				return FirstRunMode.Auto;
			}
			FirstRunMode value = mode.Value;
			return Enum.IsDefined(typeof(FirstRunMode), value) ? value : FirstRunMode.Auto;
		}

		private static bool IsValidKey(string modKey)
		{
			if (string.IsNullOrEmpty(modKey) || modKey.Trim() != modKey || modKey.IndexOfAny(InvalidKeyChars) >= 0)
			{
				ModLogger.LogError("'" + modKey + "' cannot be a first-run record key; use a plain identifier.");
				return false;
			}
			return true;
		}
	}
	internal static class ConfigUILauncher
	{
		private static Component cachedBroker;

		private static MethodInfo cachedRegister;

		private static MethodInfo cachedUnregister;

		private static bool loggedOwner;

		internal static bool IsAvailable => (Object)(object)Resolve() != (Object)null;

		internal static void Init()
		{
			Resolve();
		}

		internal static bool Register(string displayName, Action openPanel)
		{
			if (string.IsNullOrEmpty(displayName) || openPanel == null)
			{
				return false;
			}
			Component val = Resolve();
			if ((Object)(object)val == (Object)null)
			{
				return false;
			}
			if (cachedRegister == null)
			{
				ModLogger.LogError("The QuickConfig launcher on this machine has no compatible Register method; '" + displayName + "' will not appear in it.");
				return false;
			}
			try
			{
				cachedRegister.Invoke(val, new object[2] { displayName, openPanel });
				return true;
			}
			catch (Exception ex)
			{
				ModLogger.LogError("Could not register '" + displayName + "' with the QuickConfig launcher: " + ex.Message);
				return false;
			}
		}

		internal static void Unregister(string displayName)
		{
			if (string.IsNullOrEmpty(displayName))
			{
				return;
			}
			Component val = Resolve();
			if ((Object)(object)val == (Object)null || cachedUnregister == null)
			{
				return;
			}
			try
			{
				cachedUnregister.Invoke(val, new object[1] { displayName });
			}
			catch (Exception ex)
			{
				ModLogger.LogWarning("Could not unregister '" + displayName + "' from the QuickConfig launcher: " + ex.Message);
			}
		}

		private static Component Resolve()
		{
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: Expected O, but got Unknown
			if ((Object)(object)cachedBroker != (Object)null)
			{
				return cachedBroker;
			}
			cachedRegister = null;
			cachedUnregister = null;
			GameObject val = null;
			try
			{
				val = GameObject.Find("ModQuickConfigLauncher");
			}
			catch (Exception)
			{
			}
			if ((Object)(object)val == (Object)null)
			{
				try
				{
					val = new GameObject("ModQuickConfigLauncher");
					Object.DontDestroyOnLoad((Object)(object)val);
					val.AddComponent<QuickConfigBroker>();
				}
				catch (Exception ex2)
				{
					ModLogger.LogError("Could not create the QuickConfig launcher: " + ex2.Message);
					return null;
				}
			}
			Component val2 = null;
			Component[] components = val.GetComponents<Component>();
			foreach (Component val3 in components)
			{
				if ((Object)(object)val3 != (Object)null && ((object)val3).GetType().Name == "QuickConfigBroker")
				{
					val2 = val3;
					break;
				}
			}
			if ((Object)(object)val2 == (Object)null)
			{
				return null;
			}
			Type type = ((object)val2).GetType();
			cachedRegister = type.GetMethod("Register", BindingFlags.Instance | BindingFlags.Public, null, new Type[2]
			{
				typeof(string),
				typeof(Action)
			}, null);
			cachedUnregister = type.GetMethod("Unregister", BindingFlags.Instance | BindingFlags.Public, null, new Type[1] { typeof(string) }, null);
			if (!loggedOwner)
			{
				loggedOwner = true;
				int num = 1;
				try
				{
					PropertyInfo property = type.GetProperty("BrokerVersion", BindingFlags.Instance | BindingFlags.Public);
					if (property != null)
					{
						num = (int)property.GetValue(val2, null);
					}
				}
				catch (Exception)
				{
				}
				ModLogger.LogInfo($"QuickConfig launcher v{num} owned by " + $"{type.Assembly.GetName().Name}; this mod carries v{2}.");
				if (num < 2)
				{
					ModLogger.LogInfo("That copy is older than this one. Registration still works; the launcher UI is whatever the owning mod shipped.");
				}
			}
			cachedBroker = val2;
			return cachedBroker;
		}
	}
	internal static class ConfigUIPicker
	{
		[CompilerGenerated]
		private static class <>O
		{
			public static UnityAction <0>__Close;
		}

		private const float PanelW = 420f;

		private const float PanelH = 520f;

		private const float RowH = 30f;

		private const int MaxRows = 400;

		private static GameObject overlay;

		internal static bool IsOpen => (Object)(object)overlay != (Object)null;

		internal static void ShowPicker(string title, IList<string> options, string current, Action<string> onPick)
		{
			//IL_00e1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e7: Expected O, but got Unknown
			//IL_00e8: 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_0100: Unknown result type (might be due to invalid IL or missing references)
			//IL_010c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0137: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b4: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c3: Unknown result type (might be due to invalid IL or missing references)
			//IL_01fe: Unknown result type (might be due to invalid IL or missing references)
			//IL_016b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0170: Unknown result type (might be due to invalid IL or missing references)
			//IL_0176: Expected O, but got Unknown
			//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_026e: Expected O, but got Unknown
			Close();
			if (GUIManager.Instance == null || (Object)(object)GUIManager.CustomGUIFront == (Object)null)
			{
				return;
			}
			List<string> all = new List<string>();
			if (options != null)
			{
				foreach (string option in options)
				{
					if (!string.IsNullOrEmpty(option))
					{
						all.Add(option);
					}
				}
			}
			all.Sort(StringComparer.OrdinalIgnoreCase);
			overlay = ConfigUI.NewUI("ConfigUIPickerOverlay", GUIManager.CustomGUIFront.transform, typeof(Image));
			RectTransform val = (RectTransform)overlay.transform;
			val.anchorMin = Vector2.zero;
			val.anchorMax = Vector2.one;
			val.offsetMin = Vector2.zero;
			val.offsetMax = Vector2.zero;
			Image component = overlay.GetComponent<Image>();
			((Graphic)component).color = new Color(0f, 0f, 0f, 0.35f);
			Button val2 = overlay.AddComponent<Button>();
			((Selectable)val2).transition = (Transition)0;
			ButtonClickedEvent onClick = val2.onClick;
			object obj = <>O.<0>__Close;
			if (obj == null)
			{
				UnityAction val3 = Close;
				<>O.<0>__Close = val3;
				obj = (object)val3;
			}
			((UnityEvent)onClick).AddListener((UnityAction)obj);
			overlay.AddComponent<ConfigUI.ConfigUIInputGuard>().Hold();
			GameObject val4 = GUIManager.Instance.CreateWoodpanel(overlay.transform, new Vector2(0.5f, 0.5f), new Vector2(0.5f, 0.5f), new Vector2(0f, 0f), 420f, 520f, true);
			ConfigUI.AddText(val4.transform, 0f, 14f, 420f, 30f, title, 18, (TextAnchor)4, GUIManager.Instance.ValheimYellow);
			InputField val5 = ConfigUI.AddTextField(val4.transform, 16f, 52f, 320f, "", null, (ContentType)0, "Filter...");
			Transform transform = val4.transform;
			object obj2 = <>O.<0>__Close;
			if (obj2 == null)
			{
				UnityAction val6 = Close;
				<>O.<0>__Close = val6;
				obj2 = (object)val6;
			}
			ConfigUI.AddButton(transform, 344f, 52f, 60f, "Close", (UnityAction)obj2, 28f);
			ConfigUI.CreateScroll(val4.transform, 16f, 92f, 388f, 404f, out var content, out var contentW);
			if ((Object)(object)content == (Object)null)
			{
				return;
			}
			Action<string> rebuild = null;
			rebuild = delegate(string needle)
			{
				//IL_0016: Unknown result type (might be due to invalid IL or missing references)
				//IL_001c: Expected O, but got Unknown
				//IL_00f3: Unknown result type (might be due to invalid IL or missing references)
				//IL_0102: Expected O, but got Unknown
				foreach (Transform item in content)
				{
					Transform val7 = item;
					Object.Destroy((Object)(object)((Component)val7).gameObject);
				}
				int num = 0;
				foreach (string item2 in all)
				{
					if (string.IsNullOrEmpty(needle) || item2.IndexOf(needle, StringComparison.OrdinalIgnoreCase) >= 0)
					{
						if (num >= 400)
						{
							break;
						}
						num++;
						string picked = item2;
						GameObject val8 = ConfigUI.NewLayoutRow(content, contentW, 30f);
						ConfigUI.AddButton(val8.transform, 0f, 0f, contentW, picked, (UnityAction)delegate
						{
							Close();
							onPick?.Invoke(picked);
						}, 28f);
					}
				}
				if (num == 0)
				{
					GameObject val9 = ConfigUI.NewLayoutRow(content, contentW, 30f);
					ConfigUI.AddText(val9.transform, 4f, 0f, contentW, 30f, "No matches", 14, (TextAnchor)3);
				}
				else if (num >= 400)
				{
					GameObject val10 = ConfigUI.NewLayoutRow(content, contentW, 30f);
					ConfigUI.AddText(val10.transform, 4f, 0f, contentW, 30f, $"... {all.Count - num} more, type to narrow", 13, (TextAnchor)3);
				}
			};
			((UnityEvent<string>)(object)val5.onValueChanged).AddListener((UnityAction<string>)delegate(string needle)
			{
				rebuild(needle);
			});
			rebuild("");
		}

		internal static void Close()
		{
			if (!((Object)(object)overlay == (Object)null))
			{
				Object.Destroy((Object)(object)overlay);
				overlay = null;
			}
		}
	}
	internal static class ConfigUIPrompt
	{
		[CompilerGenerated]
		private static class <>O
		{
			public static UnityAction <0>__Close;
		}

		private const float PanelW = 460f;

		private static GameObject overlay;

		internal static void Show(string title, string label, string initial, string warning, Action<string> onAccept)
		{
			//IL_008e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0095: Expected O, but got Unknown
			//IL_0097: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00be: 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_011b: Unknown result type (might be due to invalid IL or missing references)
			//IL_012a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0139: Unknown result type (might be due to invalid IL or missing references)
			//IL_0170: Unknown result type (might be due to invalid IL or missing references)
			//IL_01bc: Unknown result type (might be due to invalid IL or missing references)
			//IL_028a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0299: Expected O, but got Unknown
			//IL_0255: Unknown result type (might be due to invalid IL or missing references)
			//IL_025a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0260: Expected O, but got Unknown
			Close();
			if (GUIManager.Instance != null && !((Object)(object)GUIManager.CustomGUIFront == (Object)null))
			{
				bool flag = !string.IsNullOrEmpty(warning);
				float num = (flag ? 96f : 0f);
				float num2 = 190f + num;
				overlay = ConfigUI.NewUI("ConfigUIPromptOverlay", GUIManager.CustomGUIFront.transform, typeof(Image));
				RectTransform val = (RectTransform)overlay.transform;
				val.anchorMin = Vector2.zero;
				val.anchorMax = Vector2.one;
				val.offsetMin = Vector2.zero;
				val.offsetMax = Vector2.zero;
				((Graphic)overlay.GetComponent<Image>()).color = new Color(0f, 0f, 0f, 0.45f);
				overlay.AddComponent<ConfigUI.ConfigUIInputGuard>().Hold();
				GameObject val2 = GUIManager.Instance.CreateWoodpanel(overlay.transform, new Vector2(0.5f, 0.5f), new Vector2(0.5f, 0.5f), new Vector2(0f, 0f), 460f, num2, true);
				ConfigUI.AddText(val2.transform, 0f, 14f, 460f, 30f, title, 18, (TextAnchor)4, GUIManager.Instance.ValheimYellow);
				float num3 = 56f;
				if (flag)
				{
					ConfigUI.AddText(val2.transform, 20f, num3, 420f, num - 8f, warning, 13, (TextAnchor)0, (Color?)new Color(0.98f, 0.75f, 0.14f));
					num3 += num;
				}
				ConfigUI.AddText(val2.transform, 20f, num3, 120f, 28f, label, 15, (TextAnchor)3);
				InputField field = ConfigUI.AddTextField(val2.transform, 140f, num3, 300f, initial, null, (ContentType)0, null, 64);
				num3 += 46f;
				Transform transform = val2.transform;
				float y = num3;
				object obj = <>O.<0>__Close;
				if (obj == null)
				{
					UnityAction val3 = Close;
					<>O.<0>__Close = val3;
					obj = (object)val3;
				}
				ConfigUI.AddButton(transform, 20f, y, 190f, "Cancel", (UnityAction)obj, 34f);
				ConfigUI.AddButton(val2.transform, 250f, num3, 190f, "OK", (UnityAction)delegate
				{
					string text = field.text;
					Close();
					onAccept?.Invoke(text);
				}, 34f);
				((Selectable)field).Select();
				field.ActivateInputField();
			}
		}

		internal static void Close()
		{
			if (!((Object)(object)overlay == (Object)null))
			{
				Object.Destroy((Object)(object)overlay);
				overlay = null;
			}
		}
	}
	internal static class ConfigUIStartupPopups
	{
		internal const int OrderWelcome = 100;

		internal const int OrderNotice = 200;

		private static Component cachedQueue;

		private static MethodInfo cachedEnqueue;

		private static MethodInfo cachedCancel;

		private static bool loggedOwner;

		internal static bool Enqueue(string key, int order, Func<bool> tryOpen, Func<bool> isOpen)
		{
			if (string.IsNullOrEmpty(key) || tryOpen == null || isOpen == null)
			{
				return false;
			}
			if (GUIManager.IsHeadless())
			{
				return false;
			}
			Component val = Resolve();
			if ((Object)(object)val == (Object)null)
			{
				return false;
			}
			if (cachedEnqueue == null)
			{
				ModLogger.LogError("The startup popup queue on this machine has no compatible Enqueue method; '" + key + "' will not be shown.");
				return false;
			}
			try
			{
				return (bool)cachedEnqueue.Invoke(val, new object[4] { key, order, tryOpen, isOpen });
			}
			catch (Exception ex)
			{
				ModLogger.LogError("Could not queue the startup popup '" + key + "': " + ex.Message);
				return false;
			}
		}

		internal static void Cancel(string key)
		{
			if (string.IsNullOrEmpty(key) || GUIManager.IsHeadless())
			{
				return;
			}
			Component val = Resolve();
			if ((Object)(object)val == (Object)null || cachedCancel == null)
			{
				return;
			}
			try
			{
				cachedCancel.Invoke(val, new object[1] { key });
			}
			catch (Exception ex)
			{
				ModLogger.LogWarning("Could not cancel the startup popup '" + key + "': " + ex.Message);
			}
		}

		private static Component Resolve()
		{
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: Expected O, but got Unknown
			if ((Object)(object)cachedQueue != (Object)null)
			{
				return cachedQueue;
			}
			cachedEnqueue = null;
			cachedCancel = null;
			GameObject val = null;
			try
			{
				val = GameObject.Find("ModStartupPopupQueue");
			}
			catch (Exception)
			{
			}
			if ((Object)(object)val == (Object)null)
			{
				try
				{
					val = new GameObject("ModStartupPopupQueue");
					Object.DontDestroyOnLoad((Object)(object)val);
					val.AddComponent<StartupPopupQueue>();
				}
				catch (Exception ex2)
				{
					ModLogger.LogError("Could not create the startup popup queue: " + ex2.Message);
					return null;
				}
			}
			Component val2 = null;
			Component[] components = val.GetComponents<Component>();
			foreach (Component val3 in components)
			{
				if ((Object)(object)val3 != (Object)null && ((object)val3).GetType().Name == "StartupPopupQueue")
				{
					val2 = val3;
					break;
				}
			}
			if ((Object)(object)val2 == (Object)null)
			{
				try
				{
					val2 = (Component)(object)val.AddComponent<StartupPopupQueue>();
				}
				catch (Exception ex3)
				{
					ModLogger.LogError("Could not create the startup popup queue: " + ex3.Message);
					return null;
				}
			}
			Type type = ((object)val2).GetType();
			cachedEnqueue = type.GetMethod("Enqueue", BindingFlags.Instance | BindingFlags.Public, null, new Type[4]
			{
				typeof(string),
				typeof(int),
				typeof(Func<bool>),
				typeof(Func<bool>)
			}, null);
			cachedCancel = type.GetMethod("Cancel", BindingFlags.Instance | BindingFlags.Public, null, new Type[1] { typeof(string) }, null);
			if (!loggedOwner)
			{
				loggedOwner = true;
				int num = 0;
				try
				{
					PropertyInfo property = type.GetProperty("QueueVersion", BindingFlags.Instance | BindingFlags.Public);
					if (property != null)
					{
						num = (int)property.GetValue(val2, null);
					}
				}
				catch (Exception)
				{
				}
				ModLogger.LogInfo($"Startup popup queue v{num} owned by " + $"{type.Assembly.GetName().Name}; this mod carries v{1}.");
			}
			cachedQueue = val2;
			return cachedQueue;
		}
	}
	internal class QuickConfigBroker : MonoBehaviour
	{
		internal const string BrokerObjectName = "ModQuickConfigLauncher";

		internal const string BrokerTypeName = "QuickConfigBroker";

		internal const string ButtonObjectName = "ModQuickConfigButton";

		internal const int ContractVersion = 2;

		private const float ButtonW = 150f;

		private const float ButtonH = 38f;

		private const float PanelW = 320f;

		private static QuickConfigBroker Instance;

		private static Harmony harmony;

		private readonly Dictionary<string, Action> entries = new Dictionary<string, Action>(StringComparer.Ordinal);

		private readonly List<string> order = new List<string>();

		private GameObject mainMenuButton;

		private GameObject pauseButton;

		private GameObject listPanel;

		public int BrokerVersion => 2;

		public void Register(string modName, Action openPanel)
		{
			if (!string.IsNullOrEmpty(modName) && openPanel != null)
			{
				if (!entries.ContainsKey(modName))
				{
					order.Add(modName);
				}
				entries[modName] = openPanel;
				RefreshVisibility();
			}
		}

		public void Unregister(string modName)
		{
			if (!string.IsNullOrEmpty(modName))
			{
				if (entries.Remove(modName))
				{
					order.Remove(modName);
				}
				if ((Object)(object)listPanel != (Object)null)
				{
					CloseList();
				}
				RefreshVisibility();
			}
		}

		public bool IsRegistered(string modName)
		{
			return !string.IsNullOrEmpty(modName) && entries.ContainsKey(modName);
		}

		public void Awake()
		{
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			//IL_005d: Expected O, but got Unknown
			//IL_0089: Unknown result type (might be due to invalid IL or missing references)
			//IL_0096: Expected O, but got Unknown
			//IL_00c2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d0: Expected O, but got Unknown
			Instance = this;
			GUIManager.OnCustomGUIAvailable += OnCustomGUIAvailable;
			SynchronizationManager.OnAdminStatusChanged += OnAdminStatusChanged;
			SynchronizationManager.OnConfigurationSynchronized += OnConfigurationSynchronized;
			try
			{
				if (harmony == null)
				{
					harmony = new Harmony("ModQuickConfigLauncher.broker");
					harmony.Patch((MethodBase)AccessTools.Method(typeof(Menu), "Start", (Type[])null, (Type[])null), (HarmonyMethod)null, new HarmonyMethod(typeof(QuickConfigBroker), "OnMenuStart", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
					harmony.Patch((MethodBase)AccessTools.Method(typeof(Menu), "Update", (Type[])null, (Type[])null), new HarmonyMethod(typeof(QuickConfigBroker), "OnMenuUpdate", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				}
			}
			catch (Exception ex)
			{
				ModLogger.LogWarning("QuickConfig launcher could not patch Menu: " + ex.Message);
			}
			TryBuildButtons();
		}

		public void OnDestroy()
		{
			GUIManager.OnCustomGUIAvailable -= OnCustomGUIAvailable;
			SynchronizationManager.OnAdminStatusChanged -= OnAdminStatusChanged;
			SynchronizationManager.OnConfigurationSynchronized -= OnConfigurationSynchronized;
			if ((Object)(object)Instance == (Object)(object)this)
			{
				Instance = null;
			}
		}

		private void OnAdminStatusChanged()
		{
			RefreshVisibility();
		}

		private void OnConfigurationSynchronized(object sender, EventArgs e)
		{
			RefreshVisibility();
		}

		private static void OnMenuStart(Menu __instance)
		{
			if (!((Object)(object)Instance == (Object)null))
			{
				Instance.EnsurePauseButton(__instance);
			}
		}

		private static bool OnMenuUpdate()
		{
			if ((Object)(object)Instance == (Object)null || (Object)(object)Instance.listPanel == (Object)null)
			{
				return true;
			}
			if (!ZInput.GetKeyDown((KeyCode)27, true))
			{
				return true;
			}
			Instance.CloseList();
			return false;
		}

		public void Update()
		{
			if (!((Object)(object)listPanel == (Object)null) && !((Object)(object)Menu.instance != (Object)null) && Input.GetKeyDown((KeyCode)27))
			{
				CloseList();
			}
		}

		private void OnCustomGUIAvailable()
		{
			TryBuildButtons();
		}

		private void TryBuildButtons()
		{
			if (!GUIManager.IsHeadless() && GUIManager.Instance != null)
			{
				if ((Object)(object)FejdStartup.instance != (Object)null && (Object)(object)GUIManager.CustomGUIFront != (Object)null)
				{
					mainMenuButton = EnsureCornerButton(mainMenuButton, GUIManager.CustomGUIFront.transform);
				}
				if ((Object)(object)Menu.instance != (Object)null)
				{
					EnsurePauseButton(Menu.instance);
				}
				RefreshVisibility();
			}
		}

		private void EnsurePauseButton(Menu menu)
		{
			if (!((Object)(object)menu == (Object)null) && !((Object)(object)menu.m_root == (Object)null))
			{
				pauseButton = EnsureCornerButton(pauseButton, menu.m_root);
				RefreshVisibility();
			}
		}

		private GameObject EnsureCornerButton(GameObject existing, Transform parent)
		{
			//IL_0093: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a2: 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_00d8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00de: Expected O, but got Unknown
			//IL_00e9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ff: Unknown result type (might be due to invalid IL or missing references)
			//IL_011c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0126: Expected O, but got Unknown
			if (GUIManager.IsHeadless() || GUIManager.Instance == null || (Object)(object)parent == (Object)null)
			{
				return existing;
			}
			if ((Object)(object)existing != (Object)null && (Object)(object)existing.transform.parent == (Object)(object)parent)
			{
				return existing;
			}
			Transform val = parent.Find("ModQuickConfigButton");
			if ((Object)(object)val != (Object)null)
			{
				return ((Component)val).gameObject;
			}
			GameObject val2 = GUIManager.Instance.CreateButton(ConfigUI.L("Mod Config"), parent, new Vector2(1f, 0f), new Vector2(1f, 0f), new Vector2(-20f, 20f), 150f, 38f);
			((Object)val2).name = "ModQuickConfigButton";
			RectTransform val3 = (RectTransform)val2.transform;
			val3.pivot = new Vector2(1f, 0f);
			val3.anchoredPosition = new Vector2(-20f, 20f);
			((UnityEvent)val2.GetComponent<Button>().onClick).AddListener(new UnityAction(OpenList));
			return val2;
		}

		private static bool CanConfigure()
		{
			if (GUIManager.IsHeadless() || GUIManager.Instance == null)
			{
				return false;
			}
			if ((Object)(object)ZNet.instance == (Object)null)
			{
				return true;
			}
			if (ZNet.instance.IsServer())
			{
				return true;
			}
			return SynchronizationManager.Instance != null && SynchronizationManager.Instance.PlayerIsAdmin;
		}

		private void RefreshVisibility()
		{
			bool flag = entries.Count > 0 && CanConfigure();
			if ((Object)(object)mainMenuButton != (Object)null)
			{
				mainMenuButton.SetActive(flag && (Object)(object)FejdStartup.instance != (Object)null);
			}
			if ((Object)(object)pauseButton != (Object)null)
			{
				pauseButton.SetActive(flag);
			}
			if (!flag && (Object)(object)listPanel != (Object)null)
			{
				CloseList();
			}
		}

		private void OpenList()
		{
			//IL_0094: Unknown result type (might be due to invalid IL or missing references)
			//IL_009e: Expected O, but got Unknown
			//IL_00f2: Unknown result type (might be due to invalid IL or missing references)
			//IL_0101: Expected O, but got Unknown
			CloseList();
			if (!CanConfigure())
			{
				return;
			}
			if (order.Count == 1)
			{
				Invoke(order[0]);
				return;
			}
			float h = Mathf.Clamp(80f + 42f * (float)order.Count, 122f, 560f);
			listPanel = ConfigUI.CreatePanel("Configure a mod", 320f, h, out var body);
			ConfigUI.AddCloseX(body, 320f, new UnityAction(CloseList));
			float num = 64f;
			foreach (string item in order)
			{
				string entry = item;
				ConfigUI.AddButton(body, 20f, num, 280f, entry, (UnityAction)delegate
				{
					CloseList();
					Invoke(entry);
				});
				num += 42f;
			}
		}

		private void CloseList()
		{
			if (!((Object)(object)listPanel == (Object)null))
			{
				Object.Destroy((Object)(object)listPanel);
				listPanel = null;
			}
		}

		private void Invoke(string modName)
		{
			if (!entries.TryGetValue(modName, out var value) || value == null)
			{
				return;
			}
			try
			{
				value();
			}
			catch (Exception arg)
			{
				ModLogger.LogError($"The config panel for '{modName}' failed to open: {arg}");
			}
		}
	}
	internal class StartupPopupQueue : MonoBehaviour
	{
		private sealed class Entry
		{
			internal string Key;

			internal int Order;

			internal long Sequence;

			internal Func<bool> TryOpen;

			internal Func<bool> IsOpen;
		}

		internal const string QueueObjectName = "ModStartupPopupQueue";

		internal const string QueueTypeName = "StartupPopupQueue";

		internal const string PanelGuardTypeName = "ConfigUIInputGuard";

		internal const int ContractVersion = 1;

		private const float FirstPopupDelay = 1f;

		private const float BetweenPopupsDelay = 0.5f;

		private readonly List<Entry> pending = new List<Entry>();

		private readonly List<MonoBehaviour> componentBuffer = new List<MonoBehaviour>();

		private Entry current;

		private long nextSequence;

		private float readySince = -1f;

		private bool shownThisVisit;

		private FejdStartup visit;

		public int QueueVersion => 1;

		public bool Enqueue(string key, int order, Func<bool> tryOpen, Func<bool> isOpen)
		{
			if (string.IsNullOrEmpty(key) || tryOpen == null || isOpen == null)
			{
				return false;
			}
			if (current != null && string.Equals(current.Key, key, StringComparison.Ordinal))
			{
				return false;
			}
			Entry entry = FindPending(key);
			if (entry != null)
			{
				entry.Order = order;
				entry.TryOpen = tryOpen;
				entry.IsOpen = isOpen;
				return true;
			}
			pending.Add(new Entry
			{
				Key = key,
				Order = order,
				Sequence = nextSequence++,
				TryOpen = tryOpen,
				IsOpen = isOpen
			});
			return true;
		}

		public void Cancel(string key)
		{
			Entry entry = FindPending(key);
			if (entry != null)
			{
				pending.Remove(entry);
			}
		}

		public bool IsQueued(string key)
		{
			if (string.IsNullOrEmpty(key))
			{
				return false;
			}
			if (current != null && string.Equals(current.Key, key, StringComparison.Ordinal))
			{
				return true;
			}
			return FindPending(key) != null;
		}

		public void Update()
		{
			if (current == null && pending.Count == 0)
			{
				return;
			}
			FejdStartup instance = FejdStartup.instance;
			if ((Object)(object)instance == (Object)null)
			{
				current = null;
				readySince = -1f;
				return;
			}
			if ((Object)(object)instance != (Object)(object)visit)
			{
				visit = instance;
				shownThisVisit = false;
				readySince = -1f;
			}
			if (current != null)
			{
				if (StillOpen(current))
				{
					return;
				}
				current = null;
				readySince = -1f;
			}
			if (pending.Count == 0)
			{
				return;
			}
			if (!MainMenuReady(instance))
			{
				readySince = -1f;
				return;
			}
			float unscaledTime = Time.unscaledTime;
			if (readySince < 0f)
			{
				readySince = unscaledTime;
			}
			else
			{
				if (unscaledTime - readySince < (shownThisVisit ? 0.5f : 1f))
				{
					return;
				}
				while (pending.Count > 0)
				{
					Entry entry = TakeFirst();
					if (TryOpen(entry))
					{
						current = entry;
						shownThisVisit = true;
						readySince = -1f;
						break;
					}
				}
			}
		}

		private bool MainMenuReady(FejdStartup startup)
		{
			return !CinematicsManager.IsStartedPlaying() && (Object)(object)startup.m_mainMenu != (Object)null && startup.m_mainMenu.activeInHierarchy && (Object)(object)startup.m_menuList != (Object)null && startup.m_menuList.activeInHierarchy && ((Object)(object)startup.m_connectionFailedPanel == (Object)null || !startup.m_connectionFailedPanel.activeInHierarchy) && !UnifiedPopup.IsVisible() && (Object)(object)GUIManager.CustomGUIFront != (Object)null && !KitPanelOpen();
		}

		private bool KitPanelOpen()
		{
			Transform transform = GUIManager.CustomGUIFront.transform;
			for (int i = 0; i < transform.childCount; i++)
			{
				Transform child = transform.GetChild(i);
				if (!((Component)child).gameObject.activeSelf)
				{
					continue;
				}
				((Component)child).GetComponents<MonoBehaviour>(componentBuffer);
				foreach (MonoBehaviour item in componentBuffer)
				{
					if ((Object)(object)item != (Object)null && ((object)item).GetType().Name == "ConfigUIInputGuard")
					{
						return true;
					}
				}
			}
			return false;
		}

		private Entry FindPending(string key)
		{
			if (string.IsNullOrEmpty(key))
			{
				return null;
			}
			foreach (Entry item in pending)
			{
				if (string.Equals(item.Key, key, StringComparison.Ordinal))
				{
					return item;
				}
			}
			return null;
		}

		private Entry TakeFirst()
		{
			Entry entry = pending[0];
			foreach (Entry item in pending)
			{
				if (item.Order < entry.Order || (item.Order == entry.Order && item.Sequence < entry.Sequence))
				{
					entry = item;
				}
			}
			pending.Remove(entry);
			return entry;
		}

		private static bool TryOpen(Entry entry)
		{
			bool flag = false;
			try
			{
				flag = entry.TryOpen();
			}
			catch (Exception arg)
			{
				ModLogger.LogError($"The startup popup '{entry.Key}' failed to open: {arg}");
			}
			if (flag)
			{
				return true;
			}
			return StillOpen(entry);
		}

		private static bool StillOpen(Entry entry)
		{
			try
			{
				return entry.IsOpen();
			}
			catch (Exception ex)
			{
				ModLogger.LogWarning("Could not tell whether the startup popup '" + entry.Key + "' is still open, treating it as closed: " + ex.Message);
				return false;
			}
		}
	}
	[Serializable]
	public class RecipeRequirementConfig
	{
		public string item = "";

		public int amount = 1;
	}
	[Serializable]
	public class RecipeConfig
	{
		public string name = "";

		public string item = "";

		public int amount = 1;

		public string craftingStation = "";

		public int minStationLevel = 1;

		public bool enabled = true;

		public string repairStation = "";

		public List<RecipeRequirementConfig> resources = new List<RecipeRequirementConfig>();
	}
	[Serializable]
	public class RecipesConfig
	{
		public List<RecipeConfig> recipes = new List<RecipeConfig>();
	}
	public static class GameObjectExtensions
	{
		public static RectTransform RectTransform(this GameObject go)
		{
			Transform transform = go.transform;
			return (RectTransform)(object)((transform is RectTransform) ? transform : null);
		}

		public static T RequireComponent<T>(this GameObject go) where T : Component
		{
			T component = go.GetComponent<T>();
			return ((Object)(object)component == (Object)null) ? go.AddComponent<T>() : component;
		}
	}
	public class GotDestroyed : MonoBehaviour
	{
		public void OnDisable()
		{
			Debug.LogError((object)("I got destroyed! (" + ((Object)((Component)this).gameObject).name + ")"));
			Debug.Log((object)Environment.StackTrace);
		}
	}
	public static class AssetBundleLoader
	{
		private static readonly List<Stream> _liveStreams = new List<Stream>();

		public static AssetBundle LoadFromResources(string filename, Assembly assembly)
		{
			if (assembly == null)
			{
				throw new ArgumentNullException("assembly");
			}
			string text = assembly.GetName().Name + "." + filename;
			try
			{
				Stream manifestResourceStream = assembly.GetManifestResourceStream(text);
				if (manifestResourceStream == null)
				{
					ModLogger.LogError("Embedded asset bundle '" + text + "' not found in " + assembly.GetName().Name + ".");
					return null;
				}
				AssetBundle val = AssetBundle.LoadFromStream(manifestResourceStream);
				if ((Object)(object)val == (Object)null)
				{
					manifestResourceStream.Dispose();
					ModLogger.LogError("Embedded asset bundle '" + text + "' failed to load.");
					return null;
				}
				_liveStreams.Add(manifestResourceStream);
				return val;
			}
			catch (Exception ex)
			{
				ModLogger.LogError("Failed to load embedded asset bundle '" + text + "': " + ex.Message);
				return null;
			}
		}

		public static AssetBundle LoadIntoContext(string filename, Assembly assembly)
		{
			AssetBundle val = LoadFromResources(filename, assembly);
			if ((Object)(object)val != (Object)null)
			{
				ModContext.AssetBundle = val;
			}
			return val;
		}
	}
	public static class AudioMixerMocks
	{
		public static void Resolve(GameObject root, string fallbackGroup = null)
		{
			if ((Object)(object)root == (Object)null)
			{
				return;
			}
			AudioSource[] componentsInChildren = root.GetComponentsInChildren<AudioSource>(true);
			foreach (AudioSource val in componentsInChildren)
			{
				PrefabExtension.FixReferences((object)val);
				if ((Object)(object)val.outputAudioMixerGroup == (Object)null && fallbackGroup != null)
				{
					val.outputAudioMixerGroup = Cache.GetPrefab<AudioMixerGroup>(fallbackGroup);
				}
			}
		}
	}
	public static class ConfigBinder
	{
		private static ConfigFile Cfg => ModContext.Cfg;

		public static ConfigEntry<bool> BindServerConfig(string category, string key, bool value, string description, AcceptableValueBase acceptableValues = null, bool advanced = false)
		{
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: Expected O, but got Unknown
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Expected O, but got Unknown
			return Cfg.Bind<bool>(category, key, value, new ConfigDescription(description, acceptableValues, new object[1] { (object)new ConfigurationManagerAttributes
			{
				IsAdminOnly = true,
				IsAdvanced = advanced
			} }));
		}

		public static ConfigEntry<int> BindServerConfig(string category, string key, int value, string description, bool advanced = false, int valMin = 0, int valMax = 150)
		{
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Expected O, but got Unknown
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: Expected O, but got Unknown
			return Cfg.Bind<int>(category, key, value, new ConfigDescription(description, (AcceptableValueBase)(object)new AcceptableValueRange<int>(valMin, valMax), new object[1] { (object)new ConfigurationManagerAttributes
			{
				IsAdminOnly = true,
				IsAdvanced = advanced
			} }));
		}

		public static ConfigEntry<float> BindServerConfig(string category, string key, float value, string description, bool advanced = false, float valMin = 0f, float valMax = 150f)
		{
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Expected O, but got Unknown
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: Expected O, but got Unknown
			return Cfg.Bind<float>(category, key, value, new ConfigDescription(description, (AcceptableValueBase)(object)new AcceptableValueRange<float>(valMin, valMax), new object[1] { (object)new ConfigurationManagerAttributes
			{
				IsAdminOnly = true,
				IsAdvanced = advanced
			} }));
		}

		public static ConfigEntry<string> BindServerConfig(string category, string key, string value, string description, AcceptableValueList<string> acceptableValues = null, bool advanced = false)
		{
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: Expected O, but got Unknown
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Expected O, but got Unknown
			return Cfg.Bind<string>(category, key, value, new ConfigDescription(description, (AcceptableValueBase)(object)acceptableValues, new object[1] { (object)new ConfigurationManagerAttributes
			{
				IsAdminOnly = true,
				IsAdvanced = advanced
			} }));
		}

		public static ConfigEntry<T> BindServerConfig<T>(string category, string key, T value, string description, AcceptableValueBase acceptableValues, bool advanced = false)
		{
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: Expected O, but got Unknown
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Expected O, but got Unknown
			return Cfg.Bind<T>(category, key, value, new ConfigDescription(description, acceptableValues, new object[1] { (object)new ConfigurationManagerAttributes
			{
				IsAdminOnly = true,
				IsAdvanced = advanced
			} }));
		}

		public static ConfigEntry<T> BindClientConfig<T>(string category, string key, T value, string description, AcceptableValueBase acceptableValues = null, bool advanced = false)
		{
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: Expected O, but got Unknown
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Expected O, but got Unknown
			return Cfg.Bind<T>(category, key, value, new ConfigDescription(description, acceptableValues, new object[1] { (object)new ConfigurationManagerAttributes
			{
				IsAdminOnly = false,
				IsAdvanced = advanced
			} }));
		}

		public static ConfigEntry<T> BindServerConfigInOrder<T>(string category, string key, T value, string description, AcceptableValueBase acceptableValues = null, bool advanced = false)
		{
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Expected O, but got Unknown
			return ConfigFileExtensions.BindConfigInOrder<T>(Cfg, category, key, value, description, true, true, true, acceptableValues, (Action<ConfigEntryBase>)null, new ConfigurationManagerAttributes
			{
				IsAdvanced = advanced
			});
		}

		public static ConfigEntry<T> BindClientConfigInOrder<T>(string category, string key, T value, string description, AcceptableValueBase acceptableValues = null, bool advanced = false)
		{
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Expected O, but got Unknown
			return ConfigFileExtensions.BindConfigInOrder<T>(Cfg, category, key, value, description, false, true, true, acceptableValues, (Action<ConfigEntryBase>)null, new ConfigurationManagerAttributes
			{
				IsAdvanced = advanced
			});
		}
	}
	public static class ConfigChangeDebouncer
	{
		private static readonly Dictionary<object, Action> pendingActions = new Dictionary<object, Action>();

		private static readonly Dictionary<object, float> fireAt = new Dictionary<object, float>();

		private static readonly HashSet<object> running = new HashSet<object>();

		public static void Schedule(object key, Action action)
		{
			float num = ((ModContext.ConfigApplyDelay != null) ? ModContext.ConfigApplyDelay.Value : 0f);
			if (num <= 0f)
			{
				action();
				return;
			}
			ThreadingHelper instance = ThreadingHelper.Instance;
			if (!((Object)(object)instance == (Object)null))
			{
				pendingActions[key] = action;
				fireAt[key] = Time.realtimeSinceStartup + num;
				if (!running.Contains(key))
				{
					running.Add(key);
					((MonoBehaviour)instance).StartCoroutine(Run(key));
				}
			}
		}

		private static IEnumerator Run(object key)
		{
			float at;
			while (fireAt.TryGetValue(key, out at) && Time.realtimeSinceStartup < at)
			{
				yield return null;
			}
			pendingActions.TryGetValue(key, out var action);
			pendingActions.Remove(key);
			fireAt.Remove(key);
			running.Remove(key);
			action?.Invoke();
		}
	}
	internal static class ConfigDrawHelpers
	{
		private static readonly Dictionary<object, string> TextBuffer = new Dictionary<object, string>();

		private static readonly Dictionary<object, float> SliderPending = new Dictionary<object, float>();

		private static GUIStyle _headerButton;

		private static GUIStyle _groupLabel;

		private static GUIStyle _dim;

		internal static GUIStyle HeaderButton => _headerButton;

		internal static GUIStyle Dim => _dim;

		internal static void EnsureStyles()
		{
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Expected O, but got Unknown
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_0050: Expected O, but got Unknown
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0067: Unknown result type (might be due to invalid IL or missing references)
			//IL_0075: Expected O, but got Unknown
			if (_headerButton == null)
			{
				_headerButton = new GUIStyle(GUI.skin.button)
				{
					alignment = (TextAnchor)3,
					fontStyle = (FontStyle)1
				};
				_groupLabel = new GUIStyle(GUI.skin.label)
				{
					fontStyle = (FontStyle)1
				};
				_dim = new GUIStyle(GUI.skin.label)
				{
					fontStyle = (FontStyle)2,
					fontSize = 11
				};
			}
		}

		internal static ConfigurationManagerAttributes GetAttributes(ConfigEntryBase entry)
		{
			object result;
			if (entry == null)
			{
				result = null;
			}
			else
			{
				ConfigDescription description = entry.Description;
				result = ((description == null) ? null : description.Tags?.OfType<ConfigurationManagerAttributes>().FirstOrDefault());
			}
			return (ConfigurationManagerAttributes)result;
		}

		internal static void Hide(ConfigEntryBase entry)
		{
			ConfigurationManagerAttributes attributes = GetAttributes(entry);
			if (attributes != null)
			{
				attributes.Browsable = false;
			}
		}

		internal static void GroupHeader(string text)
		{
			GUILayout.Space(4f);
			GUILayout.Label(text, _groupLabel, Array.Empty<GUILayoutOption>());
		}

		private static bool EnterPressed()
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Invalid comparison between Unknown and I4
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Invalid comparison between Unknown and I4
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Invalid comparison between Unknown and I4
			return (int)Event.current.type == 4 && ((int)Event.current.keyCode == 13 || (int)Event.current.keyCode == 271);
		}

		internal static void DrawBool(string label, ConfigEntry<bool> cfg)
		{
			bool flag = GUILayout.Toggle(cfg.Value, " " + label, Array.Empty<GUILayoutOption>());
			if (flag != cfg.Value)
			{
				cfg.Value = flag;
			}
		}

		internal static void DrawString(string label, ConfigEntry<string> cfg)
		{
			GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
			GUILayout.Label(label, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(230f) });
			string text = "str_" + ((object)cfg).GetHashCode();
			GUI.SetNextControlName(text);
			bool flag = GUI.GetNameOfFocusedControl() == text;
			string value;
			string text2 = ((flag && TextBuffer.TryGetValue(cfg, out value)) ? value : cfg.Value);
			string text3 = GUILayout.TextField(text2, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(230f) });
			string value2;
			if (flag)
			{
				TextBuffer[cfg] = text3;
				if (EnterPressed() && cfg.Value != text3)
				{
					cfg.Value = text3;
				}
			}
			else if (TextBuffer.TryGetValue(cfg, out value2))
			{
				if (cfg.Value != value2)
				{
					cfg.Value = value2;
				}
				TextBuffer.Remove(cfg);
			}
			GUILayout.EndHorizontal();
		}

		internal static void DrawChoice(string label, ConfigEntry<string> cfg)
		{
			GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
			GUILayout.Label(label, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(230f) });
			if (((ConfigEntryBase)cfg).Description.AcceptableValues is AcceptableValueList<string> val && val.AcceptableValues.Length != 0)
			{
				string[] acceptableValues = val.AcceptableValues;
				int num = Array.IndexOf(acceptableValues, cfg.Value);
				if (num < 0)
				{
					num = 0;
				}
				if (GUILayout.Button("◄", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(26f) }))
				{
					cfg.Value = acceptableValues[(num - 1 + acceptableValues.Length) % acceptableValues.Length];
				}
				GUILayout.Label(cfg.Value, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(160f) });
				if (GUILayout.Button("►", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(26f) }))
				{
					cfg.Value = acceptableValues[(num + 1) % acceptableValues.Length];
				}
			}
			else
			{
				GUILayout.Label(cfg.Value, Array.Empty<GUILayoutOption>());
			}
			GUILayout.EndHorizontal();
		}

		internal static void DrawFloat(string label, ConfigEntry<float> cfg)
		{
			float num = 0f;
			float num2 = Mathf.Max(100f, cfg.Value);
			if (((ConfigEntryBase)cfg).Description.AcceptableValues is AcceptableValueRange<float> val)
			{
				num = val.MinValue;
				num2 = val.MaxValue;
			}
			float num3 = DrawSliderRow(label, cfg, cfg.Value, num, num2, isInt: false);
			if (num3 != cfg.Value)
			{
				cfg.Value = Mathf.Clamp(num3, num, num2);
			}
		}

		internal static void DrawInt(string label, ConfigEntry<int> cfg)
		{
			float num = 0f;
			float num2 = Mathf.Max(100f, (float)cfg.Value);
			if (((ConfigEntryBase)cfg).Description.AcceptableValues is AcceptableValueRange<int> val)
			{
				num = val.MinValue;
				num2 = val.MaxValue;
			}
			float num3 = DrawSliderRow(label, cfg, cfg.Value, num, num2, isInt: true);
			int num4 = Mathf.Clamp(Mathf.RoundToInt(num3), (int)num, (int)num2);
			if (num4 != cfg.Value)
			{
				cfg.Value = num4;
			}
		}

		private static float DrawSliderRow(string label, object key, float current, float min, float max, bool isInt)
		{
			float num = current;
			GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
			GUILayout.Label(label, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(230f) });
			float value;
			float num2 = (SliderPending.TryGetValue(key, out value) ? value : current);
			float num3 = GUILayout.HorizontalSlider(num2, min, max, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(150f) });
			if (Mathf.Abs(num3 - num2) > Mathf.Epsilon)
			{
				SliderPending[key] = num3;
			}
			if (SliderPending.TryGetValue(key, out var value2) && !Input.GetMouseButton(0))
			{
				SliderPending.Remove(key);
				num = value2;
			}
			float value3;
			float num4 = (SliderPending.TryGetValue(key, out value3) ? value3 : num);
			string text = "num_" + key.GetHashCode();
			GUI.SetNextControlName(text);
			bool flag = GUI.GetNameOfFocusedControl() == text;
			string text2 = (isInt ? Mathf.RoundToInt(num4).ToString() : num4.ToString("0.###"));
			string value4;
			string text3 = ((flag && TextBuffer.TryGetValue(key, out value4)) ? value4 : text2);
			string text4 = GUILayout.TextField(text3, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(70f) });
			string value5;
			if (flag)
			{
				TextBuffer[key] = text4;
				if (EnterPressed() && float.TryParse(text4, out var result))
				{
					num = result;
				}
			}
			else if (TextBuffer.TryGetValue(key, out value5))
			{
				if (float.TryParse(value5, out var result2))
				{
					num = result2;
				}
				TextBuffer.Remove(key);
			}
			GUILayout.Label("[" + (isInt ? min.ToString() : min.ToString("0.#")) + " - " + (isInt ? max.ToString() : max.ToString("0.#")) + "]", _dim, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(90f) });
			GUILayout.EndHorizontal();
			return num;
		}
	}
	internal static class ItemConfigDrawer
	{
		private static readonly Dictionary<string, bool> Expanded = new Dictionary<string, bool>();

		private static readonly Dictionary<object, List<string[]>> RecipeRows = new Dictionary<object, List<string[]>>();

		private static int _order = 0;

		private static readonly KeyValuePair<string, ItemStat[]>[] StatGroups = new KeyValuePair<string, ItemStat[]>[5]
		{
			new KeyValuePair<string, ItemStat[]>("Damage", new ItemStat[20]
			{
				ItemStat.slash,
				ItemStat.slash_per_level,
				ItemStat.blunt,
				ItemStat.blunt_per_level,
				ItemStat.pierce,
				ItemStat.pierce_per_level,
				ItemStat.fire,
				ItemStat.fire_per_level,
				ItemStat.lightning,
				ItemStat.lightning_per_level,
				ItemStat.frost,
				ItemStat.frost_per_level,
				ItemStat.poison,
				ItemStat.poison_per_level,
				ItemStat.spirit,
				ItemStat.spirit_per_level,
				ItemStat.pickaxe,
				ItemStat.pickaxe_per_level,
				ItemStat.chop,
				ItemStat.chop_per_level
			}),
			new KeyValuePair<string, ItemStat[]>("Combat", new ItemStat[20]
			{
				ItemStat.attack_force,
				ItemStat.primary_attack_force_multiply,
				ItemStat.secondary_attack_force_multiply,
				ItemStat.primary_attack_stamina,
				ItemStat.primary_attack_eitr,
				ItemStat.primary_attack_flat_health_cost,
				ItemStat.primary_attack_percent_health_cost,
				ItemStat.primary_attack_health_returned,
				ItemStat.primary_attack_damage_bonus_per_missing_hp,
				ItemStat.primary_attack_projectile_count,
				ItemStat.secondary_attack_stamina,
				ItemStat.secondary_attack_eitr,
				ItemStat.secondary_attack_flat_health_cost,
				ItemStat.secondary_attack_percent_health_cost,
				ItemStat.projectile_velocity,
				ItemStat.projectile_accuracy_max,
				ItemStat.bow_draw_speed,
				ItemStat.crossbow_reload_speed,
				ItemStat.crossbow_reload_stamina_drain,
				ItemStat.draw_stamina_drain
			}),
			new KeyValuePair<string, ItemStat[]>("Defense", new ItemStat[5]
			{
				ItemStat.block_armor,
				ItemStat.block_armor_per_level,
				ItemStat.parry,
				ItemStat.block_force,
				ItemStat.block_force_per_level
			}),
			new KeyValuePair<string, ItemSta

GatedLootUnidentified.dll

Decompiled a day ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using EpicLoot;
using EpicLoot.Biomes;
using EpicLoot.Crafting;
using EpicLoot.CraftingV2;
using EpicLoot.Data;
using EpicLoot.GatedItemType;
using EpicLoot.LegendarySystem;
using EpicLoot_UnityLib;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using TMPro;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.UI;

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

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace GatedLootUnidentified
{
	internal static class BossUniqueRarity
	{
		private static ConfigEntry<bool> enabled;

		private static ConfigEntry<bool> verbose;

		private static ConfigEntry<string> weights;

		private static ConfigEntry<string> ignorePrefabs;

		private static readonly string[] Names = new string[5] { "Plain", "Magic", "Rare", "Epic", "Legendary" };

		private static readonly ItemRarity[] Rarities;

		internal static void Apply(Harmony harmony, ConfigFile config)
		{
			//IL_00ae: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c2: Expected O, but got Unknown
			enabled = config.Bind<bool>("BossUniqueRarity", "Enabled", true, Plugin.Synced("Roll rarity for enchantable equipment in vanilla boss drops."));
			weights = config.Bind<string>("BossUniqueRarity", "Weights", "Plain:45,Magic:35,Rare:15,Epic:4,Legendary:1", Plugin.Synced("Rarity:weight pairs; locked weights fold into the next lower unlocked rarity."));
			ignorePrefabs = config.Bind<string>("BossUniqueRarity", "IgnorePrefabs", "", Plugin.Synced("Comma-separated item prefab names that are never converted."));
			verbose = config.Bind<bool>("BossUniqueRarity", "Verbose", false, "Log boss, prefab and result for each equipment copy.");
			harmony.Patch((MethodBase)AccessTools.Method(typeof(CharacterDrop), "GenerateDropList", (Type[])null, (Type[])null), (HarmonyMethod)null, new HarmonyMethod(typeof(BossUniqueRarity), "ConvertDrops", (Type[])null)
			{
				priority = 0
			}, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
		}

		private static string Clean(string prefab)
		{
			return (prefab ?? "").Replace("(Clone)", "").Trim();
		}

		private static double[] Weights(string boss)
		{
			double[] array = new double[Names.Length];
			string[] array2 = (weights.Value ?? "").Split(new char[1] { ',' });
			foreach (string text in array2)
			{
				string[] parts = text.Split(new char[1] { ':' });
				if (parts.Length == 2)
				{
					int num = Array.FindIndex(Names, (string name) => string.Equals(name, parts[0].Trim(), StringComparison.OrdinalIgnoreCase));
					if (num >= 0 && double.TryParse(parts[1].Trim(), NumberStyles.Float, CultureInfo.InvariantCulture, out var result) && !double.IsNaN(result) && !double.IsInfinity(result))
					{
						array[num] = Math.Max(0.0, result);
					}
				}
			}
			double num2 = array.Max();
			if (num2 > 0.0)
			{
				for (int num3 = 0; num3 < array.Length; num3++)
				{
					array[num3] /= num2;
				}
			}
			for (int num4 = array.Length - 1; num4 > 0; num4--)
			{
				if (!ProgressionLoot.BossUniqueUnlocked(Rarities[num4 - 1], boss))
				{
					array[num4 - 1] += array[num4];
					array[num4] = 0.0;
				}
			}
			return array;
		}

		private static int Roll(double[] odds)
		{
			double max = odds.Max();
			if (max <= 0.0)
			{
				return 0;
			}
			double num = odds.Sum((double x) => x / max);
			double num2 = (double)Random.value * num;
			int result = 0;
			for (int num3 = 0; num3 < odds.Length; num3++)
			{
				if (!(odds[num3] <= 0.0))
				{
					result = num3;
					num2 -= odds[num3] / max;
					if (num2 < 0.0)
					{
						return num3;
					}
				}
			}
			return result;
		}

		private static void ConvertDrops(CharacterDrop __instance, Character ___m_character, List<KeyValuePair<GameObject, int>> __result)
		{
			//IL_0099: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00aa: Unknown result type (might be due to invalid IL or missing references)
			//IL_00af: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b4: Unknown result type (might be due to invalid IL or missing references)
			//IL_0163: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ad: Unknown result type (might be due to invalid IL or missing references)
			List<GameObject> list = new List<GameObject>();
			try
			{
				if (!enabled.Value || (Object)(object)___m_character == (Object)null || !___m_character.IsBoss() || __result == null)
				{
					return;
				}
				string text = Clean(((Object)((Component)___m_character).gameObject).name);
				HashSet<string> hashSet = new HashSet<string>((ignorePrefabs.Value ?? "").Split(new char[1] { ',' }).Select(Clean), StringComparer.OrdinalIgnoreCase);
				double[] odds = Weights(text);
				List<KeyValuePair<GameObject, int>> list2 = new List<KeyValuePair<GameObject, int>>();
				Vector3 val = ___m_character.GetCenterPoint() + ((Component)__instance).transform.TransformVector(__instance.m_spawnOffset);
				foreach (KeyValuePair<GameObject, int> item in __result)
				{
					ItemDrop val2 = (((Object)(object)item.Key != (Object)null) ? item.Key.GetComponent<ItemDrop>() : null);
					if ((Object)(object)val2 == (Object)null || hashSet.Contains(Clean(((Object)item.Key).name)) || !EpicLoot.CanBeMagicItem(val2.m_itemData) || item.Value <= 0)
					{
						list2.Add(item);
						continue;
					}
					int num = item.Value;
					for (int i = 0; i < item.Value; i++)
					{
						int num2 = Roll(odds);
						if (num2 != 0)
						{
							GameObject val3 = LootRoller.SpawnLootForDrop(item.Key, val, true);
							list.Add(val3);
							ItemDrop component = val3.GetComponent<ItemDrop>();
							ItemData itemData = component.m_itemData;
							MagicItemComponent component2 = ItemExtensions.Data(itemData).GetOrCreate<MagicItemComponent>("");
							MagicItem magic = LootRoller.RollMagicItem(Rarities[num2 - 1], itemData, LootRoller.GetLuckFactor(val), 1f);
							if (LootRoller.CheatForceMagicEffect)
							{
								LootRoller.AddDebugMagicEffects(magic);
							}
							AccessTools.Method(typeof(API), "WithChangeReason", (Type[])null, (Type[])null).Invoke(null, new object[2]
							{
								"LootRoll",
								(Action)delegate
								{
									component2.SetMagicItem(magic);
								}
							});
							AccessTools.Method(typeof(ItemDrop), "Save", (Type[])null, (Type[])null).Invoke(component, null);
							AccessTools.Method(typeof(LootRoller), "InitializeMagicItem", (Type[])null, (Type[])null).Invoke(null, new object[1] { itemData });
							AccessTools.Method(typeof(API), "RaiseLootGenerated", (Type[])null, (Type[])null).Invoke(null, new object[1] { itemData });
							num--;
						}
						if (verbose.Value)
						{
							Plugin.Log.LogInfo((object)("BossUniqueRarity boss=" + text + ", prefab=" + Clean(((Object)item.Key).name) + ", result=" + Names[num2]));
						}
					}
					if (num > 0)
					{
						list2.Add(new KeyValuePair<GameObject, int>(item.Key, num));
					}
				}
				__result.Clear();
				__result.AddRange(list2);
			}
			catch (Exception arg)
			{
				foreach (GameObject item2 in list)
				{
					try
					{
						if (!((Object)(object)item2 == (Object)null))
						{
							if ((Object)(object)ZNetScene.instance != (Object)null)
							{
								ZNetScene.instance.Destroy(item2);
							}
							else
							{
								Object.Destroy((Object)(object)item2);
							}
						}
					}
					catch (Exception ex)
					{
						Plugin.Log.LogWarning((object)("BossUniqueRarity cleanup failed: " + ex.Message));
					}
				}
				Plugin.Log.LogWarning((object)$"BossUniqueRarity failed; leaving vanilla drops untouched: {arg}");
			}
		}

		static BossUniqueRarity()
		{
			ItemRarity[] array = new ItemRarity[4];
			RuntimeHelpers.InitializeArray(array, (RuntimeFieldHandle)/*OpCode not supported: LdMemberToken*/);
			Rarities = (ItemRarity[])(object)array;
		}
	}
	[BepInPlugin("anri.gatedlootunidentified", "GatedLootUnidentified", "1.11.4")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public class Plugin : BaseUnityPlugin
	{
		internal struct UnlockCost
		{
			public string Item;

			public int Amount;
		}

		public const string GUID = "anri.gatedlootunidentified";

		public const string NAME = "GatedLootUnidentified";

		public const string VERSION = "1.11.4";

		private const string EPICLOOT_GUID = "randyknapp.mods.epicloot";

		internal static ManualLogSource Log;

		internal static ConfigEntry<bool> GatedDropsBecomeUnidentified;

		internal static ConfigEntry<float> GatedDropChance;

		internal static ConfigEntry<bool> BlockIdentifyUntilBossKilled;

		internal static ConfigEntry<string> UnlockCosts;

		internal static ConfigEntry<float> PerItemCostFraction;

		internal static ConfigEntry<bool> AllowPerItemPayment;

		internal static ConfigEntry<bool> RequireTableLevel;

		internal static ConfigEntry<string> TableLevelByBiome;

		internal static ConfigEntry<bool> Verbose;

		internal static ConfigEntry<string> IdentifyCostLabel;

		internal static ConfigEntry<bool> IdentifySelectsOne;

		internal static ConfigEntry<float> ExplainerFontScale;

		internal static ConfigEntry<bool> HideUndiscoveredPreview;

		internal static ConfigEntry<string> UndiscoveredName;

		internal static ConfigEntry<string> AugmentDetailsMode;

		internal static ConfigEntry<bool> AugmentDetailsAltKey;

		internal static ConfigEntry<bool> AugmentDetailsTooltips;

		internal static ConfigEntry<float> AugmentDetailsTextScale;

		internal static ConfigEntry<bool> RuneLabelsEnabled;

		internal static ConfigEntry<float> HintFontScale;

		internal static ConfigEntry<string> SetEtchLabel;

		internal static ConfigEntry<string> SetExtractLabel;

		internal static ConfigEntry<string> SetEtchAction;

		internal static ConfigEntry<string> SetExtractAction;

		internal static ConfigEntry<string> SetExtractHint;

		internal static ConfigEntry<string> SetEtchHint;

		internal static ConfigEntry<bool> SetDiscoveryEnabled;

		internal static ConfigEntry<bool> SetDiscoveryUnlockToast;

		internal static ConfigEntry<bool> SetDiscoverySetToast;

		internal static ConfigEntry<string> SetDiscoveryUnlockTopic;

		internal static ConfigEntry<string> SetDiscoveryUnlockDescription;

		internal static ConfigDescription Synced(string description, AcceptableValueBase values = null)
		{
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Expected O, but got Unknown
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Expected O, but got Unknown
			return new ConfigDescription(description, values, new object[1] { (object)new ConfigurationManagerAttributes
			{
				IsAdminOnly = true
			} });
		}

		private void Awake()
		{
			//IL_0259: Unknown result type (might be due to invalid IL or missing references)
			//IL_0263: Expected O, but got Unknown
			//IL_02d6: Unknown result type (might be due to invalid IL or missing references)
			//IL_02e0: Expected O, but got Unknown
			//IL_0333: Unknown result type (might be due to invalid IL or missing references)
			//IL_033d: Expected O, but got Unknown
			//IL_04e9: Unknown result type (might be due to invalid IL or missing references)
			//IL_04ee: Unknown result type (might be due to invalid IL or missing references)
			//IL_04f4: Expected O, but got Unknown
			//IL_04f4: Unknown result type (might be due to invalid IL or missing references)
			//IL_0500: Expected O, but got Unknown
			//IL_050b: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			GatedDropsBecomeUnidentified = ((BaseUnityPlugin)this).Config.Bind<bool>("Drops", "GatedDropsBecomeUnidentified", true, Synced("When EpicLoot rolls an item that is gated behind a boss you have not killed, drop it as an unidentified item of its real tier instead of downgrading it to current-tier gear."));
			GatedDropChance = ((BaseUnityPlugin)this).Config.Bind<float>("Drops", "GatedDropChance", 1f, Synced("Fraction (0-1) of such gated drops that become unidentified. The rest are downgraded as EpicLoot normally does."));
			BlockIdentifyUntilBossKilled = ((BaseUnityPlugin)this).Config.Bind<bool>("Identify", "BlockIdentifyUntilBossKilled", true, Synced("Lock identifying an unidentified item until its biome boss is dead, the tier is unlocked at the table, or the per-item fee is paid. Off = EpicLoot's default (identifies as lower-tier loot)."));
			UnlockCosts = ((BaseUnityPlugin)this).Config.Bind<string>("Identify", "UnlockCosts", "BlackForest:Bronze:20,Swamp:Iron:30,Mountain:Silver:40,Plains:BlackMetal:40,Mistlands:Eitr:20,AshLands:FlametalNew:20,DeepNorth:Gold:20", Synced("Permanent, world-wide unlock cost per biome as Biome:ItemPrefab:Amount. Paid once at the enchanting table with the Unlock button; afterwards everyone can identify that tier without the boss kill."));
			PerItemCostFraction = ((BaseUnityPlugin)this).Config.Bind<float>("Identify", "PerItemCostFraction", 0.1f, Synced("Identify a single locked item by paying this fraction of the biome's unlock cost (rounded up, minimum 1) on top of the normal identify cost."));
			AllowPerItemPayment = ((BaseUnityPlugin)this).Config.Bind<bool>("Identify", "AllowPerItemPayment", true, Synced("Allow the per-item payment path."));
			RequireTableLevel = ((BaseUnityPlugin)this).Config.Bind<bool>("Identify", "RequireTableLevel", false, Synced("Additionally require the table's Sacrifice feature level to reach the value in TableLevelByBiome. This is a hard lock that no payment bypasses."));
			TableLevelByBiome = ((BaseUnityPlugin)this).Config.Bind<string>("Identify", "TableLevelByBiome", "Meadows:0,BlackForest:1,Ocean:1,Swamp:2,Mountain:3,Plains:4,Mistlands:5,AshLands:6,DeepNorth:7", Synced("Minimum Sacrifice feature level per biome, as Biome:Level pairs. Only used when RequireTableLevel is on."));
			Verbose = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Verbose", false, "Log drop conversions, refusals, payments and unlocks.");
			IdentifyCostLabel = ((BaseUnityPlugin)this).Config.Bind<string>("UI", "IdentifyCostLabel", "Identify Cost:", "Text shown above the cost list while the Identify tab is selected (EpicLoot shows 'Enchant Cost:' there). Empty = leave EpicLoot's label.");
			IdentifySelectsOne = ((BaseUnityPlugin)this).Config.Bind<bool>("UI", "IdentifySelectsOne", true, "In the Identify tab, clicking a stack of unidentified items selects one instead of the whole stack. Type a number in the quantity box to identify more.");
			ExplainerFontScale = ((BaseUnityPlugin)this).Config.Bind<float>("UI", "ExplainerFontScale", 1.4f, "Multiplier for the font size of the small explainer text above the product list on the Sacrifice/Identify panel. 1 = EpicLoot's size.");
			HideUndiscoveredPreview = ((BaseUnityPlugin)this).Config.Bind<bool>("UI", "HideUndiscoveredPreview", true, "In the Identify tab's preview of possible results, items the player has never picked up are shown as a question mark instead of their real icon and name.");
			UndiscoveredName = ((BaseUnityPlugin)this).Config.Bind<string>("UI", "UndiscoveredName", "Undiscovered", "Name shown for hidden preview entries.");
			AugmentDetailsMode = ((BaseUnityPlugin)this).Config.Bind<string>("AugmentDetails", "Mode", "Shift", new ConfigDescription("Shift = show effect descriptions in the Augment tab's Available Effects list while Shift (or Alt, see AltKey) is held; Always = always show them; Off = EpicLoot default.", (AcceptableValueBase)(object)new AcceptableValueList<string>(new string[3] { "Shift", "Always", "Off" }), Array.Empty<object>()));
			AugmentDetailsAltKey = ((BaseUnityPlugin)this).Config.Bind<bool>("AugmentDetails", "AltKey", true, "Also accept Left/Right Alt as the details key.");
			AugmentDetailsTooltips = ((BaseUnityPlugin)this).Config.Bind<bool>("AugmentDetails", "Tooltips", true, "Hovering an effect in the Augment tab's left column shows its description in a tooltip.");
			AugmentDetailsTextScale = ((BaseUnityPlugin)this).Config.Bind<float>("AugmentDetails", "TextScale", 0.8f, new ConfigDescription("Font size of the description lines relative to the effect line.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.5f, 1f), Array.Empty<object>()));
			RuneLabelsEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("RuneLabels", "Enabled", true, "Relabel the Rune tab's set modes and extend their explanations.");
			HintFontScale = ((BaseUnityPlugin)this).Config.Bind<float>("RuneLabels", "HintFontScale", 1.3f, new ConfigDescription("Multiplier for the font size of the Rune tab's warning/hint text (EpicLoot's sentence plus our hint). 1 = EpicLoot's size.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.5f, 3f), Array.Empty<object>()));
			SetEtchLabel = ((BaseUnityPlugin)this).Config.Bind<string>("RuneLabels", "SetEtchLabel", "Legendary Set Etch", "Label for the set etch mode.");
			SetExtractLabel = ((BaseUnityPlugin)this).Config.Bind<string>("RuneLabels", "SetExtractLabel", "Legendary Set Extract", "Label for the set extract mode.");
			SetEtchAction = ((BaseUnityPlugin)this).Config.Bind<string>("RuneLabels", "SetEtchAction", "Etch Legendary Set Rune", "Main button text for set etch.");
			SetExtractAction = ((BaseUnityPlugin)this).Config.Bind<string>("RuneLabels", "SetExtractAction", "Extract Legendary Set Rune", "Main button text for set extract.");
			SetExtractHint = ((BaseUnityPlugin)this).Config.Bind<string>("RuneLabels", "SetExtractHint", "Only EpicLoot set pieces qualify: Legendary or better gear whose tooltip names a set, such as Heimdall, Ragnar or Thor. Vanilla armour sets like Root or Troll Leather are not sets here. Equipped items are not listed.", "Explanation appended to the set extract warning.");
			SetEtchHint = ((BaseUnityPlugin)this).Config.Bind<string>("RuneLabels", "SetEtchHint", "You need a set rune from Legendary Set Extract first. Targets are magic items that are not already a unique or set piece, at the rune's rarity, in a slot the set has a piece for.", "Explanation appended to the set etch warning.");
			SetDiscoveryEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("SetDiscovery", "Enabled", true, "Hide the Rune tab's set modes until the character has had an EpicLoot set piece in their inventory.");
			SetDiscoveryUnlockToast = ((BaseUnityPlugin)this).Config.Bind<bool>("SetDiscovery", "UnlockToast", true, "Show an unlock message the first time a set is discovered.");
			SetDiscoveryUnlockTopic = ((BaseUnityPlugin)this).Config.Bind<string>("SetDiscovery", "UnlockTopic", "Legendary Set Etch unlocked", "Topic of the first set unlock message.");
			SetDiscoveryUnlockDescription = ((BaseUnityPlugin)this).Config.Bind<string>("SetDiscovery", "UnlockDescription", "You found a piece of a legendary set. Extract and etch set runes at the enchanting table.", "Description of the first set unlock message.");
			SetDiscoverySetToast = ((BaseUnityPlugin)this).Config.Bind<bool>("SetDiscovery", "SetToast", true, "Also show a message for each newly discovered set, naming it.");
			if (!Chainloader.PluginInfos.ContainsKey("randyknapp.mods.epicloot"))
			{
				Log.LogInfo((object)"GatedLootUnidentified idle: EpicLoot not loaded.");
				return;
			}
			try
			{
				Harmony val = new Harmony("anri.gatedlootunidentified");
				Patches.Apply(val);
				ProgressionLoot.Apply(val, ((BaseUnityPlugin)this).Config);
				BossUniqueRarity.Apply(val, ((BaseUnityPlugin)this).Config);
				Log.LogInfo((object)string.Format("{0} {1} active with EpicLoot {2}.", "GatedLootUnidentified", "1.11.4", Chainloader.PluginInfos["randyknapp.mods.epicloot"].Metadata.Version));
			}
			catch (Exception arg)
			{
				Log.LogError((object)string.Format("{0} failed to patch EpicLoot (its internals probably changed): {1}", "GatedLootUnidentified", arg));
			}
		}

		internal static Dictionary<string, int> ParseLevelMap()
		{
			Dictionary<string, int> dictionary = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase);
			string[] array = (TableLevelByBiome.Value ?? "").Split(new char[1] { ',' });
			for (int i = 0; i < array.Length; i++)
			{
				string[] array2 = array[i].Split(new char[1] { ':' });
				if (array2.Length == 2 && int.TryParse(array2[1].Trim(), out var result))
				{
					dictionary[array2[0].Trim()] = result;
				}
			}
			return dictionary;
		}

		internal static Dictionary<string, UnlockCost> ParseUnlockCosts()
		{
			Dictionary<string, UnlockCost> dictionary = new Dictionary<string, UnlockCost>(StringComparer.OrdinalIgnoreCase);
			string[] array = (UnlockCosts.Value ?? "").Split(new char[1] { ',' });
			for (int i = 0; i < array.Length; i++)
			{
				string[] array2 = array[i].Split(new char[1] { ':' });
				if (array2.Length == 3 && int.TryParse(array2[2].Trim(), out var result) && result > 0)
				{
					dictionary[array2[0].Trim()] = new UnlockCost
					{
						Item = array2[1].Trim(),
						Amount = result
					};
				}
			}
			return dictionary;
		}
	}
	internal static class Patches
	{
		private struct IdentifyState
		{
			public HashSet<Biome> Bypass;

			public HashSet<Biome> Identify;

			public bool GateLogged;
		}

		private const string KeyPrefix = "anri_identify_unlocked_";

		private static FieldRef<MultiSelectItemListElement, IListElement> _listElementItem;

		private static bool _selectQuantityWarningLogged;

		private static bool _previewWarningLogged;

		private static readonly MethodInfo _memberwiseClone = AccessTools.Method(typeof(object), "MemberwiseClone", (Type[])null, (Type[])null);

		private static Sprite _questionSprite;

		private static FieldRef<AugmentUI, List<Toggle>> _augmentSelectors;

		private static bool _lastDetails;

		private static bool _augmentWarningLogged;

		private static bool _augmentTooltipWarningLogged;

		private static bool _augmentPrefabLogged;

		private static readonly MethodInfo _addWord = AccessTools.Method(typeof(Localization), "AddWord", (Type[])null, (Type[])null);

		private static bool _runeLabelsWarningLogged;

		private static bool _runeHintWarningLogged;

		private const string SetDiscoveryKey = "anri.gatedloot.sets";

		private static FieldRef<RuneUI, Toggle> _setEtchButton;

		private static FieldRef<RuneUI, Toggle> _setExtractButton;

		private static bool _hasDiscoveredSet;

		private static bool _setDiscoveryWarningLogged;

		private static readonly Dictionary<TMP_Text, float> _runeBaseFontSize = new Dictionary<TMP_Text, float>();

		private static readonly FieldInfo _localizationInstance = AccessTools.Field(typeof(Localization), "m_instance");

		[ThreadStatic]
		private static HashSet<Biome> _bypassBiomes;

		[ThreadStatic]
		private static HashSet<Biome> _identifyBiomes;

		[ThreadStatic]
		private static bool _identifyGateLogged;

		[ThreadStatic]
		private static bool _checkingBossLock;

		private static bool _itemGateWarningLogged;

		private static readonly MethodInfo _getBossKeyForMode = AccessTools.Method(typeof(GatedItemTypeHelper), "GetBossKeyForMode", (Type[])null, (Type[])null);

		private static readonly Dictionary<SacrificeUI, Button> _unlockButtons = new Dictionary<SacrificeUI, Button>();

		private static readonly Dictionary<SacrificeUI, TMP_Text> _costLabels = new Dictionary<SacrificeUI, TMP_Text>();

		private static readonly Dictionary<TMP_Text, float> _baseFontSize = new Dictionary<TMP_Text, float>();

		private static string _costLabelOriginal;

		private static Sprite QuestionSprite
		{
			get
			{
				//IL_00a8: Unknown result type (might be due to invalid IL or missing references)
				//IL_00ad: Unknown result type (might be due to invalid IL or missing references)
				//IL_00b4: Unknown result type (might be due to invalid IL or missing references)
				//IL_00bb: Unknown result type (might be due to invalid IL or missing references)
				//IL_00c4: Expected O, but got Unknown
				//IL_0131: Unknown result type (might be due to invalid IL or missing references)
				//IL_0140: Unknown result type (might be due to invalid IL or missing references)
				//IL_00fa: Unknown result type (might be due to invalid IL or missing references)
				//IL_00df: Unknown result type (might be due to invalid IL or missing references)
				if ((Object)(object)_questionSprite != (Object)null)
				{
					return _questionSprite;
				}
				string[] array = new string[16]
				{
					"................", ".....######.....", "....##....##....", "...##......##...", "...##......##...", "...........##...", "..........##....", ".........##.....", "........##......", ".......##.......",
					".......##.......", "................", "................", ".......##.......", ".......##.......", "................"
				};
				Texture2D val = new Texture2D(16, 16, (TextureFormat)4, false)
				{
					filterMode = (FilterMode)0,
					wrapMode = (TextureWrapMode)1,
					hideFlags = (HideFlags)61
				};
				for (int i = 0; i < 16; i++)
				{
					for (int j = 0; j < 16; j++)
					{
						val.SetPixel(j, 15 - i, (Color)((array[i][j] == '#') ? new Color(0.9f, 0.85f, 0.6f, 1f) : Color.clear));
					}
				}
				val.Apply();
				Sprite obj = Sprite.Create(val, new Rect(0f, 0f, 16f, 16f), new Vector2(0.5f, 0.5f), 16f);
				((Object)obj).hideFlags = (HideFlags)61;
				Object.DontDestroyOnLoad((Object)(object)val);
				Object.DontDestroyOnLoad((Object)(object)obj);
				_questionSprite = obj;
				return obj;
			}
		}

		private static HashSet<string> DiscoveredSets(Player p)
		{
			HashSet<string> hashSet = new HashSet<string>(StringComparer.Ordinal);
			if ((Object)(object)p != (Object)null && p.m_customData.TryGetValue("anri.gatedloot.sets", out var value))
			{
				string[] array = (value ?? "").Split(new char[1] { ',' });
				foreach (string text in array)
				{
					if (!string.IsNullOrWhiteSpace(text))
					{
						hashSet.Add(text.Trim());
					}
				}
			}
			return hashSet;
		}

		private static bool HasDiscoveredAnySet(Player p)
		{
			return DiscoveredSets(p).Count > 0;
		}

		private static void WarnSetDiscovery(Exception e)
		{
			if (!_setDiscoveryWarningLogged)
			{
				_setDiscoveryWarningLogged = true;
				Plugin.Log.LogWarning((object)$"Set discovery failed: {e}");
			}
		}

		private static void Discover(Player p, ItemData item, bool toast)
		{
			try
			{
				MagicItem val = default(MagicItem);
				if (!Plugin.SetDiscoveryEnabled.Value || (Object)(object)p == (Object)null || item == null || !ItemDataExtensions.IsMagic(item, ref val) || string.IsNullOrEmpty(val.SetID))
				{
					return;
				}
				HashSet<string> hashSet = DiscoveredSets(p);
				bool flag = hashSet.Count == 0;
				if (!hashSet.Add(val.SetID))
				{
					return;
				}
				p.m_customData["anri.gatedloot.sets"] = string.Join(",", hashSet);
				_hasDiscoveredSet = true;
				if (toast && !((Object)(object)MessageHud.instance == (Object)null))
				{
					if (flag && Plugin.SetDiscoveryUnlockToast.Value)
					{
						MessageHud.instance.QueueUnlockMsg(QuestionSprite, Localization.instance.Localize(Plugin.SetDiscoveryUnlockTopic.Value), Localization.instance.Localize(Plugin.SetDiscoveryUnlockDescription.Value));
					}
					if (Plugin.SetDiscoverySetToast.Value)
					{
						LegendarySetInfo val2 = default(LegendarySetInfo);
						string text = (UniqueLegendaryHelper.TryGetLegendarySetInfo(val.SetID, ref val2) ? val2.Name : val.SetID);
						MessageHud.instance.QueueUnlockMsg(QuestionSprite, Localization.instance.Localize("Set discovered"), Localization.instance.Localize(text));
					}
				}
			}
			catch (Exception e)
			{
				WarnSetDiscovery(e);
			}
		}

		private static void AddKnownItem_Postfix(Player __instance, ItemData item)
		{
			try
			{
				if ((Object)(object)__instance == (Object)(object)Player.m_localPlayer)
				{
					Discover(__instance, item, toast: true);
				}
			}
			catch (Exception e)
			{
				WarnSetDiscovery(e);
			}
		}

		private static void OnSpawned_Postfix(Player __instance)
		{
			try
			{
				if ((Object)(object)__instance != (Object)(object)Player.m_localPlayer)
				{
					return;
				}
				_hasDiscoveredSet = HasDiscoveredAnySet(__instance);
				foreach (ItemData allItem in ((Humanoid)__instance).GetInventory().GetAllItems())
				{
					Discover(__instance, allItem, toast: true);
				}
			}
			catch (Exception e)
			{
				WarnSetDiscovery(e);
			}
		}

		private static void RuneAwake_Postfix(RuneUI __instance)
		{
			RuneUpdate_Postfix(__instance);
			try
			{
				if (!Plugin.RuneLabelsEnabled.Value)
				{
					return;
				}
				Toggle val = _setEtchButton.Invoke(__instance);
				Toggle val2 = _setExtractButton.Invoke(__instance);
				if ((Object)(object)val != (Object)null)
				{
					TMP_Text[] componentsInChildren = ((Component)val).GetComponentsInChildren<TMP_Text>(true);
					for (int i = 0; i < componentsInChildren.Length; i++)
					{
						componentsInChildren[i].text = Localization.instance.Localize(Plugin.SetEtchLabel.Value);
					}
				}
				if ((Object)(object)val2 != (Object)null)
				{
					TMP_Text[] componentsInChildren = ((Component)val2).GetComponentsInChildren<TMP_Text>(true);
					for (int i = 0; i < componentsInChildren.Length; i++)
					{
						componentsInChildren[i].text = Localization.instance.Localize(Plugin.SetExtractLabel.Value);
					}
				}
			}
			catch (Exception arg)
			{
				if (!_runeLabelsWarningLogged)
				{
					_runeLabelsWarningLogged = true;
					Plugin.Log.LogWarning((object)$"Rune labels failed: {arg}");
				}
			}
		}

		private static void RuneOnEnable_Postfix(RuneUI __instance)
		{
			RuneUpdate_Postfix(__instance);
		}

		private static void RuneUpdate_Postfix(RuneUI __instance)
		{
			try
			{
				bool flag = !Plugin.SetDiscoveryEnabled.Value || (Object)(object)Player.m_localPlayer == (Object)null || _hasDiscoveredSet;
				if (_setEtchButton == null)
				{
					_setEtchButton = AccessTools.FieldRefAccess<RuneUI, Toggle>("_setEtchButton");
				}
				if (_setExtractButton == null)
				{
					_setExtractButton = AccessTools.FieldRefAccess<RuneUI, Toggle>("_setExtractButton");
				}
				Toggle val = _setEtchButton.Invoke(__instance);
				Toggle val2 = _setExtractButton.Invoke(__instance);
				if ((Object)(object)val != (Object)null && ((Component)val).gameObject.activeSelf != flag)
				{
					((Component)val).gameObject.SetActive(flag);
				}
				if ((Object)(object)val2 != (Object)null && ((Component)val2).gameObject.activeSelf != flag)
				{
					((Component)val2).gameObject.SetActive(flag);
				}
			}
			catch (Exception e)
			{
				WarnSetDiscovery(e);
			}
		}

		internal static void ApplyRuneLabels(Localization __instance)
		{
			try
			{
				if (Plugin.RuneLabelsEnabled.Value && __instance != null)
				{
					if (_addWord == null)
					{
						throw new MissingMethodException("Localization.AddWord not found");
					}
					_addWord.Invoke(__instance, new object[2]
					{
						"mod_epicloot_rune_setetch",
						Plugin.SetEtchLabel.Value
					});
					_addWord.Invoke(__instance, new object[2]
					{
						"mod_epicloot_rune_setextract",
						Plugin.SetExtractLabel.Value
					});
					_addWord.Invoke(__instance, new object[2]
					{
						"mod_epicloot_rune_setetch_action",
						Plugin.SetEtchAction.Value
					});
					_addWord.Invoke(__instance, new object[2]
					{
						"mod_epicloot_rune_setextract_action",
						Plugin.SetExtractAction.Value
					});
				}
			}
			catch (Exception arg)
			{
				if (!_runeLabelsWarningLogged)
				{
					_runeLabelsWarningLogged = true;
					Plugin.Log.LogWarning((object)$"Rune labels failed: {arg}");
				}
			}
		}

		private static void RuneSelectMode_Postfix(RuneUI __instance, object action)
		{
			try
			{
				if (!Plugin.RuneLabelsEnabled.Value || (Object)(object)__instance.Warning == (Object)null)
				{
					return;
				}
				int num = Convert.ToInt32(action);
				if (num == 2 || num == 3)
				{
					string text = ((num == 2) ? Plugin.SetExtractHint.Value : Plugin.SetEtchHint.Value);
					if (Localization.instance != null)
					{
						text = Localization.instance.Localize(text);
					}
					TMP_Text warning = __instance.Warning;
					warning.text = warning.text + "\n<color=#c0c0c0ff>" + text + "</color>";
				}
				TMP_Text warning2 = __instance.Warning;
				if (Plugin.HintFontScale.Value > 0f)
				{
					if (!_runeBaseFontSize.TryGetValue(warning2, out var value))
					{
						value = warning2.fontSize;
						_runeBaseFontSize[warning2] = value;
					}
					warning2.enableAutoSizing = false;
					warning2.fontSize = value * Plugin.HintFontScale.Value;
				}
			}
			catch (Exception arg)
			{
				if (!_runeHintWarningLogged)
				{
					_runeHintWarningLogged = true;
					Plugin.Log.LogWarning((object)$"Rune hints failed: {arg}");
				}
			}
		}

		private static bool AugmentDetailsActive()
		{
			if (Plugin.AugmentDetailsMode.Value == "Off")
			{
				return false;
			}
			if (Plugin.AugmentDetailsMode.Value == "Always")
			{
				return true;
			}
			if (!MagicItem.ShowEffectDetails)
			{
				if (Plugin.AugmentDetailsAltKey.Value)
				{
					if (!ZInput.GetKey((KeyCode)308, true))
					{
						return ZInput.GetKey((KeyCode)307, true);
					}
					return true;
				}
				return false;
			}
			return true;
		}

		private static bool IsAugmentEffectLine(string line)
		{
			line = line.TrimStart(Array.Empty<char>());
			while (line.StartsWith("<", StringComparison.Ordinal))
			{
				int num = line.IndexOf('>');
				if (num < 0)
				{
					break;
				}
				line = line.Substring(num + 1).TrimStart(Array.Empty<char>());
			}
			return line.StartsWith("‣ ", StringComparison.Ordinal);
		}

		private static void GetAvailableAugmentEffects_Postfix(ItemData item, int augmentindex, ref string __result)
		{
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0159: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				if (!AugmentDetailsActive() || string.IsNullOrEmpty(__result))
				{
					return;
				}
				MagicItem val = ((item != null) ? ItemDataExtensions.GetMagicItem(item) : null);
				if (val == null)
				{
					return;
				}
				ItemRarity rarity = val.Rarity;
				bool flag = false;
				if (augmentindex >= 0 && augmentindex < val.Effects.Count)
				{
					flag = MagicItemEffectDefinitions.Get(val.Effects[augmentindex].EffectType).GetValuesForRarity(rarity) == null;
				}
				List<MagicItemEffectDefinition> availableEffects = MagicItemEffectDefinitions.GetAvailableEffects(ItemDataExtensions.Extended(item), ItemDataExtensions.GetMagicItem(item), flag ? (-1) : augmentindex, true, false, false);
				string[] array = __result.Split(new char[1] { '\n' });
				if (array.Count(IsAugmentEffectLine) != availableEffects.Count)
				{
					return;
				}
				List<string> list = new List<string>();
				int num = 0;
				string text = (Plugin.AugmentDetailsTextScale.Value * 100f).ToString("0", CultureInfo.InvariantCulture);
				string[] array2 = array;
				foreach (string text2 in array2)
				{
					list.Add(text2);
					if (IsAugmentEffectLine(text2))
					{
						MagicItemEffectDefinition val2 = availableEffects[num++];
						if (!string.IsNullOrEmpty(val2.Description))
						{
							list.Add("   <size=" + text + "%><color=#c0c0c0ff>" + MagicItem.GetEffectDescriptionRange(val2, val2.GetValuesForRarity(ItemDataExtensions.GetRarity(item))) + "</color></size>");
						}
					}
				}
				__result = string.Join("\n", list);
			}
			catch (Exception arg)
			{
				if (!_augmentWarningLogged)
				{
					_augmentWarningLogged = true;
					Plugin.Log.LogWarning((object)$"Augment descriptions failed: {arg}");
				}
			}
		}

		private static void AugmentUpdate_Postfix(AugmentUI __instance)
		{
			if (((Behaviour)__instance).isActiveAndEnabled)
			{
				bool flag = AugmentDetailsActive();
				if (flag != _lastDetails)
				{
					_lastDetails = flag;
					__instance.OnAugmentIndexChanged();
				}
			}
		}

		private static void RefreshAugmentSelectors_Postfix(AugmentUI __instance)
		{
			//IL_0124: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				if (!Plugin.AugmentDetailsTooltips.Value)
				{
					return;
				}
				Tuple<InventoryItemListElement, int> singleSelectedItem = ((EnchantingTableUIPanelBase)__instance).AvailableItems.GetSingleSelectedItem<InventoryItemListElement>();
				ItemData obj = ((singleSelectedItem != null) ? singleSelectedItem.Item1.GetItem() : null);
				MagicItem val = ((obj != null) ? ItemDataExtensions.GetMagicItem(obj) : null);
				if (val == null)
				{
					return;
				}
				UITooltip val2 = ((Component)__instance).GetComponentInChildren<MultiSelectItemListElement>(true)?.Tooltip;
				if ((Object)(object)val2 == (Object)null || (Object)(object)val2.m_tooltipPrefab == (Object)null)
				{
					if (!_augmentPrefabLogged)
					{
						_augmentPrefabLogged = true;
						Plugin.Log.LogInfo((object)"Augment tooltip prefab unavailable; skipping effect tooltips.");
					}
					return;
				}
				if (_augmentSelectors == null)
				{
					_augmentSelectors = AccessTools.FieldRefAccess<AugmentUI, List<Toggle>>("_AugmentSelectors");
				}
				List<Toggle> list = _augmentSelectors.Invoke(__instance);
				MagicItemEffectDefinition val4 = default(MagicItemEffectDefinition);
				for (int i = 0; i < list.Count && i < val.Effects.Count; i++)
				{
					MagicItemEffect val3 = val.Effects[i];
					if (!((Object)(object)list[i] == (Object)null) && MagicItemEffectDefinitions.TryGet(val3.EffectType, ref val4) && !string.IsNullOrEmpty(val4.Description))
					{
						UITooltip obj2 = ((Component)list[i]).GetComponent<UITooltip>() ?? ((Component)list[i]).gameObject.AddComponent<UITooltip>();
						obj2.m_tooltipPrefab = val2.m_tooltipPrefab;
						obj2.m_topic = MagicItem.GetEffectText(val3, val.Rarity, false, (string)null);
						obj2.m_text = MagicItem.GetEffectDescription(val4, val3.EffectValue);
					}
				}
			}
			catch (Exception arg)
			{
				if (!_augmentTooltipWarningLogged)
				{
					_augmentTooltipWarningLogged = true;
					Plugin.Log.LogWarning((object)$"Augment tooltips failed: {arg}");
				}
			}
		}

		internal static void Apply(Harmony harmony)
		{
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: Expected O, but got Unknown
			//IL_0082: Unknown result type (might be due to invalid IL or missing references)
			//IL_008f: Expected O, but got Unknown
			//IL_00d4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e1: Expected O, but got Unknown
			//IL_0277: Unknown result type (might be due to invalid IL or missing references)
			//IL_0284: Expected O, but got Unknown
			//IL_0126: Unknown result type (might be due to invalid IL or missing references)
			//IL_0133: Expected O, but got Unknown
			//IL_02cc: Unknown result type (might be due to invalid IL or missing references)
			//IL_02d9: Expected O, but got Unknown
			//IL_0178: Unknown result type (might be due to invalid IL or missing references)
			//IL_0185: Expected O, but got Unknown
			//IL_0321: Unknown result type (might be due to invalid IL or missing references)
			//IL_032e: Expected O, but got Unknown
			//IL_01dc: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e9: Expected O, but got Unknown
			//IL_0376: Unknown result type (might be due to invalid IL or missing references)
			//IL_0383: Expected O, but got Unknown
			//IL_044d: Unknown result type (might be due to invalid IL or missing references)
			//IL_045a: Expected O, but got Unknown
			//IL_046f: Unknown result type (might be due to invalid IL or missing references)
			//IL_047c: Expected O, but got Unknown
			//IL_0490: Unknown result type (might be due to invalid IL or missing references)
			//IL_04a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_04b2: Expected O, but got Unknown
			//IL_04b2: Expected O, but got Unknown
			//IL_04c6: Unknown result type (might be due to invalid IL or missing references)
			//IL_04db: Unknown result type (might be due to invalid IL or missing references)
			//IL_04e8: Expected O, but got Unknown
			//IL_04e8: Expected O, but got Unknown
			//IL_04fc: Unknown result type (might be due to invalid IL or missing references)
			//IL_050a: Expected O, but got Unknown
			//IL_0551: Unknown result type (might be due to invalid IL or missing references)
			//IL_055f: Expected O, but got Unknown
			//IL_05b7: Unknown result type (might be due to invalid IL or missing references)
			//IL_05c5: Expected O, but got Unknown
			harmony.Patch((MethodBase)AccessTools.Method(typeof(Player), "AddKnownItem", new Type[1] { typeof(ItemData) }, (Type[])null), (HarmonyMethod)null, new HarmonyMethod(typeof(Patches), "AddKnownItem_Postfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			harmony.Patch((MethodBase)AccessTools.Method(typeof(Player), "OnSpawned", new Type[1] { typeof(bool) }, (Type[])null), (HarmonyMethod)null, new HarmonyMethod(typeof(Patches), "OnSpawned_Postfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			MethodInfo methodInfo = AccessTools.Method(typeof(RuneUI), "Awake", (Type[])null, (Type[])null);
			if (methodInfo == null)
			{
				Plugin.Log.LogWarning((object)"RuneUI.Awake not found; skipping Rune Awake adjustments.");
			}
			else
			{
				harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, new HarmonyMethod(typeof(Patches), "RuneAwake_Postfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			}
			MethodInfo methodInfo2 = AccessTools.Method(typeof(RuneUI), "OnEnable", (Type[])null, (Type[])null);
			if (methodInfo2 == null)
			{
				Plugin.Log.LogWarning((object)"RuneUI.OnEnable not found; skipping Rune OnEnable adjustments.");
			}
			else
			{
				harmony.Patch((MethodBase)methodInfo2, (HarmonyMethod)null, new HarmonyMethod(typeof(Patches), "RuneOnEnable_Postfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			}
			MethodInfo methodInfo3 = AccessTools.Method(typeof(RuneUI), "Update", (Type[])null, (Type[])null);
			if (methodInfo3 == null)
			{
				Plugin.Log.LogWarning((object)"RuneUI.Update not found; skipping Rune Update adjustments.");
			}
			else
			{
				harmony.Patch((MethodBase)methodInfo3, (HarmonyMethod)null, new HarmonyMethod(typeof(Patches), "RuneUpdate_Postfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			}
			MethodInfo methodInfo4 = AccessTools.Method(typeof(Localization), "SetupLanguage", new Type[1] { typeof(string) }, (Type[])null);
			if (methodInfo4 == null)
			{
				Plugin.Log.LogWarning((object)"Localization.SetupLanguage not found; skipping Rune label language refresh.");
			}
			else
			{
				harmony.Patch((MethodBase)methodInfo4, (HarmonyMethod)null, new HarmonyMethod(typeof(Patches), "ApplyRuneLabels", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			}
			try
			{
				object? obj = _localizationInstance?.GetValue(null);
				Localization val = (Localization)((obj is Localization) ? obj : null);
				if (val != null)
				{
					ApplyRuneLabels(val);
				}
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("Rune labels initial apply skipped: " + ex.Message));
			}
			MethodInfo methodInfo5 = AccessTools.Method(typeof(RuneUI), "SelectMode", (Type[])null, (Type[])null);
			if (methodInfo5 == null)
			{
				Plugin.Log.LogWarning((object)"RuneUI.SelectMode not found; skipping Rune set hints.");
			}
			else
			{
				harmony.Patch((MethodBase)methodInfo5, (HarmonyMethod)null, new HarmonyMethod(typeof(Patches), "RuneSelectMode_Postfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			}
			MethodInfo methodInfo6 = AccessTools.Method(typeof(EnchantingUIController), "GetAvailableAugmentEffects", (Type[])null, (Type[])null);
			if (methodInfo6 == null)
			{
				Plugin.Log.LogWarning((object)"GetAvailableAugmentEffects not found; skipping Augment descriptions.");
			}
			else
			{
				harmony.Patch((MethodBase)methodInfo6, (HarmonyMethod)null, new HarmonyMethod(typeof(Patches), "GetAvailableAugmentEffects_Postfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			}
			MethodInfo methodInfo7 = AccessTools.Method(typeof(AugmentUI), "Update", (Type[])null, (Type[])null);
			if (methodInfo7 == null)
			{
				Plugin.Log.LogWarning((object)"AugmentUI.Update not found; skipping Augment details key refresh.");
			}
			else
			{
				harmony.Patch((MethodBase)methodInfo7, (HarmonyMethod)null, new HarmonyMethod(typeof(Patches), "AugmentUpdate_Postfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			}
			MethodInfo methodInfo8 = AccessTools.Method(typeof(AugmentUI), "RefreshAugmentSelectors", (Type[])null, (Type[])null);
			if (methodInfo8 == null)
			{
				Plugin.Log.LogWarning((object)"RefreshAugmentSelectors not found; skipping Augment tooltips.");
			}
			else
			{
				harmony.Patch((MethodBase)methodInfo8, (HarmonyMethod)null, new HarmonyMethod(typeof(Patches), "RefreshAugmentSelectors_Postfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			}
			MethodInfo methodInfo9 = AccessTools.Method(typeof(LootRoller), "SelectDropType", (Type[])null, (Type[])null);
			MethodInfo methodInfo10 = AccessTools.Method(typeof(SacrificeUI), "OnSelectedItemsChanged", (Type[])null, (Type[])null);
			MethodInfo methodInfo11 = AccessTools.Method(typeof(EnchantingUIController), "LootRollSelectedItems", (Type[])null, (Type[])null);
			MethodInfo methodInfo12 = AccessTools.Method(typeof(EnchantingUIController), "GetPotentialItemRollsByCategory", (Type[])null, (Type[])null);
			MethodInfo methodInfo13 = AccessTools.Method(typeof(GatedItemTypeHelper), "GetCurrentOrLowerBiomeByDefeatedBossSettings", (Type[])null, (Type[])null);
			if (methodInfo9 == null || methodInfo10 == null || methodInfo11 == null || methodInfo12 == null || methodInfo13 == null)
			{
				throw new MissingMethodException("One of SelectDropType / OnSelectedItemsChanged / LootRollSelectedItems / GetPotentialItemRollsByCategory / GetCurrentOrLowerBiomeByDefeatedBossSettings not found");
			}
			harmony.Patch((MethodBase)methodInfo9, (HarmonyMethod)null, new HarmonyMethod(typeof(Patches), "SelectDropType_Postfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			harmony.Patch((MethodBase)methodInfo10, (HarmonyMethod)null, new HarmonyMethod(typeof(Patches), "OnSelectedItemsChanged_Postfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			harmony.Patch((MethodBase)methodInfo11, new HarmonyMethod(typeof(Patches), "LootRollSelectedItems_Prefix", (Type[])null), new HarmonyMethod(typeof(Patches), "LootRollSelectedItems_Postfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			harmony.Patch((MethodBase)methodInfo12, new HarmonyMethod(typeof(Patches), "PotentialRolls_Prefix", (Type[])null), new HarmonyMethod(typeof(Patches), "PotentialRolls_Postfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			harmony.Patch((MethodBase)methodInfo13, new HarmonyMethod(typeof(Patches), "BiomeCap_Prefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			MethodInfo methodInfo14 = AccessTools.Method(typeof(GatedItemTypeHelper), "CheckGateBossKill", (Type[])null, (Type[])null);
			if (methodInfo14 == null)
			{
				Plugin.Log.LogWarning((object)"CheckGateBossKill not found; skipping Identify item boss gate patch.");
			}
			else
			{
				harmony.Patch((MethodBase)methodInfo14, new HarmonyMethod(typeof(Patches), "CheckGateBossKill_Prefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			}
			MethodInfo methodInfo15 = AccessTools.Method(typeof(MultiSelectItemListElement), "SelectMaxQuantity", new Type[1] { typeof(bool) }, (Type[])null);
			if (methodInfo15 == null)
			{
				Plugin.Log.LogWarning((object)"SelectMaxQuantity not found; skipping Identify quantity patch.");
			}
			else
			{
				harmony.Patch((MethodBase)methodInfo15, new HarmonyMethod(typeof(Patches), "SelectMaxQuantity_Prefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			}
		}

		private static bool SelectMaxQuantity_Prefix(MultiSelectItemListElement __instance, bool noSound)
		{
			try
			{
				if (!Plugin.IdentifySelectsOne.Value)
				{
					return true;
				}
				if (_listElementItem == null)
				{
					_listElementItem = AccessTools.FieldRefAccess<MultiSelectItemListElement, IListElement>("_item");
				}
				IListElement obj = _listElementItem.Invoke(__instance);
				ItemData val = ((obj != null) ? obj.GetItem() : null);
				if (val == null || !ItemDataExtensions.IsUnidentified(val))
				{
					return true;
				}
				SacrificeUI componentInParent = ((Component)__instance).GetComponentInParent<SacrificeUI>();
				if ((Object)(object)componentInParent == (Object)null)
				{
					return true;
				}
				object value = Traverse.Create((object)componentInParent).Field("_sacrificeMode").GetValue();
				if (value == null || Convert.ToInt32(value) != 1)
				{
					return true;
				}
				if ((Object)(object)((Component)__instance).GetComponentInParent<MultiSelectItemList>() != (Object)(object)((EnchantingTableUIPanelBase)componentInParent).AvailableItems)
				{
					return true;
				}
				__instance.SelectQuantity(1, noSound);
				return false;
			}
			catch (Exception arg)
			{
				if (!_selectQuantityWarningLogged)
				{
					_selectQuantityWarningLogged = true;
					Plugin.Log.LogWarning((object)$"SelectMaxQuantity prefix failed: {arg}");
				}
				return true;
			}
		}

		private static bool BossCapActive()
		{
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			if (!Plugin.BlockIdentifyUntilBossKilled.Value)
			{
				return false;
			}
			if (LootRoller.CheatDisableGating)
			{
				return false;
			}
			return GatedItemTypeHelper.UsesBossBiomeCap(EpicLoot.GetGatedItemTypeMode());
		}

		internal static string BiomeName(Biome b)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			return BiomeDataManager.GetName(b);
		}

		internal static bool IsUnlockedByKey(Biome biome)
		{
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)ZoneSystem.instance != (Object)null)
			{
				return ZoneSystem.instance.GetGlobalKey("anri_identify_unlocked_" + BiomeName(biome));
			}
			return false;
		}

		internal static bool BossLocked(Biome biome)
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			if (!BossCapActive() || (int)biome == 0)
			{
				return false;
			}
			HashSet<Biome> bypassBiomes = _bypassBiomes;
			bool checkingBossLock = _checkingBossLock;
			_bypassBiomes = null;
			_checkingBossLock = true;
			try
			{
				GatedItemTypeMode gatedItemTypeMode = EpicLoot.GetGatedItemTypeMode();
				return GatedItemTypeHelper.GetCurrentOrLowerBiomeByDefeatedBossSettings(biome, gatedItemTypeMode) != biome;
			}
			finally
			{
				_bypassBiomes = bypassBiomes;
				_checkingBossLock = checkingBossLock;
			}
		}

		private static Biome BiomeOf(ItemData item)
		{
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			if (item == null || (Object)(object)item.m_dropPrefab == (Object)null || !ItemDataExtensions.IsUnidentified(item))
			{
				return (Biome)0;
			}
			return EnchantHelper.GetBiomeFromUnidentifiedItem(item);
		}

		private static HashSet<Biome> PayableBiomes(IEnumerable<ItemData> items)
		{
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			HashSet<Biome> hashSet = new HashSet<Biome>();
			foreach (ItemData item in items)
			{
				Biome val = BiomeOf(item);
				if ((int)val != 0 && BossLocked(val) && !IsUnlockedByKey(val))
				{
					hashSet.Add(val);
				}
			}
			return hashSet;
		}

		private static HashSet<Biome> IdentifyBiomes(IEnumerable<ItemData> items)
		{
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			HashSet<Biome> hashSet = new HashSet<Biome>();
			Dictionary<string, Plugin.UnlockCost> dictionary = Plugin.ParseUnlockCosts();
			if (items == null)
			{
				return hashSet;
			}
			foreach (ItemData item in items)
			{
				Biome val = BiomeOf(item);
				if (BossLocked(val) && (IsUnlockedByKey(val) || (Plugin.AllowPerItemPayment.Value && dictionary.ContainsKey(BiomeName(val)))))
				{
					hashSet.Add(val);
				}
			}
			return hashSet;
		}

		private static bool TableLevelBlocked(IEnumerable<ItemData> items, out string reason)
		{
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0061: Unknown result type (might be due to invalid IL or missing references)
			//IL_0083: Unknown result type (might be due to invalid IL or missing references)
			reason = null;
			if (!Plugin.RequireTableLevel.Value)
			{
				return false;
			}
			EnchantingTable val = (((Object)(object)EnchantingTableUI.instance != (Object)null) ? EnchantingTableUI.instance.SourceTable : null);
			int num = (((Object)(object)val != (Object)null) ? val.GetFeatureLevel((EnchantingFeature)0) : 0);
			Dictionary<string, int> dictionary = Plugin.ParseLevelMap();
			foreach (ItemData item in items)
			{
				Biome val2 = BiomeOf(item);
				if ((int)val2 != 0 && dictionary.TryGetValue(BiomeName(val2), out var value) && num < value)
				{
					reason = $"Sacrifice level {value} required to identify {BiomeName(val2)} gear (table is level {num}).";
					return true;
				}
			}
			return false;
		}

		private static int PerItemAmount(Plugin.UnlockCost c)
		{
			return Math.Max(1, Mathf.CeilToInt((float)c.Amount * Plugin.PerItemCostFraction.Value));
		}

		private static Dictionary<string, int> PerItemFee(List<Tuple<ItemData, int>> items, out string missingBiome)
		{
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			missingBiome = null;
			Dictionary<string, Plugin.UnlockCost> dictionary = Plugin.ParseUnlockCosts();
			Dictionary<string, int> dictionary2 = new Dictionary<string, int>();
			foreach (Tuple<ItemData, int> item in items)
			{
				Biome val = BiomeOf(item.Item1);
				if ((int)val != 0 && BossLocked(val) && !IsUnlockedByKey(val))
				{
					if (!dictionary.TryGetValue(BiomeName(val), out var value))
					{
						missingBiome = BiomeName(val);
						continue;
					}
					int num = PerItemAmount(value) * Math.Max(1, item.Item2);
					dictionary2[value.Item] = (dictionary2.TryGetValue(value.Item, out var value2) ? (value2 + num) : num);
				}
			}
			return dictionary2;
		}

		private static bool CanAfford(Dictionary<string, int> cost)
		{
			if ((Object)(object)Player.m_localPlayer == (Object)null)
			{
				return false;
			}
			if (Player.m_localPlayer.NoCostCheat())
			{
				return true;
			}
			InventoryManagement instance = InventoryManagement.Instance;
			foreach (KeyValuePair<string, int> item in cost)
			{
				if (instance.CountItem(item.Key) < item.Value)
				{
					return false;
				}
			}
			return true;
		}

		private static void Pay(Dictionary<string, int> cost)
		{
			if ((Object)(object)Player.m_localPlayer == (Object)null || Player.m_localPlayer.NoCostCheat())
			{
				return;
			}
			InventoryManagement instance = InventoryManagement.Instance;
			foreach (KeyValuePair<string, int> item in cost)
			{
				instance.RemoveItem(item.Key, item.Value);
			}
		}

		private static string DisplayName(string prefab)
		{
			GameObject val = (((Object)(object)ObjectDB.instance != (Object)null) ? ObjectDB.instance.GetItemPrefab(prefab) : null);
			ItemDrop val2 = (((Object)(object)val != (Object)null) ? val.GetComponent<ItemDrop>() : null);
			if (!((Object)(object)val2 != (Object)null))
			{
				return prefab;
			}
			return Localization.instance.Localize(val2.m_itemData.m_shared.m_name);
		}

		private static List<InventoryItemListElement> CostRows(Dictionary<string, int> cost)
		{
			//IL_0090: Unknown result type (might be due to invalid IL or missing references)
			//IL_0095: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a2: Expected O, but got Unknown
			List<InventoryItemListElement> list = new List<InventoryItemListElement>();
			foreach (KeyValuePair<string, int> item in cost)
			{
				GameObject val = (((Object)(object)ObjectDB.instance != (Object)null) ? ObjectDB.instance.GetItemPrefab(item.Key) : null);
				if ((Object)(object)val == (Object)null)
				{
					Plugin.Log.LogWarning((object)("Unlock cost item '" + item.Key + "' not found in ObjectDB; check UnlockCosts."));
					continue;
				}
				ItemData val2 = val.GetComponent<ItemDrop>().m_itemData.Clone();
				val2.m_dropPrefab = val;
				val2.m_stack = item.Value;
				list.Add(new InventoryItemListElement
				{
					Item = val2
				});
			}
			return list;
		}

		private static void SelectDropType_Postfix(LootDrop lootDrop, bool __1, bool cheatsActive, ref LootDropType __result)
		{
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			//IL_0063: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				if (!Plugin.GatedDropsBecomeUnidentified.Value || (int)__result != 0 || __1 || cheatsActive || LootRoller.CheatDisableGating || (Object)(object)Player.m_localPlayer == (Object)null || lootDrop == null || string.IsNullOrEmpty(lootDrop.Item))
				{
					return;
				}
				GatedItemTypeMode gatedItemTypeMode = EpicLoot.GetGatedItemTypeMode();
				if ((int)gatedItemTypeMode == 0 || GatedItemTypeHelper.IsItemAvailableUngated(lootDrop.Item, gatedItemTypeMode))
				{
					return;
				}
				GameObject val = (((Object)(object)ObjectDB.instance != (Object)null) ? ObjectDB.instance.GetItemPrefab(lootDrop.Item) : null);
				ItemDrop val2 = (((Object)(object)val != (Object)null) ? val.GetComponent<ItemDrop>() : null);
				if (!((Object)(object)val2 == (Object)null) && EpicLoot.IsAllowedMagicItemType(val2.m_itemData) && !(Random.value > Plugin.GatedDropChance.Value))
				{
					__result = (LootDropType)2;
					if (Plugin.Verbose.Value)
					{
						Plugin.Log.LogInfo((object)("Gated drop '" + lootDrop.Item + "' converted to an unidentified item of its own tier."));
					}
				}
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("SelectDropType postfix failed: " + ex.Message));
			}
		}

		private static bool BiomeCap_Prefix(Biome biome, ref Biome __result)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Expected I4, but got Unknown
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: Expected I4, but got Unknown
			try
			{
				if ((int)biome == 0)
				{
					return true;
				}
				if (_bypassBiomes != null && _bypassBiomes.Contains(biome))
				{
					__result = (Biome)(int)biome;
					return false;
				}
				if (!_checkingBossLock && Plugin.BlockIdentifyUntilBossKilled.Value && IsUnlockedByKey(biome))
				{
					__result = (Biome)(int)biome;
					return false;
				}
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("BiomeCap prefix failed: " + ex.Message));
			}
			return true;
		}

		private static bool CheckGateBossKill_Prefix(string itemName, GatedItemTypeMode mode, ref bool __result)
		{
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_0063: Unknown result type (might be due to invalid IL or missing references)
			//IL_013b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0161: Unknown result type (might be due to invalid IL or missing references)
			//IL_00dc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ed: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f2: Unknown result type (might be due to invalid IL or missing references)
			if (_identifyBiomes == null || _identifyBiomes.Count == 0)
			{
				return true;
			}
			try
			{
				if (!GatedItemTypeHelper.AllItemsWithDetails.TryGetValue(itemName, out var value) || value.RequiredBosses == null)
				{
					return true;
				}
				Biome val = (Biome)0;
				foreach (string requiredBoss in value.RequiredBosses)
				{
					string text = (string)_getBossKeyForMode.Invoke(null, new object[2] { requiredBoss, mode });
					if (string.IsNullOrEmpty(text) || ((Object)(object)ZoneSystem.instance != (Object)null && ZoneSystem.instance.GetGlobalKey(text)))
					{
						continue;
					}
					bool flag = false;
					foreach (KeyValuePair<Biome, List<string>> biomesToBossKey in GatedItemTypeHelper.BiomesToBossKeys)
					{
						if (biomesToBossKey.Value != null && biomesToBossKey.Value.Contains<string>(text, StringComparer.OrdinalIgnoreCase) && _identifyBiomes.Contains(biomesToBossKey.Key))
						{
							flag = true;
							val = biomesToBossKey.Key;
							break;
						}
					}
					if (!flag)
					{
						__result = true;
						return false;
					}
				}
				__result = false;
				if ((int)val != 0 && Plugin.Verbose.Value && !_identifyGateLogged)
				{
					_identifyGateLogged = true;
					Plugin.Log.LogInfo((object)("Identify: item boss gate lifted for " + BiomeName(val)));
				}
				return false;
			}
			catch (Exception ex)
			{
				if (!_itemGateWarningLogged)
				{
					_itemGateWarningLogged = true;
					Plugin.Log.LogWarning((object)("CheckGateBossKill prefix failed: " + ex.Message));
				}
				return true;
			}
		}

		private static void PotentialRolls_Prefix(List<ItemData> itemsSelected, out IdentifyState __state)
		{
			__state = new IdentifyState
			{
				Bypass = _bypassBiomes,
				Identify = _identifyBiomes,
				GateLogged = _identifyGateLogged
			};
			try
			{
				_identifyBiomes = IdentifyBiomes(itemsSelected);
				_identifyGateLogged = false;
				if (Plugin.AllowPerItemPayment.Value && itemsSelected != null)
				{
					_bypassBiomes = PayableBiomes(itemsSelected);
				}
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("PotentialRolls prefix failed: " + ex.Message));
			}
		}

		private static void PotentialRolls_Postfix(IdentifyState __state, ref List<InventoryItemListElement> __result)
		{
			//IL_009b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ad: Expected O, but got Unknown
			_bypassBiomes = __state.Bypass;
			_identifyBiomes = __state.Identify;
			_identifyGateLogged = __state.GateLogged;
			try
			{
				if (!Plugin.HideUndiscoveredPreview.Value || __result == null)
				{
					return;
				}
				List<InventoryItemListElement> list = new List<InventoryItemListElement>(__result.Count);
				foreach (InventoryItemListElement item2 in __result)
				{
					ItemData val = item2?.Item;
					if (val == null || ((Object)(object)Player.m_localPlayer != (Object)null && Player.m_localPlayer.IsMaterialKnown(val.m_shared.m_name)))
					{
						list.Add(item2);
						continue;
					}
					ItemData item = MakePlaceholder(val);
					list.Add(new InventoryItemListElement
					{
						Item = item
					});
				}
				__result = list;
			}
			catch (Exception ex)
			{
				if (!_previewWarningLogged)
				{
					_previewWarningLogged = true;
					Plugin.Log.LogWarning((object)("PotentialRolls preview failed: " + ex.Message));
				}
			}
		}

		private static ItemData MakePlaceholder(ItemData original)
		{
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Expected O, but got Unknown
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f2: Unknown result type (might be due to invalid IL or missing references)
			//IL_0182: Unknown result type (might be due to invalid IL or missing references)
			//IL_018c: Expected O, but got Unknown
			//IL_018d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0197: Expected O, but got Unknown
			//IL_0199: Unknown result type (might be due to invalid IL or missing references)
			ItemData val = original.Clone();
			val.m_shared = (SharedData)_memberwiseClone.Invoke(val.m_shared, null);
			SharedData shared = val.m_shared;
			shared.m_itemType = (ItemType)16;
			shared.m_weight = 0f;
			shared.m_scaleWeightByQuality = 0f;
			shared.m_maxStackSize = 1;
			shared.m_maxQuality = 1;
			shared.m_useDurability = false;
			shared.m_maxDurability = 0f;
			shared.m_durabilityPerLevel = 0f;
			shared.m_movementModifier = 0f;
			shared.m_eitrRegenModifier = 0f;
			shared.m_armor = 0f;
			shared.m_armorPerLevel = 0f;
			shared.m_blockPower = 0f;
			shared.m_blockPowerPerLevel = 0f;
			shared.m_deflectionForce = 0f;
			shared.m_deflectionForcePerLevel = 0f;
			shared.m_timedBlockBonus = 0f;
			shared.m_backstabBonus = 1f;
			shared.m_damages = default(DamageTypes);
			shared.m_damagesPerLevel = default(DamageTypes);
			shared.m_damageModifiers = new List<DamageModPair>();
			shared.m_food = 0f;
			shared.m_foodStamina = 0f;
			shared.m_foodEitr = 0f;
			shared.m_foodRegen = 0f;
			shared.m_foodBurnTime = 0f;
			shared.m_setName = "";
			shared.m_setSize = 0;
			shared.m_setStatusEffect = null;
			shared.m_equipStatusEffect = null;
			shared.m_consumeStatusEffect = null;
			shared.m_dlc = "";
			shared.m_teleportable = true;
			shared.m_questItem = false;
			shared.m_value = 0;
			shared.m_attack = new Attack();
			shared.m_secondaryAttack = new Attack();
			shared.m_skillType = (SkillType)0;
			shared.m_toolTier = 0;
			val.m_crafterID = 0L;
			val.m_crafterName = "";
			val.m_quality = 1;
			val.m_variant = 0;
			val.m_stack = 1;
			val.m_customData = new Dictionary<string, string>();
			shared.m_name = Plugin.UndiscoveredName.Value;
			shared.m_description = "You have not found this item yet.";
			shared.m_icons = (Sprite[])(object)new Sprite[1] { QuestionSprite };
			return val;
		}

		private static bool LootRollSelectedItems_Prefix(List<Tuple<ItemData, int>> items, ref List<InventoryItemListElement> __result, out object __state)
		{
			//IL_009f: Unknown result type (might be due to invalid IL or missing references)
			__state = null;
			_identifyBiomes = null;
			_identifyGateLogged = false;
			try
			{
				if (items == null || items.Count == 0)
				{
					return true;
				}
				List<ItemData> items2 = items.Select((Tuple<ItemData, int> t) => t.Item1).ToList();
				if (TableLevelBlocked(items2, out var reason))
				{
					return Refuse(reason, ref __result);
				}
				HashSet<Biome> hashSet = PayableBiomes(items2);
				_identifyBiomes = IdentifyBiomes(items2);
				_identifyGateLogged = false;
				if (hashSet.Count == 0)
				{
					return true;
				}
				if (!Plugin.AllowPerItemPayment.Value)
				{
					return Refuse("Locked: defeat the " + BiomeName(hashSet.First()) + " boss or unlock the tier at this table first.", ref __result);
				}
				string missingBiome;
				Dictionary<string, int> dictionary = PerItemFee(items, out missingBiome);
				if (missingBiome != null)
				{
					return Refuse("No unlock cost configured for " + missingBiome + " gear.", ref __result);
				}
				if (!CanAfford(dictionary))
				{
					return Refuse("Locked: you cannot afford the identify fee (" + string.Join(", ", dictionary.Select((KeyValuePair<string, int> kv) => $"{kv.Value} {DisplayName(kv.Key)}")) + ").", ref __result);
				}
				__state = dictionary;
				_bypassBiomes = hashSet;
				return true;
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("LootRollSelectedItems prefix failed: " + ex.Message));
				_bypassBiomes = null;
				_identifyBiomes = null;
				return true;
			}
		}

		private static void LootRollSelectedItems_Postfix(List<InventoryItemListElement> __result, object __state)
		{
			_bypassBiomes = null;
			_identifyBiomes = null;
			try
			{
				if (!(__state is Dictionary<string, int> dictionary) || __result == null || __result.Count <= 0)
				{
					return;
				}
				Pay(dictionary);
				if (Plugin.Verbose.Value)
				{
					Plugin.Log.LogInfo((object)("Identify fee paid: " + string.Join(", ", dictionary.Select((KeyValuePair<string, int> kv) => $"{kv.Value} {kv.Key}"))));
				}
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("LootRollSelectedItems postfix failed: " + ex.Message));
			}
		}

		private static bool Refuse(string reason, ref List<InventoryItemListElement> result)
		{
			if ((Object)(object)Player.m_localPlayer != (Object)null)
			{
				((Character)Player.m_localPlayer).Message((MessageType)2, reason, 0, (Sprite)null, false);
			}
			if (Plugin.Verbose.Value)
			{
				Plugin.Log.LogInfo((object)("Identify refused: " + reason));
			}
			result = null;
			return false;
		}

		private static void ApplyIdentifyUi(SacrificeUI ui, bool identify)
		{
			try
			{
				string want = Plugin.IdentifyCostLabel.Value;
				if (!string.IsNullOrEmpty(want))
				{
					if (!_costLabels.TryGetValue(ui, out var value) || (Object)(object)value == (Object)null)
					{
						string vanilla = Localization.instance.Localize("$mod_epicloot_enchantcost");
						value = ((IEnumerable<TMP_Text>)((Component)ui).GetComponentsInChildren<TMP_Text>(true)).FirstOrDefault((Func<TMP_Text, bool>)((TMP_Text t) => t.text == vanilla || t.text == _costLabelOriginal || t.text == want));
						if ((Object)(object)value != (Object)null)
						{
							_costLabels[ui] = value;
							if (_costLabelOriginal == null)
							{
								_costLabelOriginal = ((value.text == want) ? vanilla : value.text);
							}
						}
					}
					if ((Object)(object)value != (Object)null)
					{
						value.text = (identify ? want : (_costLabelOriginal ?? value.text));
					}
				}
				TMP_Text explainer = ui.Explainer;
				if ((Object)(object)explainer != (Object)null && Plugin.ExplainerFontScale.Value > 0f)
				{
					if (!_baseFontSize.TryGetValue(explainer, out var value2))
					{
						value2 = explainer.fontSize;
						_baseFontSize[explainer] = value2;
					}
					explainer.enableAutoSizing = false;
					explainer.fontSize = value2 * Plugin.ExplainerFontScale.Value;
				}
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("ApplyIdentifyUi failed: " + ex.Message));
			}
		}

		private static string PrefabName(ItemData d)
		{
			if (!((Object)(object)d?.m_dropPrefab != (Object)null))
			{
				return d?.m_shared?.m_name;
			}
			return ((Object)d.m_dropPrefab).name;
		}

		private static void OnSelectedItemsChanged_Postfix(SacrificeUI __instance)
		{
			//IL_0194: Unknown result type (might be due to invalid IL or missing references)
			//IL_0199: Unknown result type (might be due to invalid IL or missing references)
			//IL_019f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0582: Unknown result type (might be due to invalid IL or missing references)
			//IL_058c: Expected O, but got Unknown
			try
			{
				object value = Traverse.Create((object)__instance).Field("_sacrificeMode").GetValue();
				bool flag = value != null && Convert.ToInt32(value) == 1;
				Button unlockButton = GetUnlockButton(__instance);
				ApplyIdentifyUi(__instance, flag);
				if (!flag)
				{
					if (Object.op_Implicit((Object)(object)unlockButton))
					{
						((Component)unlockButton).gameObject.SetActive(false);
					}
					return;
				}
				List<Tuple<ItemData, int>> list = (from x in ((EnchantingTableUIPanelBase)__instance).AvailableItems.GetSelectedItems<IListElement>()
					select new Tuple<ItemData, int>(x.Item1.GetItem(), x.Item2)).ToList();
				int num = list.Sum((Tuple<ItemData, int> t) => Math.Max(1, t.Item2));
				if (num > 1)
				{
					Append(__instance, $"{num} items selected; the cost shown is the total for all of them.");
				}
				List<ItemData> items = list.Select((Tuple<ItemData, int> t) => t.Item1).ToList();
				if (TableLevelBlocked(items, out var reason))
				{
					((Selectable)((EnchantingTableUIPanelBase)__instance).MainButton).interactable = false;
					Append(__instance, reason);
					if (Object.op_Implicit((Object)(object)unlockButton))
					{
						((Component)unlockButton).gameObject.SetActive(false);
					}
					return;
				}
				HashSet<Biome> hashSet = PayableBiomes(items);
				if (hashSet.Count == 0)
				{
					if (Object.op_Implicit((Object)(object)unlockButton))
					{
						((Component)unlockButton).gameObject.SetActive(false);
					}
					return;
				}
				Dictionary<string, Plugin.UnlockCost> dictionary = Plugin.ParseUnlockCosts();
				Biome biome = hashSet.First();
				string text = BiomeName(biome);
				string text2 = "<color=#ff7a7a>" + text + " boss not defeated.</color>";
				if (!Plugin.AllowPerItemPayment.Value || !dictionary.TryGetValue(text, out var value2))
				{
					((Selectable)((EnchantingTableUIPanelBase)__instance).MainButton).interactable = false;
					Append(__instance, text2 + " Locked until the boss is killed" + (dictionary.ContainsKey(text) ? " or the tier is unlocked." : "."));
				}
				else
				{
					string missingBiome;
					Dictionary<string, int> dictionary2 = PerItemFee(list, out missingBiome);
					string text3 = __instance.IdentifyStyle.options[__instance.IdentifyStyle.value].text;
					Tuple<float, float> featureCurrentValue = EnchantingTableUI.instance.SourceTable.GetFeatureCurrentValue((EnchantingFeature)0);
					float num2 = ((featureCurrentValue.Item1 == 0f || float.IsNaN(featureCurrentValue.Item1)) ? 1f : (1f - featureCurrentValue.Item1 / 100f));
					List<InventoryItemListElement> list2 = new List<InventoryItemListElement>(((List<InventoryItemListElement>)AccessTools.Method(typeof(EnchantingUIController), "GetIdentifyCostForCategory", (Type[])null, (Type[])null).Invoke(null, new object[3] { text3, list, num2 })) ?? new List<InventoryItemListElement>());
					Dictionary<string, int> dictionary3 = new Dictionary<string, int>();
					foreach (InventoryItemListElement item2 in list2)
					{
						string text4 = PrefabName(item2.Item);
						if (text4 != null)
						{
							dictionary3[text4] = (dictionary3.TryGetValue(text4, out var value3) ? value3 : 0) + item2.Item.m_stack;
						}
					}
					foreach (KeyValuePair<string, int> kv in dictionary2)
					{
						InventoryItemListElement val = ((IEnumerable<InventoryItemListElement>)list2).FirstOrDefault((Func<InventoryItemListElement, bool>)((InventoryItemListElement r) => PrefabName(r.Item) == kv.Key));
						if (val != null)
						{
							ItemData item = val.Item;
							item.m_stack += kv.Value;
						}
						else
						{
							list2.AddRange(CostRows(new Dictionary<string, int> { { kv.Key, kv.Value } }));
						}
						dictionary3[kv.Key] = (dictionary3.TryGetValue(kv.Key, out var value4) ? value4 : 0) + kv.Value;
					}
					__instance.CostList.SetItems(list2.Cast<IListElement>().ToList());
					bool interactable = ((Selectable)((EnchantingTableUIPanelBase)__instance).MainButton).interactable && CanAfford(dictionary3);
					((Selectable)((EnchantingTableUIPanelBase)__instance).MainButton).interactable = interactable;
					Append(__instance, text2 + $" Identify fee: {PerItemAmount(value2)} {DisplayName(value2.Item)} per item, or unlock {text} permanently for {value2.Amount} {DisplayName(value2.Item)}.");
				}
				if (Object.op_Implicit((Object)(object)unlockButton) && dictionary.TryGetValue(text, out var value5))
				{
					((Component)unlockButton).gameObject.SetActive(true);
					SetLabel(unlockButton, $"Unlock {text} ({value5.Amount} {DisplayName(value5.Item)})");
					Dictionary<string, int> full = new Dictionary<string, int> { { value5.Item, value5.Amount } };
					((Selectable)unlockButton).interactable = CanAfford(full);
					((UnityEventBase)unlockButton.onClick).RemoveAllListeners();
					((UnityEvent)unlockButton.onClick).AddListener((UnityAction)delegate
					{
						//IL_0007: Unknown result type (might be due to invalid IL or missing references)
						DoUnlock(__instance, biome, full);
					});
				}
				else if (Object.op_Implicit((Object)(object)unlockButton))
				{
					((Component)unlockButton).gameObject.SetActive(false);
				}
			}
			catch (Exception arg)
			{
				Plugin.Log.LogWarning((object)$"OnSelectedItemsChanged postfix failed: {arg}");
			}
		}

		private static void Append(SacrificeUI ui, string line)
		{
			if ((Object)(object)ui.Explainer != (Object)null)
			{
				TMP_Text explainer = ui.Explainer;
				explainer.text = explainer.text + "\n" + line;
			}
		}

		private static void DoUnlock(SacrificeUI ui, Biome biome, Dictionary<string, int> cost)
		{
			//IL_0036: 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_007f: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				if (!CanAfford(cost))
				{
					Player localPlayer = Player.m_localPlayer;
					if (localPlayer != null)
					{
						((Character)localPlayer).Message((MessageType)2, "$msg_missingrequirement", 0, (Sprite)null, false);
					}
					return;
				}
				Pay(cost);
				ZoneSystem.instance.SetGlobalKey("anri_identify_unlocked_" + BiomeName(biome));
				Player localPlayer2 = Player.m_localPlayer;
				if (localPlayer2 != null)
				{
					((Character)localPlayer2).Message((MessageType)2, BiomeName(biome) + " identification unlocked for this world.", 0, (Sprite)null, false);
				}
				Plugin.Log.LogInfo((object)("Unlocked identify for " + BiomeName(biome) + " (paid " + string.Join(", ", cost.Select((KeyValuePair<string, int> kv) => $"{kv.Value} {kv.Key}")) + ")."));
				Traverse.Create((object)ui).Method("OnSelectedItemsChanged", Array.Empty<object>()).GetValue();
			}
			catch (Exception arg)
			{
				Plugin.Log.LogWarning((object)$"Unlock failed: {arg}");
			}
		}

		private static Button GetUnlockButton(SacrificeUI ui)
		{
			//IL_0064: Unknown result type (might be due to invalid IL or missing references)
			//IL_006e: Expected O, but got Unknown
			//IL_0081: Unknown result type (might be due to invalid IL or missing references)
			//IL_008c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0091: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a5: Unknown result type (might be due to invalid IL or missing references)
			if (_unlockButtons.TryGetValue(ui, out var value) && (Object)(object)value != (Object)null)
			{
				return value;
			}
			try
			{
				if ((Object)(object)((EnchantingTableUIPanelBase)ui).MainButton == (Object)null)
				{
					return null;
				}
				GameObject obj = Object.Instantiate<GameObject>(((Component)((EnchantingTableUIPanelBase)ui).MainButton).gameObject, ((Component)((EnchantingTableUIPanelBase)ui).MainButton).transform.parent, false);
				((Object)obj).name = "AnriUnlockTierButton";
				Button component = obj.GetComponent<Button>();
				component.onClick = new ButtonClickedEvent();
				RectTransform component2 = obj.GetComponent<RectTransform>();
				RectTransform component3 = ((Component)((EnchantingTableUIPanelBase)ui).MainButton).GetComponent<RectTransform>();
				Vector2 anchoredPosition = component3.anchoredPosition;
				Rect rect = component3.rect;
				component2.anchoredPosition = anchoredPosition + new Vector2(0f, ((Rect)(ref rect)).height + 6f);
				obj.SetActive(false);
				_unlockButtons[ui] = component;
				return component;
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("Could not create unlock button: " + ex.Message));
				return null;
			}
		}

		private static void SetLabel(Button btn, string text)
		{
			Text componentInChildren = ((Component)btn).GetComponentInChildren<Text>(true);
			if ((Object)(object)componentInChildren != (Object)null)
			{
				componentInChildren.text = text;
				return;
			}
			TMP_Text componentInChildren2 = ((Component)btn).GetComponentInChildren<TMP_Text>(true);
			if ((Object)(object)componentInChildren2 != (Object)null)
			{
				componentInChildren2.text = text;
			}
		}
	}
	internal static class ProgressionLoot
	{
		private sealed class Kill
		{
			internal string Prefab;

			internal int Stars;

			internal bool Processed;
		}

		private struct KillScope
		{
			internal int Depth;

			internal Kill Previous;

			internal bool SetCurrent;
		}

		[Serializable]
		[CompilerGenerated]
		private sealed class <>c
		{
			public static readonly <>c <>9 = new <>c();

			public static Func<string, string> <>9__32_0;

			public static Func<string, bool> <>9__32_1;

			public static Func<ItemRarity, ItemRarity> <>9__35_0;

			public static Func<ItemRarity, double> <>9__35_1;

			public static ConsoleEvent <>9__37_0;

			public static Func<LootDrop, double> <>9__44_0;

			internal string <UnlockPrefabs>b__32_0(string p)
			{
				return p.Trim();
			}

			internal bool <UnlockPrefabs>b__32_1(string p)
			{
				return p.Length > 0;
			}

			internal ItemRarity <Odds>b__35_0(ItemRarity r)
			{
				//IL_0000: Unknown result type (might be due to invalid IL or missing references)
				return r;
			}

			internal double <Odds>b__35_1(ItemRarity r)
			{
				return 0.0;
			}

			internal void <RegisterCommand>b__37_0(ConsoleEventArgs args)
			{
				int result = 0;
				if (args.Length > 1 && !int.TryParse(args[1], out result))
				{
					args.Context.AddString("Usage: lootodds [stars] [prefab]");
					return;
				}
				string text = ((args.Length > 2) ? args[2] : "");
				args.Context.AddString($"ProgressionLoot enabled={enabled.Value}, stars={result}, prefab={text}: {Table(Odds(result, text))}");
			}

			internal double <AddEquipment>b__44_0(LootDrop x)
			{
				return x.Weight;
			}
		}

		private const string PreviousUnlocks = "Epic:defeated_eikthyr:Eikthyr,Legendary:defeated_gdking:gd_king,Mythic:defeated_bonemass:Bonemass,Ancient:defeated_goblinking:GoblinKing";

		private const string DefaultUnlocks = "Epic:defeated_eikthyr:Eikthyr|BossAsmodeus_TW,Legendary:defeated_gdking:gd_king|BossSvalt_TW,Mythic:defeated_bonemass:Bonemass|BossVrykolathas_TW,Ancient:defeated_goblinking:GoblinKing";

		private const string PreviousBossFloors = "Eikthyr:Epic:0.10,gd_king:Legendary:0.05,Bonemass:Legendary:0.07,Dragon:Legendary:0.10,GoblinKing:Legendary:0.15,SeekerQueen:Legendary:0.20,Fader:Legendary:0.50";

		private const string DefaultBossFloors = "Eikthyr:Epic:0.10,gd_king:Legendary:0.05,Bonemass:Legendary:0.07,Dragon:Legendary:0.10,GoblinKing:Legendary:0.15,SeekerQueen:Legendary:0.20,Fader:Legendary:0.50,BossAsmodeus_TW:Epic:0.05,BossSvalt_TW:Epic:0.05,BossVrykolathas_TW:Legendary:0.02";

		private static ConfigEntry<bool> enabled;

		private static ConfigEntry<bool> verbose;

		private static ConfigEntry<string> baseline;

		private static ConfigEntry<string> unlocks;

		private static ConfigEntry<string> floors;

		private static ConfigEntry<float> growthStart;

		private static ConfigEntry<float> growthPerKill;

		private static ConfigEntry<float> starBase;

		private static ConfigEntry<float> apexChance;

		private static ConfigEntry<int> apexStars;

		private static readonly ItemRarity[] Rarities;

		[ThreadStatic]
		private static Kill current;

		[ThreadStatic]
		private static ItemRarity? forced;

		[ThreadStatic]
		private static int creatureRollDepth;

		private static readonly Type OutputType;

		private static MethodInfo Method(string name, params Type[] types)
		{
			return AccessTools.Method(typeof(LootRoller), name, (types.Length == 0) ? null : types, (Type[])null);
		}

		internal static void Apply(Harmony harmony, ConfigFile config)
		{
			//IL_01f2: Unknown result type (might be due to invalid IL or missing references)
			//IL_0209: Unknown result type (might be due to invalid IL or missing references)
			//IL_0214: Expected O, but got Unknown
			//IL_0214: Expected O, but got Unknown
			//IL_0277: Unknown result type (might be due to invalid IL or missing references)
			//IL_0285: Expected O, but got Unknown
			//IL_02e6: Unknown result type (might be due to invalid IL or missing references)
			//IL_02fd: Unknown result type (might be due to invalid IL or missing references)
			//IL_0308: Expected O, but got Unknown
			//IL_0308: Expected O, but got Unknown
			//IL_0329: Unknown result type (might be due to invalid IL or missing references)
			//IL_0337: Expected O, but got Unknown
			//IL_0358: Unknown result type (might be due to invalid IL or missing references)
			//IL_0366: Expected O, but got Unknown
			//IL_038f: Unknown result type (might be due to invalid IL or missing references)
			//IL_039c: Expected O, but got Unknown
			enabled = config.Bind<bool>("ProgressionLoot", "Enabled", true, Plugin.Synced("Replace creature magic equipment rolls with world progression odds."));
			baseline = config.Bind<string>("ProgressionLoot", "Baseline", "Magic:1,Rare:0.5,Epic:0.25,Legendary:0.2,Mythic:0.1,Ancient:0.01", Plugin.Synced("Rarity:percent pairs."));
			unlocks = config.Bind<string>("ProgressionLoot", "Unlocks", "Epic:defeated_eikthyr:Eikthyr|BossAsmodeus_TW,Legendary:defeated_gdking:gd_king|BossSvalt_TW,Mythic:defeated_bonemass:Bonemass|BossVrykolathas_TW,Ancient:defeated_goblinking:GoblinKing", Plugin.Synced("Rarity:globalkey:bossPrefab entries; separate multiple boss prefabs with | to sum their growth kill counters."));
			if (string.Equals(unlocks.Value, "Epic:defeated_eikthyr:Eikthyr,Legendary:defeated_gdking:gd_king,Mythic:defeated_bonemass:Bonemass,Ancient:defeated_goblinking:GoblinKing", StringComparison.Ordinal))
			{
				unlocks.Value = "Epic:defeated_eikthyr:Eikthyr|BossAsmodeus_TW,Legendary:defeated_gdking:gd_king|BossSvalt_TW,Mythic:defeated_bonemass:Bonemass|BossVrykolathas_TW,Ancient:defeated_goblinking:GoblinKing";
				Plugin.Log.LogInfo((object)"ProgressionLoot: migrated default Unlocks to include Monstrum miniboss growth.");
			}
			growthStart = config.Bind<float>("ProgressionLoot", "GrowthStart", 0.25f, Plugin.Synced("Starting multiplier for unlocked rarities."));
			growthPerKill = config.Bind<float>("ProgressionLoot", "GrowthPerKill", 0.25f, Plugin.Synced("Multiplier added per recorded boss kill, capped at one."));
			starBase = config.Bind<float>("ProgressionLoot", "StarBase", 5f, Plugin.Synced("Multiplier per ten stars."));
			apexStars = config.Bind<int>("ProgressionLoot", "ApexStars", 20, Plugin.Synced("Stars required for the Ancient floor."));
			apexChance = config.Bind<float>("ProgressionLoot", "ApexAncientChance", 0.1f, Plugin.Synced("Ancient probability floor when unlocked and at apex stars."));
			floors = config.Bind<string>("ProgressionLoot", "BossFloors", "Eikthyr:Epic:0.10,gd_king:Legendary:0.05,Bonemass:Legendary:0.07,Dragon:Legendary:0.10,GoblinKing:Legendary:0.15,SeekerQueen:Legendary:0.20,Fader:Legendary:0.50,BossAsmodeus_TW:Epic:0.05,BossSvalt_TW:Epic:0.05,BossVrykolathas_TW:Legendary:0.02", Plugin.Synced("BossPrefab:rarity:chance entries; a floor bypasses that rarity's unlock for that boss only."));
			if (string.Equals(floors.Value, "Eikthyr:Epic:0.10,gd_king:Legendary:0.05,Bonemass:Legendary:0.07,Dragon:Legendary:0.10,GoblinKing:Legendary:0.15,SeekerQueen:Legendary:0.20,Fader:Legendary:0.50", StringComparison.Ordinal))
			{
				floors.Value = "Eikthyr:Epic:0.10,gd_king:Legendary:0.05,Bonemass:Legendary:0.07,Dragon:Legendary:0.10,GoblinKing:Legendary:0.15,SeekerQueen:Legendary:0.20,Fader:Legendary:0.50,BossAsmodeus_TW:Epic:0.05,BossSvalt_TW:Epic:0.05,BossVrykolathas_TW:Legendary:0.02";
				Plugin.Log.LogInfo((object)"ProgressionLoot: migrated default BossFloors to include Monstrum altar bosses.");
			}
			verbose = config.Bind<bool>("ProgressionLoot", "Verbose", false, "Log probabilities and result once per creature kill.");
			harmony.Patch((MethodBase)AccessTools.Method(typeof(CharacterDrop), "OnDeath", (Type[])null, (Type[])null), new HarmonyMethod(typeof(ProgressionLoot), "BeginKill", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, new HarmonyMethod(typeof(ProgressionLoot), "EndKill", (Type[])null), (HarmonyMethod)null);
			harmony.Patch((MethodBase)Method("RollLootTableAndSpawnObjects", typeof(List<LootTable>), typeof(int), typeof(string), typeof(Vector3), typeof(bool)), new HarmonyMethod(typeof(ProgressionLoot), "RollCreature", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			harmony.Patch((MethodBase)AccessTools.Method(typeof(EpicLootDropsHelper), "OnCharacterDeath", new Type[4]
			{
				typeof(string),
				typeof(int),
				typeof(Vector3),
				typeof(bool)
			}, (Type[])null), new HarmonyMethod(typeof(ProgressionLoot), "BeginCreatureRoll", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, new HarmonyMethod(typeof(ProgressionLoot), "EndCreatureRoll", (Type[])null), (HarmonyMethod)null);
			harmony.Patch((MethodBase)Method("RollItemRarity"), new HarmonyMethod(typeof(ProgressionLoot), "ForceRarity", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			harmony.Patch((MethodBase)Method("SelectDropType"), new HarmonyMethod(typeof(ProgressionLoot), "ForceEquipment", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			harmony.Patch((MethodBase)AccessTools.Method(typeof(Terminal), "InitTerminal", (Type[])null, (Type[])null), (HarmonyMethod)null, new HarmonyMethod(typeof(ProgressionLoot), "RegisterCommand", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
		}

		private static string Clean(string prefab)
		{
			return (prefab ?? "").Replace("(Clone)", "").Trim();
		}

		private static void BeginKill(CharacterDrop __instance, out Kill __state)
		{
			__state = current;
			Character component = ((Component)__instance).GetComponent<Character>();
			if (enabled.Value && (Object)(object)component != (Object)null)
			{
				current = new Kill
				{
					Prefab = Clean(((Object)((Component)component).gameObject).name),
					Stars = component.GetLevel() - 1
				};
			}
		}

		private static void EndKill(Kill __state)
		{
			current = __state;
		}

		private static void BeginCreatureRoll(string characterName, int level, bool allowBonusRolls, out KillScope __state)
		{
			__state = new KillScope
			{
				Depth = creatureRollDepth,
				Previous = current
			};
			creatureRollDepth++;
			if (enabled.Value && allowBonusRolls && current == null && !string.IsNullOrEmpty(characterName))
			{
				current = new Kill
				{
					Prefab = Clean(characterName),
					Stars = Math.Max(0, level - 1)
				};
				__state.SetCurrent = true;
				if (verbose.Value)
				{
					Plugin.Log.LogInfo((object)$"ProgressionLoot: ragdoll/deferred roll scoped for {current.Prefab} ({current.Stars} stars)");
				}
			}
		}

		private static void EndCreatureRoll(KillScope __state)
		{
			creatureRollDepth = __state.Depth;
			current = __state.Previous;
		}

		private static bool Number(string value, out double result)
		{
			if (double.TryParse(value, NumberStyles.Float, CultureInfo.InvariantCulture, out result) && !double.IsNaN(result))
			{
				return !double.IsInfinity(result);
			}
			return false;
		}

		private static bool Rarity(string value, out ItemRarity result)
		{
			if (Enum.TryParse<ItemRarity>(value.Trim(), ignoreCase: true, out result))
			{
				return Rarities.Contains(result);
			}
			return false;
		}

		private static double Clamp(double value)
		{
			if (!double.IsNaN(value))
			{
				return Math.Max(0.0, Math.Min(1.0, value));
			}
			return 0.0;
		}

		private static Dictionary<ItemRarity, string[]> UnlockMap()
		{
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			Dictionary<ItemRarity, string[]> dictionary = new Dictionary<ItemRarity, string[]>();
			string[] array = (unlocks.Value ?? "").Split(new char[1] { ',' });
			for (int i = 0; i < array.Length; i++)
			{
				string[] array2 = array[i].Split(new char[1] { ':' });
				if (array2.Length == 3 && Rarity(array2[0], out var result))
				{
					dictionary[result] = array2;
				}
			}
			return dictionary;
		}

		private static IEnumerable<string> UnlockPrefabs(string[] parts)
		{
			return from p in parts[2].Split(new char[1] { '|' })
				select p.Trim() into p
				where p.Length > 0
				select p;
		}

		private static bool UnlockKeySatisfied(string[] parts)
		{
			if ((Object)(object)ZoneSystem.instance != (Object)null)
			{
				return ZoneSystem.instance.GetGlobalKey(parts[1].Trim().ToLowerInvariant());
			}
			return false;
		}

		internal static bool BossUniqueUnlocked(ItemRarity rarity, string prefab)
		{
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Invalid comparison between Unknown and I4
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			if ((int)rarity == 0 || (int)rarity == 1)
			{
				return true;
			}
			if (UnlockMap().TryGetValue(rarity, out var value) && !UnlockKeySatisfied(value))
			{
				return UnlockPrefabs(value).Any((string p) => string.Equals(Clean(p), Clean(prefab), StringComparison.OrdinalIgnoreCase));
			}
			return true;
		}

		internal static Dictionary<ItemRarity, double> Odds(int stars, string prefab)
		{
			//IL_00a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a9: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_026f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0152: Unknown result type (might be due to invalid IL or missing references)
			//IL_0155: Unknown result type (might be due to invalid IL or missing references)
			//IL_0289: Unknown result type (might be due to invalid IL or missing references)
			//IL_02c1: Unknown result type (might be due to invalid IL or missing references)
			//IL_02c4: Invalid comparison between Unknown and I4
			//IL_02f2: Unknown result type (might be due to invalid IL or missing references)
			//IL_0309: Unknown result type (might be due to invalid IL or missing references)
			Dictionary<ItemRarity, double> dictionary = Rarities.ToDictionary((ItemRarity r) => r, (ItemRarity r) => 0.0);
			string[] array = baseline.Value.Split(new char[1] { ',' });
			for (int num = 0; num < array.Length; num++)
			{
				string[] array2 = array[num].Split(new char[1] { ':' });
				if (array2.Length == 2 && Rarity(array2[0], out var result) && Number(array2[1], out var result2))
				{
					dictionary[result] = Math.Max(0.0, result2 / 100.0);
				}
			}
			Dictionary<ItemRarity, string[]> dictionary2 = UnlockMap();
			Dictionary<ItemRarity, double> dictionary3 = new Dictionary<ItemRarity, double>();
			array = floors.Value.Split(new char[1] { ',' });
			for (int num = 0; num < array.Length; num++)
			{
				string[] array3 = array[num].Split(new char[1] { ':' });
				if (array3.Length == 3 && string.Equals(array3[0].Trim(), Clean(prefab), StringComparison.OrdinalIgnoreCase) && Rarity(array3[1], out var result3) && Number(array3[2], out var result4))
				{
					dictionary3[result3] = Math.Max(dictionary3.TryGetValue(result3, out var value) ? value : 0.0, Clamp(result4));
				}
			}
			Dictionary<ItemRarity, double> dictionary4 = new Dictionary<ItemRarity, double>();
			ItemRarity[] rarities = Rarities;
			string s = default(string);
			foreach (ItemRarity val in rarities)
			{
				bool flag = true;
				double num2 = 1.0;
				if (dictionary2.TryGetValue(val, out var value2))
				{
					flag = UnlockKeySatisfied(value2);
					double num3 = 0.0;
					if ((Object)(object)ZoneSystem.instance != (Object)null)
					{
						foreach (string item in UnlockPrefabs(value2))
						{
							if (ZoneSystem.instance.GetGlobalKey("anri_bosskills_" + item.ToLowerInvariant(), ref s) && int.TryParse(s, out var result5))
							{
								num3 += (double)result5;
							}
						}
					}
					num2 = Clamp((double)growthStart.Value + (double)growthPerKill.Value * num3);
				}
				flag |= dictionary3.ContainsKey(val);
				double num4 = (flag ? Clamp(dictionary[val] * num2 * Math.Pow(Math.Max(0f, starBase.Value), (double)stars / 10.0)) : 0.0);
				if ((int)val == 5 && flag && stars >= apexStars.Value)
				{
					num4 = Math.Max(num4, Clamp(apexChance.Value));
				}
				if (dictionary3.TryGetValue(val, out var value3))
				{
					num4 = Math.Max(num4, value3);
				}
				dictionary4[val] = num4;
			}
			return dictionary4;
		}

		private unsafe static string Table(Dictionary<ItemRarity, double> odds)
		{
			return string.Join(", ", Rarities.Select((ItemRarity r) => ((object)(*(ItemRarity*)(&r))/*cast due to .constrained prefix*/).ToString() + "=" + (odds[r] * 100.0).ToString("0.####", CultureInfo.InvariantCulture) + "%"));
		}

		private static void RegisterCommand()
		{
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Expected O, but got Unknown
			object obj = <>c.<>9__37_0;
			if (obj == null)
			{
				ConsoleEvent val = delegate(ConsoleEventArgs args)
				{
					int result = 0;
					if (args.Length > 1 && !int.TryParse(args[1], out result))
					{
						args.Context.AddString("Usage: lootodds [stars] [prefab]");
					}
					else
					{
						string text = ((args.Length > 2) ? args[2] : "");
						args.Context.AddString($"ProgressionLoot enabled={enabled.Value}, stars={result}, prefab={text}: {Table(Odds(result, text))}");
					}
				};
				<>c.<>9__37_0 = val;
				obj = (object)val;
			}
			new ConsoleCommand("lootodds", "[stars] [prefab] Show world progression probabilities (independent top-down rolls)", (ConsoleEvent)obj, false, false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false);
		}

		private static bool ForceRarity(ref ItemRarity __result)
		{
			//IL_0014: Unknown re

RecipeDiscovery.dll

Decompiled a day ago
using System;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using 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: AssemblyCompany("RecipeDiscovery")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("RecipeDiscovery")]
[assembly: AssemblyTitle("RecipeDiscovery")]
[assembly: AssemblyVersion("1.0.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace RecipeDiscovery
{
	[BepInPlugin("anri.recipediscovery", "RecipeDiscovery", "1.0.0")]
	public class Plugin : BaseUnityPlugin
	{
		public const string GUID = "anri.recipediscovery";

		public const string NAME = "RecipeDiscovery";

		public const string VERSION = "1.0.0";

		private static ConfigEntry<bool> IgnoreStationLevel;

		private static ConfigEntry<bool> Verbose;

		private static ManualLogSource Log;

		private static bool _loggedException;

		private static readonly MethodInfo HaveRequirementItems = AccessTools.Method(typeof(Player), "HaveRequirementItems", new Type[4]
		{
			typeof(Recipe),
			typeof(bool),
			typeof(int),
			typeof(int)
		}, (Type[])null);

		private static readonly MethodInfo KnowStationLevel = AccessTools.Method(typeof(Player), "KnowStationLevel", (Type[])null, (Type[])null);

		private void Awake()
		{
			//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00de: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			IgnoreStationLevel = ((BaseUnityPlugin)this).Config.Bind<bool>("Discovery", "IgnoreStationLevel", true, "Learn a recipe as soon as all its ingredients are known materials, even if you have not yet seen the required crafting station level. Crafting still requires the real station.");
			Verbose = ((BaseUnityPlugin)this).Config.Bind<bool>("Discovery", "Verbose", false, "Log each recipe learned through this rule.");
			MethodInfo methodInfo = AccessTools.Method(typeof(Player), "HaveRequirements", new Type[4]
			{
				typeof(Recipe),
				typeof(bool),
				typeof(int),
				typeof(int)
			}, (Type[])null);
			if (methodInfo == null)
			{
				Log.LogWarning((object)"Player.HaveRequirements(Recipe, bool, int, int) not found; skipping recipe discovery patch.");
			}
			else
			{
				new Harmony("anri.recipediscovery").Patch((MethodBase)methodInfo, new HarmonyMethod(typeof(Plugin), "Prefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			}
			Log.LogInfo((object)"RecipeDiscovery 1.0.0 loaded.");
		}

		private static bool Prefix(Player __instance, Recipe recipe, bool discover, int qualityLevel, int amount, ref bool __result)
		{
			if (!discover || !IgnoreStationLevel.Value)
			{
				return true;
			}
			try
			{
				string dlc = recipe.m_item.m_itemData.m_shared.m_dlc;
				if (dlc.Length > 0 && !DLCMan.instance.IsDLCInstalled(dlc))
				{
					__result = false;
				}
				else
				{
					__result = (bool)HaveRequirementItems.Invoke(__instance, new object[4] { recipe, discover, qualityLevel, amount });
				}
				if ((Verbose.Value & __result) && (Object)(object)recipe.m_craftingStation != (Object)null && !(bool)KnowStationLevel.Invoke(__instance, new object[2]
				{
					recipe.m_craftingStation.m_name,
					recipe.m_minStationLevel
				}))
				{
					Log.LogInfo((object)$"Learned {recipe.m_item.m_itemData.m_shared.m_name} before seeing {recipe.m_craftingStation.m_name} level {recipe.m_minStationLevel}");
				}
				return false;
			}
			catch (Exception arg)
			{
				if (!_loggedException)
				{
					_loggedException = true;
					Log.LogWarning((object)$"Recipe discovery failed; falling back to vanilla: {arg}");
				}
				return true;
			}
		}
	}
}

SpawnLimiter.dll

Decompiled a day ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using 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: AssemblyCompany("SpawnLimiter")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("SpawnLimiter")]
[assembly: AssemblyTitle("SpawnLimiter")]
[assembly: AssemblyVersion("1.0.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace SpawnLimiter
{
	[BepInPlugin("anri.spawnlimiter", "SpawnLimiter", "1.1.0")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public class Plugin : BaseUnityPlugin
	{
		internal sealed class Rule
		{
			internal HashSet<int> PrefabHashes;

			internal int Max;

			internal float Radius;

			internal string DisplayName;
		}

		[HarmonyPatch(typeof(CreatureSpawner), "Spawn", new Type[] { })]
		private static class CreatureSpawnerPatch
		{
			private static bool Prefix(CreatureSpawner __instance, ref ZNetView __result)
			{
				//IL_0026: Unknown result type (might be due to invalid IL or missing references)
				//IL_002b: Unknown result type (might be due to invalid IL or missing references)
				//IL_0037: Unknown result type (might be due to invalid IL or missing references)
				//IL_003d: Expected O, but got Unknown
				//IL_0083: Unknown result type (might be due to invalid IL or missing references)
				try
				{
					if (!Enabled.Value)
					{
						return true;
					}
					string name = ((Object)__instance.m_creaturePrefab).name;
					Vector3 position = ((Component)__instance).transform.position;
					ZNetView val = (ZNetView)CreatureView.GetValue(__instance);
					ZDO val2 = (((Object)(object)val != (Object)null) ? val.GetZDO() : null);
					long ticks = ZNet.instance.GetTime().Ticks;
					if (!IsDeferred((val2 != null) ? val2.GetLong(DeferKey, 0L) : 0, ticks))
					{
						if (!TryDefer(name, position, ticks, out var deadline))
						{
							return true;
						}
						if (val2 != null)
						{
							val2.Set(DeferKey, deadline);
						}
					}
					__result = null;
					return false;
				}
				catch (Exception error)
				{
					WarnOnce("CreatureSpawner.Spawn", error);
					return true;
				}
			}
		}

		[HarmonyPatch(typeof(SpawnSystem), "Spawn", new Type[]
		{
			typeof(SpawnData),
			typeof(Vector3),
			typeof(bool)
		})]
		private static class SpawnSystemPatch
		{
			private static bool Prefix(SpawnSystem __instance, SpawnData critter, Vector3 spawnPoint)
			{
				//IL_003c: Unknown result type (might be due to invalid IL or missing references)
				//IL_0042: Expected O, but got Unknown
				//IL_005b: Unknown result type (might be due to invalid IL or missing references)
				//IL_0060: 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)
				//IL_0069: Unknown result type (might be due to invalid IL or missing references)
				//IL_0071: Unknown result type (might be due to invalid IL or missing references)
				//IL_0078: Unknown result type (might be due to invalid IL or missing references)
				//IL_00f9: Unknown result type (might be due to invalid IL or missing references)
				try
				{
					if (!Enabled.Value)
					{
						return true;
					}
					string name = ((Object)critter.m_prefab).name;
					int stableHashCode = StringExtensionMethods.GetStableHashCode("anri_sl_defer_" + name);
					ZNetView val = (ZNetView)SpawnSystemView.GetValue(__instance);
					ZDO val2 = (((Object)(object)val != (Object)null) ? val.GetZDO() : null);
					Vector2s zone = ZoneSystem.GetZone(((Component)__instance).transform.position);
					(ZDOID, int) key = (new ZDOID((long)zone.x, (uint)zone.y), StringExtensionMethods.GetStableHashCode(name));
					ZoneDeferrals.TryGetValue(key, out var value);
					long ticks = ZNet.instance.GetTime().Ticks;
					long deadline = ((val2 != null) ? val2.GetLong(stableHashCode, value) : value);
					if (IsDeferred(deadline, ticks))
					{
						if (val2 != null && value != 0L)
						{
							val2.Set(stableHashCode, deadline);
							ZoneDeferrals.Remove(key);
						}
						return false;
					}
					ZoneDeferrals.Remove(key);
					if (!TryDefer(name, spawnPoint, ticks, out deadline))
					{
						return true;
					}
					if (val2 != null)
					{
						val2.Set(stableHashCode, deadline);
					}
					else
					{
						ZoneDeferrals[key] = deadline;
					}
					return false;
				}
				catch (Exception error)
				{
					WarnOnce("SpawnSystem.Spawn", error);
					return true;
				}
			}
		}

		[HarmonyPatch(typeof(BaseAI), "Awake")]
		private static class BaseAiPatch
		{
			private static void Prefix(BaseAI __instance, out bool __state)
			{
				__state = false;
				try
				{
					if (Enabled.Value && CatchAllSpawns.Value)
					{
						ZNetView component = ((Component)__instance).GetComponent<ZNetView>();
						__state = (Object)(object)component != (Object)null && component.IsValid() && component.IsOwner() && component.GetZDO().GetLong(ZDOVars.s_spawnTime, 0L) == 0;
					}
				}
				catch (Exception error)
				{
					WarnOnce("BaseAI.Awake prefix", error);
				}
			}

			private static void Postfix(BaseAI __instance, Character ___m_character, bool __state)
			{
				//IL_0086: Unknown result type (might be due to invalid IL or missing references)
				//IL_008b: Unknown result type (might be due to invalid IL or missing references)
				//IL_008c: Unknown result type (might be due to invalid IL or missing references)
				//IL_008e: Unknown result type (might be due to invalid IL or missing references)
				try
				{
					if (!Enabled.Value || !CatchAllSpawns.Value || !__state)
					{
						return;
					}
					ZNetView component = ((Component)__instance).GetComponent<ZNetView>();
					if ((Object)(object)component == (Object)null || !component.IsValid() || !component.IsOwner())
					{
						return;
					}
					ZDO zDO = component.GetZDO();
					if (!(___m_character is Player) && (!((Object)(object)___m_character != (Object)null) || !___m_character.IsTamed()) && !zDO.GetBool(ZDOVars.s_tamed, false))
					{
						string prefabName = Utils.GetPrefabName(((Component)__instance).gameObject);
						Vector3 position = ((Component)__instance).transform.position;
						if (Blocked(prefabName, position, zDO.m_uid, out var _))
						{
							((MonoBehaviour)Instance).StartCoroutine(DestroyBlockedSpawn(((Component)__instance).gameObject));
						}
					}
				}
				catch (Exception error)
				{
					WarnOnce("BaseAI.Awake postfix", error);
				}
			}
		}

		public const string GUID = "anri.spawnlimiter";

		public const string NAME = "SpawnLimiter";

		public const string VERSION = "1.1.0";

		private const string DefaultRules = "StoneGolem|ObsidianGolem_TW:1:200";

		private static Plugin Instance;

		private static ManualLogSource Log;

		private static ConfigEntry<bool> Enabled;

		private static ConfigEntry<bool> Verbose;

		private static ConfigEntry<bool> CatchAllSpawns;

		private static volatile List<Rule> Rules = new List<Rule>();

		private static readonly List<ZDO> TempZdos = new List<ZDO>();

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

		private static ConfigEntry<float> FirstMinMinutes;

		private static ConfigEntry<float> FirstMaxMinutes;

		private static ConfigEntry<float> MinutesPerCreature;

		private static readonly int DeferKey = StringExtensionMethods.GetStableHashCode("anri_sl_defer");

		private static readonly Dictionary<(ZDOID zone, int prefabHash), long> ZoneDeferrals = new Dictionary<(ZDOID, int), long>();

		private static readonly FieldInfo CreatureView = AccessTools.Field(typeof(CreatureSpawner), "m_nview");

		private static readonly FieldInfo SpawnSystemView = AccessTools.Field(typeof(SpawnSystem), "m_nview");

		private void Awake()
		{
			//IL_0168: Unknown result type (might be due to invalid IL or missing references)
			Instance = this;
			Log = ((BaseUnityPlugin)this).Logger;
			Enabled = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enabled", true, AdminOnly("Enable creature group spawn limits."));
			Verbose = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Verbose", false, "Log one Info line for each new deferral or catch-all removal. Local setting; not synced.");
			CatchAllSpawns = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "CatchAllSpawns", true, AdminOnly("Also enforce limits on newly created creatures from other spawn paths through BaseAI.Awake."));
			AcceptableValueRange<float> range = new AcceptableValueRange<float>(0.01f, 1440f);
			FirstMinMinutes = ((BaseUnityPlugin)this).Config.Bind<float>("Defer", "FirstMinMinutes", 5f, AdminOnly("Minimum random delay in minutes with one creature (also used for empty groups with max zero).", (AcceptableValueBase)(object)range));
			FirstMaxMinutes = ((BaseUnityPlugin)this).Config.Bind<float>("Defer", "FirstMaxMinutes", 10f, AdminOnly("Maximum random delay in minutes with one creature. Reversed bounds are sorted.", (AcceptableValueBase)(object)range));
			MinutesPerCreature = ((BaseUnityPlugin)this).Config.Bind<float>("Defer", "MinutesPerCreature", 10f, AdminOnly("Delay in minutes per creature when two or more remain.", (AcceptableValueBase)(object)range));
			ConfigEntry<string> rules = ((BaseUnityPlugin)this).Config.Bind<string>("Rules", "Rules", "StoneGolem|ObsidianGolem_TW:1:200", AdminOnly("Rules separated by ; in prefab1|prefab2:max:radiusMeters format. Max is the count at which deferral starts. A spawn must satisfy every matching rule. Max must be nonnegative; radius must be positive and is clamped to 300 m because the nearby-sector lookup cannot fully cover radii above approximately 320 m. Tamed creatures do not count. Reparsed when changed, including server sync."));
			rules.SettingChanged += delegate
			{
				ParseRules(rules.Value);
			};
			ParseRules(rules.Value);
			new Harmony("anri.spawnlimiter").PatchAll(typeof(Plugin).Assembly);
			Log.LogInfo((object)(string.Format("{0} {1} loaded. {2} rule(s): ", "SpawnLimiter", "1.1.0", Rules.Count) + string.Join("; ", Rules.Select((Rule r) => $"{r.DisplayName}:{r.Max}:{r.Radius.ToString(CultureInfo.InvariantCulture)}"))));
		}

		private static ConfigDescription AdminOnly(string description, AcceptableValueBase range = null)
		{
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Expected O, but got Unknown
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Expected O, but got Unknown
			return new ConfigDescription(description, range, new object[1] { (object)new ConfigurationManagerAttributes
			{
				IsAdminOnly = true
			} });
		}

		private static void ParseRules(string text)
		{
			List<Rule> list = new List<Rule>();
			string[] array = (text ?? "").Split(new char[1] { ';' });
			for (int i = 0; i < array.Length; i++)
			{
				string text2 = array[i].Trim();
				if (text2.Length == 0)
				{
					continue;
				}
				string[] array2 = text2.Split(new char[1] { ':' });
				if (array2.Length != 3 || !int.TryParse(array2[1].Trim(), NumberStyles.Integer, CultureInfo.InvariantCulture, out var result) || result < 0 || !float.TryParse(array2[2].Trim(), NumberStyles.Float, CultureInfo.InvariantCulture, out var result2) || float.IsNaN(result2) || float.IsInfinity(result2) || result2 <= 0f)
				{
					Log.LogWarning((object)("Ignoring malformed rule '" + text2 + "'."));
					continue;
				}
				string[] array3 = (from n in array2[0].Split(new char[1] { '|' })
					select n.Trim() into n
					where n.Length > 0
					select n).ToArray();
				if (array3.Length == 0)
				{
					Log.LogWarning((object)("Ignoring malformed rule '" + text2 + "'."));
					continue;
				}
				list.Add(new Rule
				{
					PrefabHashes = new HashSet<int>(array3.Select((string n) => StringExtensionMethods.GetStableHashCode(n))),
					Max = result,
					Radius = Math.Min(result2, 300f),
					DisplayName = string.Join("|", array3)
				});
			}
			Rules = list;
		}

		internal static int CountGroup(Rule rule, Vector3 center, ZDOID exclude)
		{
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_006c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0071: Unknown result type (might be due to invalid IL or missing references)
			TempZdos.Clear();
			ZDOMan.instance.FindSectorObjects(ZoneSystem.GetZone(center), SimulationDistance.OriginalDistance, TempZdos, (List<ZDO>)null);
			int num = 0;
			foreach (ZDO tempZdo in TempZdos)
			{
				if (tempZdo.m_uid != exclude && rule.PrefabHashes.Contains(tempZdo.GetPrefab()) && !tempZdo.GetBool(ZDOVars.s_tamed, false) && Vector3.Distance(tempZdo.GetPosition(), center) <= rule.Radius)
				{
					num++;
				}
			}
			return num;
		}

		internal static bool Blocked(string prefabName, Vector3 center, ZDOID exclude, out Rule rule)
		{
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			int stableHashCode = StringExtensionMethods.GetStableHashCode(prefabName);
			foreach (Rule rule2 in Rules)
			{
				if (rule2.PrefabHashes.Contains(stableHashCode) && CountGroup(rule2, center, exclude) >= rule2.Max)
				{
					rule = rule2;
					return true;
				}
			}
			rule = null;
			return false;
		}

		private static void LogBlocked(string prefab, Vector3 center, ZDOID exclude, Rule rule)
		{
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: 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_0045: Unknown result type (might be due to invalid IL or missing references)
			if (Verbose.Value)
			{
				Log.LogInfo((object)string.Format(CultureInfo.InvariantCulture, "[SpawnLimiter] blocked {0} at ({1:0.#},{2:0.#}): {3} of group '{4}' within {5} m", prefab, center.x, center.z, CountGroup(rule, center, exclude), rule.DisplayName, rule.Radius));
			}
		}

		private static bool IsDeferred(long deadline, long now)
		{
			if (deadline > now)
			{
				return deadline - now <= 864000000000L;
			}
			return false;
		}

		private static bool TryDefer(string prefab, Vector3 center, long now, out long deadline)
		{
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00df: Unknown result type (might be due to invalid IL or missing references)
			int stableHashCode = StringExtensionMethods.GetStableHashCode(prefab);
			foreach (Rule rule in Rules)
			{
				if (!rule.PrefabHashes.Contains(stableHashCode))
				{
					continue;
				}
				int num = CountGroup(rule, center, ZDOID.None);
				if (num >= rule.Max)
				{
					double num2 = ((num <= 1) ? ((double)Random.Range(Math.Min(FirstMinMinutes.Value, FirstMaxMinutes.Value), Math.Max(FirstMinMinutes.Value, FirstMaxMinutes.Value))) : ((double)num * (double)MinutesPerCreature.Value));
					deadline = now + (long)(num2 * 600000000.0);
					if (Verbose.Value)
					{
						Log.LogInfo((object)string.Format(CultureInfo.InvariantCulture, "[SpawnLimiter] deferred {0} at ({1:0.#},{2:0.#}) for {3:0.0} min: {4} of group '{5}' within {6} m", prefab, center.x, center.z, num2, num, rule.DisplayName, rule.Radius));
					}
					return true;
				}
			}
			deadline = 0L;
			return false;
		}

		private static void WarnOnce(string patch, Exception error)
		{
			if (Warnings.Add(patch))
			{
				Log.LogWarning((object)(patch + " spawn limit failed; allowing vanilla behavior: " + error.Message));
			}
		}

		private static IEnumerator DestroyBlockedSpawn(GameObject go)
		{
			WaitForEndOfFrame val;
			try
			{
				val = new WaitForEndOfFrame();
			}
			catch (Exception error)
			{
				WarnOnce("BaseAI.Awake deferred destroy", error);
				yield break;
			}
			yield return val;
			try
			{
				if ((Object)(object)go == (Object)null)
				{
					yield break;
				}
				ZNetView component = go.GetComponent<ZNetView>();
				if ((Object)(object)component == (Object)null || !component.IsValid() || !component.IsOwner())
				{
					yield break;
				}
				ZDO zDO = component.GetZDO();
				long num = ZNet.instance.GetTime().Ticks - zDO.GetLong(ZDOVars.s_spawnTime, 0L);
				if (num < -50000000 || num > 50000000)
				{
					yield break;
				}
				Character component2 = go.GetComponent<Character>();
				if (component2 is Player || ((Object)(object)component2 != (Object)null && component2.IsTamed()) || zDO.GetBool(ZDOVars.s_tamed, false))
				{
					yield break;
				}
				string prefabName = Utils.GetPrefabName(go);
				Vector3 position = go.transform.position;
				if (Blocked(prefabName, position, zDO.m_uid, out var rule))
				{
					LogBlocked(prefabName, position, zDO.m_uid, rule);
					CharacterDrop component3 = go.GetComponent<CharacterDrop>();
					if ((Object)(object)component3 != (Object)null)
					{
						Object.Destroy((Object)(object)component3);
					}
					ZNetScene.instance.Destroy(go);
				}
			}
			catch (Exception error2)
			{
				WarnOnce("BaseAI.Awake deferred destroy", error2);
			}
		}
	}
}