Decompiled source of KingsAndGodsAdditions v1.6.8

KingsAndGodsAdditions.dll

Decompiled 4 days ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Text;
using System.Text.RegularExpressions;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using HarmonyLib;
using Jotunn.Configs;
using Jotunn.Entities;
using Jotunn.Managers;
using Microsoft.CodeAnalysis;
using Steamworks;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.SceneManagement;
using UnityEngine.UI;

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

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace KingAndGodsAdditional
{
	internal static class BossEnvironments
	{
		internal static readonly string[] Names = new string[8] { "Eikthyr", "GDKing", "Bonemass", "Moder", "GoblinKing", "Queen", "Fader", "DN_Bossroom" };

		private static bool registered;

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

		internal static string Prefab(string name)
		{
			return "KAG_Environment_" + name;
		}

		internal static void Install(Harmony harmony)
		{
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: Expected O, but got Unknown
			//IL_00a3: 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)
			//IL_00bc: Expected O, but got Unknown
			//IL_00e4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fd: Expected O, but got Unknown
			PrefabManager.OnVanillaPrefabsAvailable += Register;
			harmony.Patch((MethodBase)AccessTools.Method(typeof(EnvMan), "GetEnvironmentOverride", (Type[])null, (Type[])null), (HarmonyMethod)null, new HarmonyMethod(typeof(BossEnvironments), "Environment", (Type[])null)
			{
				priority = 0
			}, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			harmony.Patch((MethodBase)AccessTools.Method(AccessTools.TypeByName("Localization"), "Localize", new Type[2]
			{
				typeof(string),
				typeof(string[])
			}, (Type[])null), new HarmonyMethod(typeof(BossEnvironments), "NewDay", (Type[])null)
			{
				priority = 800
			}, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			harmony.Patch((MethodBase)AccessTools.Method(typeof(ItemDrop), "Interact", (Type[])null, (Type[])null), new HarmonyMethod(typeof(BossEnvironments), "NoPickup", (Type[])null)
			{
				priority = 800
			}, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
		}

		private static void Register()
		{
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Expected O, but got Unknown
			//IL_007e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0084: Expected O, but got Unknown
			if (registered)
			{
				return;
			}
			string[] names = Names;
			foreach (string text in names)
			{
				string text2 = Prefab(text);
				ItemConfig val = new ItemConfig();
				val.Name = "Environment " + text;
				val.Description = "Persistent boss environment: " + text + ". Radius: 200 metres. Place with spawn " + Prefab(text) + ". Remove with an admin world-edit tool.";
				val.StackSize = 1;
				CustomItem val2 = new CustomItem(text2, "Stone", val);
				GameObject itemPrefab = val2.ItemPrefab;
				ItemDrop itemDrop = val2.ItemDrop;
				itemDrop.m_autoPickup = false;
				itemDrop.m_autoDestroy = false;
				Rigidbody component = itemPrefab.GetComponent<Rigidbody>();
				if ((Object)(object)component != (Object)null)
				{
					component.isKinematic = true;
					component.useGravity = false;
				}
				ZNetView component2 = itemPrefab.GetComponent<ZNetView>();
				component2.m_persistent = true;
				component2.m_distant = true;
				itemPrefab.AddComponent<BossEnvironmentMarker>().EnvironmentName = text;
				if (!ItemManager.Instance.AddItem(val2))
				{
					throw new InvalidOperationException("Cannot register " + Prefab(text));
				}
			}
			registered = true;
		}

		private static bool NewDay(string __0, ref string __result)
		{
			if (__0 != "$msg_newday")
			{
				return true;
			}
			__result = "A new day has dawned";
			return false;
		}

		private static bool NoPickup(ItemDrop __instance, ref bool __result)
		{
			if ((Object)(object)((Component)__instance).GetComponent<BossEnvironmentMarker>() == (Object)null)
			{
				return true;
			}
			__result = false;
			return false;
		}

		private static void Environment(EnvMan __instance, ref string __result)
		{
			//IL_0025: 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_0063: 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_006e: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)Player.m_localPlayer == (Object)null || !string.IsNullOrEmpty(__instance.m_debugEnv))
			{
				return;
			}
			Vector3 position = ((Component)Player.m_localPlayer).transform.position;
			BossEnvironmentMarker bossEnvironmentMarker = null;
			float num = 40000f;
			foreach (BossEnvironmentMarker item in BossEnvironmentMarker.Active)
			{
				if (!((Object)(object)item == (Object)null) && item.Live)
				{
					Vector3 val = ((Component)item).transform.position - position;
					float sqrMagnitude = ((Vector3)(ref val)).sqrMagnitude;
					if (!(sqrMagnitude > num) && GetEnv.Invoke(__instance, new object[1] { item.EnvironmentName }) != null)
					{
						bossEnvironmentMarker = item;
						num = sqrMagnitude;
					}
				}
			}
			if ((Object)(object)bossEnvironmentMarker != (Object)null)
			{
				__result = bossEnvironmentMarker.EnvironmentName;
			}
		}
	}
	public sealed class BossEnvironmentMarker : MonoBehaviour
	{
		public const float Radius = 200f;

		public string EnvironmentName;

		internal static readonly HashSet<BossEnvironmentMarker> Active = new HashSet<BossEnvironmentMarker>();

		private ZNetView view;

		internal bool Live
		{
			get
			{
				if ((Object)(object)view != (Object)null)
				{
					return view.IsValid();
				}
				return false;
			}
		}

		private void Awake()
		{
			view = ((Component)this).GetComponent<ZNetView>();
		}

		private void OnEnable()
		{
			Active.Add(this);
		}

		private void OnDisable()
		{
			Active.Remove(this);
		}

		private void OnDestroy()
		{
			Active.Remove(this);
		}
	}
	public sealed class ConfigAuthority
	{
		private readonly Dictionary<string, ConfigEntryBase> entries = new Dictionary<string, ConfigEntryBase>();

		private readonly Dictionary<ConfigEntryBase, string> originals = new Dictionary<ConfigEntryBase, string>();

		private readonly Dictionary<ConfigEntryBase, string> locked = new Dictionary<ConfigEntryBase, string>();

		private readonly Dictionary<ConfigFile, bool> saveFlags = new Dictionary<ConfigFile, bool>();

		private readonly List<ConfigFile> files = new List<ConfigFile>();

		private readonly Dictionary<ConfigFile, DateTime> stamps = new Dictionary<ConfigFile, DateTime>();

		private bool applying;

		private bool active;

		public bool Received { get; private set; }

		public void Discover(BaseUnityPlugin owner)
		{
			AddFile("kag", owner.Config);
			if (Chainloader.PluginInfos.TryGetValue("com.nandor.racesofvalheim", out var value))
			{
				BaseUnityPlugin instance = value.Instance;
				if ((Object)(object)instance != (Object)null)
				{
					AddFile("races", instance.Config);
				}
			}
		}

		private static string Key(string group, string section, string key)
		{
			return group + "\t" + Convert.ToBase64String(Encoding.UTF8.GetBytes(section)) + "\t" + Convert.ToBase64String(Encoding.UTF8.GetBytes(key));
		}

		private void AddFile(string group, ConfigFile file)
		{
			if (!files.Contains(file))
			{
				files.Add(file);
				file.SettingChanged += Changed;
			}
			foreach (KeyValuePair<ConfigDefinition, ConfigEntryBase> item in file)
			{
				entries[Key(group, item.Key.Section, item.Key.Key)] = item.Value;
			}
		}

		public bool Manages(ConfigFile file)
		{
			return files.Contains(file);
		}

		public void ReloadServerFiles(bool force)
		{
			foreach (ConfigFile file in files)
			{
				DateTime lastWriteTimeUtc = File.GetLastWriteTimeUtc(file.ConfigFilePath);
				if (force || !stamps.TryGetValue(file, out var value) || !(value == lastWriteTimeUtc))
				{
					file.Reload();
					stamps[file] = File.GetLastWriteTimeUtc(file.ConfigFilePath);
				}
			}
		}

		public void SetServerValue(string group, string section, string key, string value)
		{
			if (!entries.TryGetValue(Key(group, section, key), out var value2))
			{
				throw new FormatException("Unknown configuration entry");
			}
			TomlTypeConverter.ConvertToValue(value, value2.SettingType);
			value2.SetSerializedValue(value);
			value2.ConfigFile.Save();
		}

		public string Snapshot()
		{
			StringBuilder stringBuilder = new StringBuilder("KAGCFG2\n");
			foreach (KeyValuePair<string, ConfigEntryBase> item in entries.OrderBy<KeyValuePair<string, ConfigEntryBase>, string>((KeyValuePair<string, ConfigEntryBase> p) => p.Key, StringComparer.Ordinal))
			{
				stringBuilder.Append(item.Key).Append('\t').Append(Convert.ToBase64String(Encoding.UTF8.GetBytes(item.Value.GetSerializedValue())))
					.Append('\n');
			}
			string text = stringBuilder.ToString();
			if (text.Length > 1048576)
			{
				throw new InvalidOperationException("Server configuration exceeds 1 MiB");
			}
			return text;
		}

		public static Dictionary<string, string> Decode(string text)
		{
			if (text == null || text.Length > 1048576 || !text.StartsWith("KAGCFG2\n", StringComparison.Ordinal))
			{
				throw new FormatException("Invalid configuration packet");
			}
			Dictionary<string, string> dictionary = new Dictionary<string, string>(StringComparer.Ordinal);
			string[] array = text.Substring(8).Split('\n');
			foreach (string text2 in array)
			{
				if (text2.Length != 0)
				{
					string[] array2 = text2.Split('\t');
					if (array2.Length != 4 || (array2[0] != "kag" && array2[0] != "races"))
					{
						throw new FormatException("Invalid config row");
					}
					Encoding.UTF8.GetString(Convert.FromBase64String(array2[1]));
					Encoding.UTF8.GetString(Convert.FromBase64String(array2[2]));
					string key = array2[0] + "\t" + array2[1] + "\t" + array2[2];
					if (dictionary.ContainsKey(key))
					{
						throw new FormatException("Duplicate configuration key");
					}
					dictionary.Add(key, Encoding.UTF8.GetString(Convert.FromBase64String(array2[3])));
				}
			}
			return dictionary;
		}

		public void Apply(string text)
		{
			Dictionary<string, string> values = Decode(text);
			if (values.Count != entries.Count || entries.Keys.Any((string k) => !values.ContainsKey(k)))
			{
				throw new InvalidOperationException("Server/client configuration schema differs. Install matching RacesOfValheim and KAG versions.");
			}
			foreach (KeyValuePair<string, string> item in values)
			{
				TomlTypeConverter.ConvertToValue(item.Value, entries[item.Key].SettingType);
			}
			applying = true;
			try
			{
				foreach (ConfigFile file in files)
				{
					if (!saveFlags.ContainsKey(file))
					{
						saveFlags[file] = file.SaveOnConfigSet;
					}
					file.SaveOnConfigSet = false;
				}
				foreach (KeyValuePair<string, string> item2 in values)
				{
					ConfigEntryBase val = entries[item2.Key];
					if (!originals.ContainsKey(val))
					{
						originals[val] = val.GetSerializedValue();
					}
					val.SetSerializedValue(item2.Value);
					locked[val] = val.GetSerializedValue();
				}
				active = true;
				Received = true;
			}
			finally
			{
				applying = false;
			}
		}

		private void Changed(object sender, SettingChangedEventArgs args)
		{
			if (!active || applying || !locked.TryGetValue(args.ChangedSetting, out var value) || args.ChangedSetting.GetSerializedValue() == value)
			{
				return;
			}
			applying = true;
			try
			{
				args.ChangedSetting.SetSerializedValue(value);
			}
			finally
			{
				applying = false;
			}
		}

		public void Enforce()
		{
			if (!active)
			{
				return;
			}
			applying = true;
			try
			{
				foreach (ConfigFile file in files)
				{
					file.SaveOnConfigSet = false;
				}
				foreach (KeyValuePair<ConfigEntryBase, string> item in locked)
				{
					if (item.Key.GetSerializedValue() != item.Value)
					{
						item.Key.SetSerializedValue(item.Value);
					}
				}
			}
			finally
			{
				applying = false;
			}
		}

		public void Restore()
		{
			active = false;
			Received = false;
			applying = true;
			try
			{
				foreach (KeyValuePair<ConfigEntryBase, string> original in originals)
				{
					original.Key.SetSerializedValue(original.Value);
				}
				foreach (KeyValuePair<ConfigFile, bool> saveFlag in saveFlags)
				{
					saveFlag.Key.SaveOnConfigSet = saveFlag.Value;
				}
				originals.Clear();
				locked.Clear();
				saveFlags.Clear();
			}
			finally
			{
				applying = false;
			}
		}
	}
	internal static class DayNightCycle
	{
		private static ConfigEntry<int> day;

		private static ConfigEntry<int> night;

		internal static long CycleSeconds => (long)(day.Value + night.Value) * 60L;

		internal static double Dawn => (double)night.Value / (2.0 * (double)(day.Value + night.Value));

		internal static void Install(Harmony harmony)
		{
			Install(harmony, "FixedUpdate", "SetCycle");
			Install(harmony, "RescaleDayFraction", "RescaleTime");
			Install(harmony, "GetMorningStartSec", "Morning");
			Install(harmony, "SkipToMorning", "Sleep");
		}

		private static void Install(Harmony harmony, string target, string prefix)
		{
			//IL_0052: 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_0067: Expected O, but got Unknown
			MethodInfo methodInfo = AccessTools.Method(typeof(EnvMan), target, (Type[])null, (Type[])null);
			if (methodInfo == null)
			{
				throw new MissingMethodException(typeof(EnvMan).FullName, target);
			}
			harmony.Patch((MethodBase)methodInfo, new HarmonyMethod(typeof(DayNightCycle), prefix, (Type[])null)
			{
				priority = 0
			}, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			Patches patchInfo = Harmony.GetPatchInfo((MethodBase)methodInfo);
			if (patchInfo == null || !patchInfo.Prefixes.Any((Patch p) => p.owner == "kingsandgods.additional" && p.PatchMethod.Name == prefix))
			{
				throw new InvalidOperationException("Time patch was not installed: " + target);
			}
		}

		internal static void Bind(ConfigFile config)
		{
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Expected O, but got Unknown
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			//IL_005d: Expected O, but got Unknown
			day = config.Bind<int>("Time", "DayDurationMinutes", 21, new ConfigDescription("Real minutes from dawn to dusk. Server authoritative. Change before starting the world; changing cycle length changes the displayed day number.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 1440), Array.Empty<object>()));
			night = config.Bind<int>("Time", "NightDurationMinutes", 9, new ConfigDescription("Real minutes from dusk to dawn. Server authoritative. Sleeping still skips the night. Change before starting the world.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 1440), Array.Empty<object>()));
		}

		internal static float Rescale(float fraction, double dawn)
		{
			if ((double)fraction < dawn)
			{
				return (float)((double)fraction / dawn * 0.25);
			}
			if ((double)fraction <= 1.0 - dawn)
			{
				return (float)(0.25 + ((double)fraction - dawn) / (1.0 - 2.0 * dawn) * 0.5);
			}
			return (float)(0.75 + ((double)fraction - (1.0 - dawn)) / dawn * 0.25);
		}

		private static void SetCycle(ref long ___m_dayLengthSec)
		{
			___m_dayLengthSec = CycleSeconds;
		}

		private static bool RescaleTime(float __0, ref float __result)
		{
			__result = Rescale(__0, Dawn);
			return false;
		}

		private static bool Morning(int __0, ref double __result)
		{
			__result = (double)__0 * (double)CycleSeconds + (double)night.Value * 30.0;
			return false;
		}

		private static bool Sleep(ref bool ___m_skipTime, ref double ___m_skipToTime, ref double ___m_timeSkipSpeed)
		{
			if ((Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer())
			{
				return false;
			}
			double timeSeconds = ZNet.instance.GetTimeSeconds();
			double num = (double)night.Value * 30.0;
			double num2 = (Math.Floor((timeSeconds - num) / (double)CycleSeconds) + 1.0) * (double)CycleSeconds + num;
			___m_skipTime = true;
			___m_skipToTime = num2;
			___m_timeSkipSpeed = (num2 - timeSeconds) / 12.0;
			return false;
		}
	}
	internal static class DeathRules
	{
		private static ConfigEntry<bool> keep;

		[ThreadStatic]
		private static int dying;

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

		internal static void Bind(ConfigFile config)
		{
			keep = config.Bind<bool>("Death", "KeepInventoryOnDeath", true, "Server setting: keep all carried inventory on death, prevent new tombstones and death map markers. Existing graves and pins are not deleted. Skill/food death penalties are unchanged.");
		}

		internal static void Install(Harmony h)
		{
			//IL_0027: 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_0049: 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_005b: Expected O, but got Unknown
			//IL_005b: Expected O, but got Unknown
			h.Patch((MethodBase)AccessTools.Method(typeof(Player), "OnDeath", (Type[])null, (Type[])null), new HarmonyMethod(typeof(DeathRules), "Begin", (Type[])null)
			{
				priority = 800
			}, (HarmonyMethod)null, (HarmonyMethod)null, new HarmonyMethod(typeof(DeathRules), "End", (Type[])null)
			{
				priority = 0
			}, (HarmonyMethod)null);
			Patch(h, AccessTools.Method(typeof(Player), "CreateTombStone", (Type[])null, (Type[])null), "NoGrave");
			Patch(h, AccessTools.Method(typeof(PlayerProfile), "SetDeathPoint", (Type[])null, (Type[])null), "NoDeathPoint");
			Patch(h, AccessTools.Method(typeof(PlayerProfile), "HaveDeathPoint", (Type[])null, (Type[])null), "NoSkull");
			Patch(h, AccessTools.Method(typeof(Minimap), "AddPin", (Type[])null, (Type[])null), "NoPin");
			Patch(h, AccessTools.Method(typeof(ZoneSystem), "GetGlobalKey", new Type[1] { typeof(GlobalKeys) }, (Type[])null), "KeepKey");
			Type type = AccessTools.TypeByName("PassiveTree.Permadeath");
			if (type != null)
			{
				Patch(h, AccessTools.Method(type, "DestroyItems", (Type[])null, (Type[])null), "NoGrave");
			}
		}

		private static void Patch(Harmony h, MethodInfo method, string prefix)
		{
			//IL_0028: 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_0041: Expected O, but got Unknown
			if (method == null)
			{
				throw new MissingMethodException("Death rule target: " + prefix);
			}
			h.Patch((MethodBase)method, new HarmonyMethod(typeof(DeathRules), prefix, (Type[])null)
			{
				priority = 800
			}, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
		}

		private static void Begin(out int __state)
		{
			__state = dying;
			if (Enabled)
			{
				dying++;
			}
		}

		private static void End(int __state)
		{
			dying = __state;
		}

		private static bool NoGrave()
		{
			return !Enabled;
		}

		private static bool NoDeathPoint()
		{
			if (Enabled)
			{
				return dying == 0;
			}
			return true;
		}

		private static bool NoSkull(ref bool __result)
		{
			if (!Enabled)
			{
				return true;
			}
			__result = false;
			return false;
		}

		private static bool NoPin(PinType __1, ref PinData __result)
		{
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Invalid comparison between Unknown and I4
			if (!Enabled || dying == 0 || (int)__1 != 4)
			{
				return true;
			}
			__result = null;
			return false;
		}

		private static bool KeepKey(GlobalKeys __0, ref bool __result)
		{
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Invalid comparison between Unknown and I4
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Invalid comparison between Unknown and I4
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Invalid comparison between Unknown and I4
			if (!Enabled)
			{
				return true;
			}
			if ((int)__0 == 23)
			{
				__result = true;
				return false;
			}
			if ((int)__0 == 20 || (int)__0 == 21)
			{
				__result = false;
				return false;
			}
			return true;
		}
	}
	internal static class DemigodRunes
	{
		private static Type ui;

		private static Type demigod;

		private static Type configs;

		private static Action<string> log;

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

		private static ItemData usedRune;

		private static string selectedClass;

		private static bool opening;

		private static bool registered;

		private static IList Defaults => (IList)AccessTools.Property(demigod, "DemigodDefaults").GetValue(null, null);

		private static string Name(object entry)
		{
			return (string)AccessTools.Property(entry.GetType(), "Name").GetValue(entry, null);
		}

		private static bool Hidden(object entry)
		{
			return (bool)AccessTools.Property(entry.GetType(), "Hidden").GetValue(entry, null);
		}

		internal static string PrefabName(string name)
		{
			return "KAG_Rune_" + name;
		}

		private static int Index(string name)
		{
			IList defaults = Defaults;
			for (int i = 0; i < defaults.Count; i++)
			{
				if (Name(defaults[i]) == name && !Hidden(defaults[i]))
				{
					return i;
				}
			}
			return -1;
		}

		internal static void Install(Harmony harmony, Action<string> logger)
		{
			//IL_008a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0096: Expected O, but got Unknown
			ui = AccessTools.TypeByName("NorseDemigods.CustomUI");
			if (!(ui == null))
			{
				log = logger;
				demigod = AccessTools.TypeByName("NorseDemigods.Demigod");
				configs = AccessTools.TypeByName("NorseDemigods.Configs");
				MethodInfo methodInfo = AccessTools.Method(AccessTools.Inner(AccessTools.TypeByName("NorseDemigods.NorseDemigods"), "Player_OnSpawned_Patch"), "Postfix", (Type[])null, (Type[])null);
				if (methodInfo == null)
				{
					throw new MissingMethodException("NorseDemigods starter runestone hook");
				}
				harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, (HarmonyMethod)null, new HarmonyMethod(typeof(DemigodRunes), "NoStarterRune", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null);
				log("NorseDemigods automatic starter runestone disabled; pickup and crafting preserved.");
				Hook(harmony, AccessTools.TypeByName("NorseDemigods.NorseDemigods"), "AddCustomItems", null, "Register");
				Hook(harmony, ui, "RunestoneMenuOpen", "BeforeOpen", "AfterOpen");
				Hook(harmony, ui, "BuildRunestoneMenu", null, "HideDropdown");
				Hook(harmony, ui, "RunestoneMenuUpdatePage", "LockPage", null);
				Hook(harmony, ui, "RunestoneMenuChooseDemigod", "ValidChoice", null);
				Hook(harmony, ui, "RunestoneMenuConfirmChoice", "ValidChoice", null);
				Hook(harmony, ui, "RunestoneDemigodChosen", "ConsumeRune", null);
				Hook(harmony, typeof(Humanoid), "UseItem", "UseRune", null);
			}
		}

		private static void Hook(Harmony harmony, Type type, string name, string pre, string post)
		{
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//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)
			MethodInfo methodInfo = AccessTools.Method(type, name, (Type[])null, (Type[])null);
			if (methodInfo == null)
			{
				throw new MissingMethodException(type.FullName, name);
			}
			harmony.Patch((MethodBase)methodInfo, (pre == null) ? ((HarmonyMethod)null) : new HarmonyMethod(typeof(DemigodRunes), pre, (Type[])null)
			{
				priority = 800
			}, (post == null) ? ((HarmonyMethod)null) : new HarmonyMethod(typeof(DemigodRunes), post, (Type[])null)
			{
				priority = 0
			}, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
		}

		private static IEnumerable<CodeInstruction> NoStarterRune(IEnumerable<CodeInstruction> instructions)
		{
			List<CodeInstruction> list = new List<CodeInstruction>(instructions);
			int num = 0;
			for (int i = 0; i + 1 < list.Count; i++)
			{
				FieldInfo fieldInfo = list[i].operand as FieldInfo;
				MethodInfo methodInfo = list[i + 1].operand as MethodInfo;
				if (!(list[i].opcode != OpCodes.Ldsfld) && !(fieldInfo == null) && !(fieldInfo.DeclaringType.FullName != "NorseDemigods.Configs") && !(fieldInfo.Name != "RunestoneSpawnAtStart") && !(methodInfo == null) && !(methodInfo.Name != "get_Value") && !(methodInfo.ReturnType != typeof(bool)))
				{
					list[i].opcode = OpCodes.Ldc_I4_0;
					list[i].operand = null;
					list[i + 1].opcode = OpCodes.Nop;
					list[i + 1].operand = null;
					num++;
				}
			}
			if (num != 1)
			{
				throw new InvalidOperationException("Unsupported NorseDemigods starter-rune branch: " + num);
			}
			return list;
		}

		private static void Register()
		{
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0061: Expected O, but got Unknown
			//IL_00a4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00aa: Expected O, but got Unknown
			//IL_00b4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ba: Expected O, but got Unknown
			//IL_00c1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c8: Expected O, but got Unknown
			if (registered)
			{
				return;
			}
			if (ItemManager.Instance.GetItem("DemigodRunestone") == null)
			{
				throw new InvalidOperationException("DemigodRunestone is not registered.");
			}
			foreach (object @default in Defaults)
			{
				if (!Hidden(@default))
				{
					string text = Name(@default);
					string text2 = PrefabName(text);
					ItemConfig val = new ItemConfig();
					val.Name = "Rune " + text;
					val.Description = "Use to read about " + text + " and choose this demigod.";
					val.Amount = 1;
					val.Requirements = (RequirementConfig[])(object)new RequirementConfig[2]
					{
						new RequirementConfig("Stone", 1, 0, true),
						new RequirementConfig("Resin", 1, 0, true)
					};
					CustomItem val2 = new CustomItem(text2, "DemigodRunestone", val);
					if (!ItemManager.Instance.AddItem(val2))
					{
						throw new InvalidOperationException("Cannot register " + text2);
					}
					classes.Add(text2, text);
					log("Registered Rune " + text + ": " + text2);
				}
			}
			registered = true;
		}

		private static bool UseRune(Humanoid __instance, ItemData item)
		{
			if (item == null || (Object)(object)item.m_dropPrefab == (Object)null || !classes.TryGetValue(((Object)item.m_dropPrefab).name, out var value))
			{
				return true;
			}
			if ((Object)(object)__instance != (Object)(object)Player.m_localPlayer || !__instance.GetInventory().ContainsItem(item))
			{
				return false;
			}
			if (Index(value) < 0)
			{
				return false;
			}
			usedRune = item;
			selectedClass = value;
			opening = true;
			try
			{
				AccessTools.Method(ui, "RunestoneMenuOpen", (Type[])null, (Type[])null).Invoke(null, null);
			}
			finally
			{
				opening = false;
			}
			return false;
		}

		private static void BeforeOpen()
		{
			if (opening)
			{
				return;
			}
			usedRune = null;
			selectedClass = null;
			foreach (object @default in Defaults)
			{
				if (!Hidden(@default))
				{
					selectedClass = Name(@default);
					break;
				}
			}
			if ((Object)(object)Player.m_localPlayer != (Object)null)
			{
				usedRune = ((Humanoid)Player.m_localPlayer).GetInventory().GetAllItems().Find((ItemData i) => (Object)(object)i.m_dropPrefab != (Object)null && ((Object)i.m_dropPrefab).name == "DemigodRunestone");
			}
		}

		private static void AfterOpen()
		{
			AccessTools.Property(ui, "Owner").SetValue(null, Player.m_localPlayer, null);
			int num = Index(selectedClass);
			if (num >= 0)
			{
				AccessTools.Method(ui, "RunestoneMenuUpdatePage", (Type[])null, (Type[])null).Invoke(null, new object[1] { num });
			}
			HideDropdown();
		}

		private static void HideDropdown()
		{
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Expected O, but got Unknown
			GameObject val = (GameObject)AccessTools.Field(ui, "RunestoneMenuPanel").GetValue(null);
			if (!((Object)(object)val == (Object)null))
			{
				Dropdown[] componentsInChildren = val.GetComponentsInChildren<Dropdown>(true);
				for (int i = 0; i < componentsInChildren.Length; i++)
				{
					((Component)componentsInChildren[i]).gameObject.SetActive(false);
				}
			}
		}

		private static void LockPage(ref int __0)
		{
			int num = Index(selectedClass);
			if (num >= 0)
			{
				__0 = num;
			}
		}

		private static bool ValidChoice()
		{
			Player localPlayer = Player.m_localPlayer;
			int num = Index(selectedClass);
			if ((Object)(object)localPlayer == (Object)null || usedRune == null || !((Humanoid)localPlayer).GetInventory().ContainsItem(usedRune) || num < 0)
			{
				return false;
			}
			object obj = Defaults[num];
			if ((bool)AccessTools.Method(demigod, "IsDemigodDisabled", (Type[])null, (Type[])null).Invoke(null, new object[1] { obj }))
			{
				return false;
			}
			AccessTools.Field(ui, "RunestoneMenuPage").SetValue(null, num);
			return true;
		}

		private static bool ConsumeRune()
		{
			if (selectedClass == null)
			{
				return true;
			}
			object value = AccessTools.Field(configs, "RunestoneConsumable").GetValue(null);
			if ((bool)AccessTools.Property(value.GetType(), "Value").GetValue(value, null) && (Object)(object)Player.m_localPlayer != (Object)null && usedRune != null && ((Humanoid)Player.m_localPlayer).GetInventory().ContainsItem(usedRune))
			{
				((Humanoid)Player.m_localPlayer).GetInventory().RemoveItem(usedRune, 1);
			}
			usedRune = null;
			selectedClass = null;
			return false;
		}
	}
	internal static class GearInterface
	{
		private static FieldInfo foldTab;

		private static FieldInfo settingsButton;

		private static FieldInfo settings;

		internal static void Install(Harmony harmony)
		{
			//IL_007f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0084: Unknown result type (might be due to invalid IL or missing references)
			//IL_0098: Expected O, but got Unknown
			//IL_00d3: 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_00e7: Expected O, but got Unknown
			//IL_010e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0113: Unknown result type (might be due to invalid IL or missing references)
			//IL_0127: Expected O, but got Unknown
			Type type = AccessTools.TypeByName("PassiveTree.Gear.Sheet");
			Type type2 = AccessTools.TypeByName("PassiveTree.Gear.Module");
			if (type == null || type2 == null)
			{
				throw new MissingMemberException("PathOfValheiman equipment interface");
			}
			foldTab = Field(type, "foldTab");
			settingsButton = Field(type, "settingsButton");
			settings = Field(type, "settings");
			harmony.Patch((MethodBase)Method(type2, "get_Compact"), new HarmonyMethod(typeof(GearInterface), "Compact", (Type[])null)
			{
				priority = 800
			}, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			string[] array = new string[2] { "SyncFold", "Tick" };
			foreach (string name in array)
			{
				harmony.Patch((MethodBase)Method(type, name), (HarmonyMethod)null, new HarmonyMethod(typeof(GearInterface), "HideControls", (Type[])null)
				{
					priority = 0
				}, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			}
			harmony.Patch((MethodBase)Method(type, "ToggleSettings"), new HarmonyMethod(typeof(GearInterface), "BlockSettings", (Type[])null)
			{
				priority = 800
			}, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
		}

		private static FieldInfo Field(Type type, string name)
		{
			return AccessTools.Field(type, name) ?? throw new MissingFieldException(type.FullName, name);
		}

		private static MethodInfo Method(Type type, string name)
		{
			return AccessTools.Method(type, name, (Type[])null, (Type[])null) ?? throw new MissingMethodException(type.FullName, name);
		}

		private static bool Compact(ref bool __result)
		{
			__result = true;
			return false;
		}

		private static bool BlockSettings()
		{
			return false;
		}

		private static void HideControls(object __instance)
		{
			Hide(foldTab.GetValue(__instance));
			Hide(settingsButton.GetValue(__instance));
			Hide(settings.GetValue(__instance));
		}

		private static void Hide(object value)
		{
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = (GameObject)((value is GameObject) ? value : null);
			if ((Object)(object)val == (Object)null && value is Component)
			{
				val = ((Component)value).gameObject;
			}
			if ((Object)(object)val != (Object)null && val.activeSelf)
			{
				val.SetActive(false);
			}
		}
	}
	public sealed class InventoryStatsLayout : MonoBehaviour
	{
		private InventoryGui gui;

		private Type trashType;

		private FieldInfo trashRect;

		private FieldInfo[] sortButtons;

		private RectTransform shieldIcon;

		private Vector2 shieldHome;

		internal static void Install(Harmony harmony)
		{
			//IL_0028: 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_003c: Expected O, but got Unknown
			harmony.Patch((MethodBase)AccessTools.Method(typeof(InventoryGui), "Awake", (Type[])null, (Type[])null), (HarmonyMethod)null, new HarmonyMethod(typeof(InventoryStatsLayout), "Attach", (Type[])null)
			{
				priority = 0
			}, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
		}

		private static void Attach(InventoryGui __instance)
		{
			if ((Object)(object)((Component)__instance).GetComponent<InventoryStatsLayout>() == (Object)null)
			{
				((Component)__instance).gameObject.AddComponent<InventoryStatsLayout>();
			}
		}

		private void Awake()
		{
			gui = ((Component)this).GetComponent<InventoryGui>();
			trashType = AccessTools.TypeByName("QuickStackStore.TrashModule+TrashButton");
			if (trashType != null)
			{
				trashRect = AccessTools.Field(trashType, "rectTransform");
			}
			Type type = AccessTools.TypeByName("QuickStackStore.ButtonRenderer");
			if (type != null)
			{
				sortButtons = new FieldInfo[4]
				{
					AccessTools.Field(type, "favoritingTogglingButton"),
					AccessTools.Field(type, "sortInventoryButton"),
					AccessTools.Field(type, "restockAreaButton"),
					AccessTools.Field(type, "quickStackAreaButton")
				};
			}
		}

		private static bool Visible(RectTransform rect)
		{
			if ((Object)(object)rect != (Object)null)
			{
				return ((Component)rect).gameObject.activeInHierarchy;
			}
			return false;
		}

		private static Bounds VisualBounds(RectTransform space, RectTransform root)
		{
			//IL_0009: 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_00f9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ba: 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_00d5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00da: 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)
			//IL_00e1: 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_005e: 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_0063: 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_0081: 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_0071: Unknown result type (might be due to invalid IL or missing references)
			Vector3[] array = (Vector3[])(object)new Vector3[4];
			Bounds result = default(Bounds);
			bool flag = false;
			Graphic[] componentsInChildren = ((Component)root).GetComponentsInChildren<Graphic>(false);
			foreach (Graphic val in componentsInChildren)
			{
				if (!((Behaviour)val).enabled || val.color.a <= 0.001f)
				{
					continue;
				}
				val.rectTransform.GetWorldCorners(array);
				Vector3[] array2 = array;
				foreach (Vector3 val2 in array2)
				{
					Vector3 val3 = ((Transform)space).InverseTransformPoint(val2);
					if (!flag)
					{
						((Bounds)(ref result))..ctor(val3, Vector3.zero);
						flag = true;
					}
					else
					{
						((Bounds)(ref result)).Encapsulate(val3);
					}
				}
			}
			if (!flag)
			{
				root.GetWorldCorners(array);
				((Bounds)(ref result))..ctor(((Transform)space).InverseTransformPoint(array[0]), Vector3.zero);
				Vector3[] array2 = array;
				foreach (Vector3 val4 in array2)
				{
					((Bounds)(ref result)).Encapsulate(((Transform)space).InverseTransformPoint(val4));
				}
			}
			return result;
		}

		private void LateUpdate()
		{
			//IL_0134: 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_013c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0141: 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_0159: Unknown result type (might be due to invalid IL or missing references)
			//IL_0166: Unknown result type (might be due to invalid IL or missing references)
			//IL_0172: 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_018f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0191: Unknown result type (might be due to invalid IL or missing references)
			//IL_0196: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)gui == (Object)null || (Object)(object)gui.m_player == (Object)null || !((Component)gui.m_player).gameObject.activeInHierarchy)
			{
				return;
			}
			RectTransform player = gui.m_player;
			AdjustShieldAndButtons(player);
			Transform obj = ((Transform)player).Find("CoinPocketUI");
			RectTransform val = (RectTransform)(object)((obj is RectTransform) ? obj : null);
			if (!Visible(val))
			{
				return;
			}
			Transform obj2 = ((Transform)player).Find("Trash");
			RectTransform val2 = (RectTransform)(object)((obj2 is RectTransform) ? obj2 : null);
			if (!Visible(val2) && trashType != null && trashRect != null)
			{
				Component componentInChildren = ((Component)gui).GetComponentInChildren(trashType, true);
				if ((Object)(object)componentInChildren != (Object)null)
				{
					object? value = trashRect.GetValue(componentInChildren);
					val2 = (RectTransform)((value is RectTransform) ? value : null);
				}
			}
			if (!Visible(val2))
			{
				RectTransform[] componentsInChildren = ((Component)gui).GetComponentsInChildren<RectTransform>(false);
				foreach (RectTransform val3 in componentsInChildren)
				{
					if (((Object)val3).name.Equals("Trash", StringComparison.OrdinalIgnoreCase) || ((Object)val3).name.Equals("TrashButton", StringComparison.OrdinalIgnoreCase))
					{
						val2 = val3;
						break;
					}
				}
			}
			if (Visible(val2))
			{
				Bounds val4 = VisualBounds(player, val2);
				Bounds val5 = VisualBounds(player, val);
				Vector3 val6 = default(Vector3);
				((Vector3)(ref val6))..ctor(((Bounds)(ref val4)).max.x + 12f - ((Bounds)(ref val5)).min.x, ((Bounds)(ref val4)).center.y - ((Bounds)(ref val5)).center.y, 0f);
				((Transform)val).position = ((Transform)val).position + ((Transform)player).TransformVector(val6);
			}
		}

		private void AdjustShieldAndButtons(RectTransform space)
		{
			//IL_009e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a3: 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_005c: Unknown result type (might be due to invalid IL or missing references)
			//IL_006b: 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_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_010b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0110: Unknown result type (might be due to invalid IL or missing references)
			//IL_0117: Unknown result type (might be due to invalid IL or missing references)
			//IL_0124: Unknown result type (might be due to invalid IL or missing references)
			//IL_0136: Unknown result type (might be due to invalid IL or missing references)
			//IL_014e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0154: 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_015b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0168: Unknown result type (might be due to invalid IL or missing references)
			Transform obj = ((Transform)space).Find("Armor");
			RectTransform val = (RectTransform)(object)((obj is RectTransform) ? obj : null);
			if (Visible(val))
			{
				Transform obj2 = ((Transform)val).Find("armor_icon");
				RectTransform val2 = (RectTransform)(object)((obj2 is RectTransform) ? obj2 : null);
				if ((Object)(object)val2 != (Object)null)
				{
					if ((Object)(object)shieldIcon != (Object)(object)val2)
					{
						shieldIcon = val2;
						shieldHome = val2.anchoredPosition;
					}
					val2.anchoredPosition = shieldHome + new Vector2(0f, -14f);
				}
			}
			Transform obj3 = ((Transform)space).Find("Weight");
			RectTransform val3 = (RectTransform)(object)((obj3 is RectTransform) ? obj3 : null);
			if (!Visible(val3) || sortButtons == null)
			{
				return;
			}
			Bounds val4 = VisualBounds(space, val3);
			float num = ((Bounds)(ref val4)).min.x;
			FieldInfo[] array = sortButtons;
			Vector3 val8 = default(Vector3);
			foreach (FieldInfo fieldInfo in array)
			{
				Component val5 = (Component)((fieldInfo == null) ? null : /*isinst with value type is only supported in some contexts*/);
				RectTransform val6 = (RectTransform)(((Object)(object)val5 == (Object)null) ? null : /*isinst with value type is only supported in some contexts*/);
				if (Visible(val6))
				{
					Bounds val7 = VisualBounds(space, val6);
					((Vector3)(ref val8))..ctor(num - ((Bounds)(ref val7)).min.x, ((Bounds)(ref val4)).min.y - 10f - ((Bounds)(ref val7)).max.y, 0f);
					((Transform)val6).position = ((Transform)val6).position + ((Transform)space).TransformVector(val8);
					num += ((Bounds)(ref val7)).size.x + 10f;
				}
			}
		}
	}
	internal static class MenuTheme
	{
		private static ConfigEntry<bool> enabled;

		internal static void Bind(ConfigFile config)
		{
			enabled = config.Bind<bool>("Interface", "BlueMenuTheme", true, "Use a Moder-inspired cold blue lighting/fog palette in the main-menu scene. Replaces the old EikthyrMenuTheme option. Does not change weather in the world. Restart after changing.");
		}

		internal static void Install(Harmony harmony)
		{
			//IL_0027: 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_003c: Expected O, but got Unknown
			harmony.Patch((MethodBase)AccessTools.Method(typeof(MenuScene), "Update", (Type[])null, (Type[])null), new HarmonyMethod(typeof(MenuTheme), "Apply", (Type[])null)
			{
				priority = 0
			}, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
		}

		private static void Apply(ref Color ___m_sunFogColor, ref Color ___m_fogColor, ref Color ___m_ambientLightColor, ref float ___m_fogDensity, ref float ___m_windIntensity, Light ___m_dirLight)
		{
			//IL_0022: 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_0041: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//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_0099: Unknown result type (might be due to invalid IL or missing references)
			if (enabled.Value)
			{
				___m_sunFogColor = new Color(0.12f, 0.3f, 0.55f, 1f);
				___m_fogColor = new Color(0.055f, 0.12f, 0.24f, 1f);
				___m_ambientLightColor = new Color(0.14f, 0.2f, 0.3f, 1f);
				___m_fogDensity = 0.008f;
				___m_windIntensity = 0.7f;
				if ((Object)(object)___m_dirLight != (Object)null)
				{
					___m_dirLight.color = new Color(0.4f, 0.65f, 1f, 1f);
					___m_dirLight.intensity = 0.65f;
				}
			}
		}
	}
	[BepInPlugin("kingsandgods.additional", "KingsAndGodsAdditions", "1.6.8")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public sealed class Plugin : BaseUnityPlugin
	{
		[Serializable]
		[CompilerGenerated]
		private sealed class <>c
		{
			public static readonly <>c <>9 = new <>c();

			public static UnityAction <>9__85_0;

			internal void <DiscordPostfix>b__85_0()
			{
				Application.OpenURL("https://discord.gg/PMPDpuPTND");
			}
		}

		public const string Guid = "kingsandgods.additional";

		public const string Version = "1.6.8";

		private const string NetworkVersion = "1.6.8";

		private const string HelloRpc = "KAGAdditional_Hello_v1";

		private const string AssignRpc = "KAGAdditional_Assign_v1";

		private const string ConfigRpc = "KAGAdditional_Config_v2";

		private const string AdminRpc = "KAGAdditional_Admin_v2";

		private const string ReplyRpc = "KAGAdditional_Reply_v2";

		private const string ConfigAckRpc = "KAGAdditional_ConfigAck_v2";

		private const string RecipesRpc = "KAGAdditional_Recipes_v3";

		private const string RecipesAckRpc = "KAGAdditional_RecipesAck_v3";

		private const string Discord = "https://discord.gg/PMPDpuPTND";

		internal static Plugin One;

		private Harmony harmony;

		private Type raceType;

		private object raceInstance;

		private ZRoutedRpc registered;

		private ZNet session;

		private float nextTick;

		private float nextRequest;

		private float nextApply;

		private string assigned;

		private bool applying;

		private Player lastPlayer;

		private string assignmentFile;

		private DateTime fileStamp;

		private RaceAssignments assignments = new RaceAssignments();

		private readonly Dictionary<long, float> firstSeen = new Dictionary<long, float>();

		private readonly HashSet<long> verified = new HashSet<long>();

		private ConfigEntry<bool> requireClients;

		private ConfigEntry<int> graceSeconds;

		private ConfigEntry<float> logoOffset;

		private readonly Dictionary<RectTransform, Vector2> logoPositions = new Dictionary<RectTransform, Vector2>();

		private Sprite logo;

		private Texture2D logoTexture;

		private bool ready;

		private readonly ConfigAuthority authority = new ConfigAuthority();

		private readonly Dictionary<long, string> sentConfig = new Dictionary<long, string>();

		private bool clientAdmin;

		private string configSnapshot;

		private readonly HashSet<long> configConfirmed = new HashSet<long>();

		private RecipeAuthority recipes;

		private readonly Dictionary<long, string> sentRecipes = new Dictionary<long, string>();

		private readonly HashSet<long> recipesConfirmed = new HashSet<long>();

		private string recipeAck;

		private static readonly MethodInfo ServerPeerMethod = AccessTools.Method(typeof(ZRoutedRpc), "GetServerPeerID", (Type[])null, (Type[])null);

		private long ServerPeerId
		{
			get
			{
				if (registered != null)
				{
					return (long)ServerPeerMethod.Invoke(registered, null);
				}
				return 0L;
			}
		}

		private void Awake()
		{
			//IL_0096: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a0: Expected O, but got Unknown
			//IL_0103: Unknown result type (might be due to invalid IL or missing references)
			//IL_010d: Expected O, but got Unknown
			One = this;
			DayNightCycle.Bind(((BaseUnityPlugin)this).Config);
			DeathRules.Bind(((BaseUnityPlugin)this).Config);
			MenuTheme.Bind(((BaseUnityPlugin)this).Config);
			logoOffset = ((BaseUnityPlugin)this).Config.Bind<float>("Interface", "LogoOffsetDown", 60f, "Move the main-menu logo down in UI units. Positive = lower. Restart the game after editing.");
			requireClients = ((BaseUnityPlugin)this).Config.Bind<bool>("Server", "RequireClientMod", true, "Disconnect peers that do not complete the KingsAndGodsAdditions version handshake. Server setting only.");
			graceSeconds = ((BaseUnityPlugin)this).Config.Bind<int>("Server", "HandshakeTimeoutSeconds", 60, new ConfigDescription("Seconds allowed after a peer joins. Server setting only.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(20, 300), Array.Empty<object>()));
			assignmentFile = Path.Combine(Paths.ConfigPath, "KingAndGodsAdditional.races.yml");
			if (!File.Exists(assignmentFile))
			{
				File.WriteAllText(assignmentFile, RaceAssignments.Template());
			}
			recipes = new RecipeAuthority(Path.Combine(Paths.ConfigPath, "KingAndGodsAdditional.recipes.yml"), delegate(string text)
			{
				((BaseUnityPlugin)this).Logger.LogInfo((object)text);
			});
			harmony = new Harmony("kingsandgods.additional");
			InventoryStatsLayout.Install(harmony);
			BossEnvironments.Install(harmony);
			DeathRules.Install(harmony);
			Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
			foreach (Assembly assembly in assemblies)
			{
				Type type = assembly.GetType("Vapok.Common.Managers.Splash.ModSplashManager", throwOnError: false);
				if (!(type == null))
				{
					Patch(type, "CheckAndQueueStartupSplashes", "Block", null);
					((BaseUnityPlugin)this).Logger.LogInfo((object)("Vapok automatic startup splash disabled: " + assembly.GetName().Name));
				}
			}
			MenuTheme.Install(harmony);
			Patch(typeof(Game), "ShowIntro", "SkipTextIntro", null);
			DemigodRunes.Install(harmony, delegate(string text)
			{
				((BaseUnityPlugin)this).Logger.LogInfo((object)text);
			});
			Type type2 = AccessTools.TypeByName("Guilds.GuildUI");
			if (type2 != null)
			{
				Patch(type2, "BuildDiscordLink", "Block", null);
				((BaseUnityPlugin)this).Logger.LogInfo((object)"Guilds: Northarun Discord link hidden.");
			}
			DayNightCycle.Install(harmony);
			((BaseUnityPlugin)this).Logger.LogInfo((object)"Day/night hooks installed and verified. Config: [Time] DayDurationMinutes / NightDurationMinutes.");
			raceType = AccessTools.TypeByName("RacesOfValheim.RacesOfValheim");
			if (raceType == null)
			{
				throw new InvalidOperationException("RacesOfValheim 1.6.6 is required.");
			}
			Patch(raceType, "ShowRaceSelectionMenu", "Block", null);
			Patch(raceType, "ResetRace", "SelectionGuard", null);
			string[] names = RaceAssignments.Names;
			foreach (string race in names)
			{
				Patch(raceType, "Select" + RaceAssignments.Canonical(race), "SelectionGuard", null);
			}
			Patch(NeedType("RacesOfValheim.RaceMenuWard"), "TryPlaceAtSpawn", "Block", null);
			Patch(NeedType("RacesOfValheim.RaceMenuWardUI"), "Show", "Block", null);
			Type type3 = AccessTools.TypeByName("PassiveTree.UI.GameLogo");
			if (type3 != null)
			{
				GearInterface.Install(harmony);
				SkillsButton.Install(harmony);
				Patch(type3, "Ours", "LogoPrefix", null);
				Patch(type3, "Apply", null, "LogoPositionPostfix");
				Patch(NeedType("PassiveTree.UI.MenuDiscord"), "Add", null, "DiscordPostfix");
				Patch(NeedType("PassiveTree.UI.TreeWindow"), "BuildTopBar", null, "TreePostfix");
				Type type4 = NeedType("PassiveTree.UI.SetupWizard");
				Patch(type4, "MaybeShow", "WizardGuard", null);
				Patch(type4, "Build", "Block", null);
				Patch(type4, "OpenForTest", "Block", null);
				Type type5 = NeedType("PassiveTree.DifficultyAdmin");
				Patch(type5, "SenderCanEdit", "PathAdminGuard", null);
				Patch(type5, "get_CanEdit", "PathCanEdit", null);
				Patch(NeedType("PassiveTree.ConfigSync"), "OnApply", "PathServerMessage", null);
				Patch(type5, "ReceiveRole", "PathServerMessage", null);
				Patch(type5, "ReceiveState", "PathServerMessage", null);
				((BaseUnityPlugin)this).Logger.LogInfo((object)"PathOfValheiman hooks enabled: logo, Discord, tree Settings and BuildsManager buttons.");
			}
			authority.Discover((BaseUnityPlugin)(object)this);
			Patch(typeof(ConfigFile), "Save", "ConfigSaveGuard", null);
			RegisterCommands();
			ready = true;
			((BaseUnityPlugin)this).Logger.LogInfo((object)"KingsAndGodsAdditions 1.6.8 loaded. Race selection and automatic starter altar disabled.");
			((BaseUnityPlugin)this).Logger.LogInfo((object)("Server assignment file: " + assignmentFile));
		}

		private static Type NeedType(string name)
		{
			return AccessTools.TypeByName(name) ?? throw new MissingMemberException(name);
		}

		private void Patch(Type type, string method, string prefix, string postfix)
		{
			//IL_002f: 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_0054: 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)
			MethodInfo methodInfo = AccessTools.Method(type, method, (Type[])null, (Type[])null);
			if (methodInfo == null)
			{
				throw new MissingMethodException(type.FullName, method);
			}
			HarmonyMethod val = ((prefix == null) ? ((HarmonyMethod)null) : new HarmonyMethod(typeof(Plugin), prefix, (Type[])null)
			{
				priority = 800
			});
			HarmonyMethod val2 = ((postfix == null) ? ((HarmonyMethod)null) : new HarmonyMethod(typeof(Plugin), postfix, (Type[])null)
			{
				priority = 0
			});
			harmony.Patch((MethodBase)methodInfo, val, val2, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			((BaseUnityPlugin)this).Logger.LogInfo((object)("Bound " + type.FullName + "." + method));
		}

		private static bool Block()
		{
			return false;
		}

		private static bool SkipTextIntro(Game __instance)
		{
			AccessTools.Method(typeof(Game), "SkipIntro", (Type[])null, (Type[])null).Invoke(__instance, null);
			return false;
		}

		private static bool SelectionGuard()
		{
			if ((Object)(object)One != (Object)null)
			{
				return One.applying;
			}
			return false;
		}

		private static bool ConfigSaveGuard(ConfigFile __instance)
		{
			if (!((Object)(object)One == (Object)null) && !((Object)(object)ZNet.instance == (Object)null) && !ZNet.instance.IsServer())
			{
				return !One.authority.Manages(__instance);
			}
			return true;
		}

		private static bool WizardGuard(ref bool __result)
		{
			__result = false;
			return false;
		}

		private static bool PathAdminGuard(long __0, ref bool __result)
		{
			__result = (Object)(object)One != (Object)null && One.IsServerAdmin(__0);
			return false;
		}

		private static bool PathCanEdit(ref bool __result)
		{
			__result = (Object)(object)ZNet.instance != (Object)null && (ZNet.instance.IsServer() || ((Object)(object)One != (Object)null && One.clientAdmin));
			return false;
		}

		private static bool PathServerMessage(long __0)
		{
			if ((Object)(object)One != (Object)null && (Object)(object)ZNet.instance != (Object)null)
			{
				return RaceAssignments.TrustedServer(ZNet.instance.IsServer(), __0, One.ServerPeerId);
			}
			return false;
		}

		private void Update()
		{
			if (!ready || Time.realtimeSinceStartup < nextTick)
			{
				return;
			}
			nextTick = Time.realtimeSinceStartup + 1f;
			try
			{
				Tick();
			}
			catch (Exception ex)
			{
				((BaseUnityPlugin)this).Logger.LogError((object)("KAG update: " + ex));
			}
		}

		private void Tick()
		{
			ZNet instance = ZNet.instance;
			if ((Object)(object)instance == (Object)null)
			{
				authority.Restore();
				recipes.Reset();
				recipeAck = null;
				sentRecipes.Clear();
				recipesConfirmed.Clear();
				clientAdmin = false;
				sentConfig.Clear();
				configConfirmed.Clear();
				configSnapshot = null;
				session = null;
				registered = null;
				assigned = null;
				lastPlayer = null;
				verified.Clear();
				firstSeen.Clear();
				return;
			}
			if ((Object)(object)session != (Object)(object)instance)
			{
				authority.Restore();
				recipes.Reset();
				recipeAck = null;
				sentRecipes.Clear();
				recipesConfirmed.Clear();
				clientAdmin = false;
				sentConfig.Clear();
				configConfirmed.Clear();
				configSnapshot = null;
				session = instance;
				registered = null;
				assigned = null;
				lastPlayer = null;
				verified.Clear();
				firstSeen.Clear();
				nextRequest = 0f;
				nextApply = 0f;
				if (instance.IsServer())
				{
					ReloadAssignments(force: true);
					ReloadServerConfig(force: true);
				}
			}
			if (ZRoutedRpc.instance != null && registered != ZRoutedRpc.instance)
			{
				registered = ZRoutedRpc.instance;
				registered.Register<string>("KAGAdditional_Hello_v1", (Action<long, string>)ReceiveHello);
				registered.Register<string>("KAGAdditional_Assign_v1", (Action<long, string>)ReceiveAssignment);
				registered.Register<string>("KAGAdditional_Config_v2", (Action<long, string>)ReceiveConfig);
				registered.Register<string>("KAGAdditional_Admin_v2", (Action<long, string>)ReceiveAdmin);
				registered.Register<string>("KAGAdditional_Reply_v2", (Action<long, string>)ReceiveReply);
				registered.Register<string>("KAGAdditional_ConfigAck_v2", (Action<long, string>)ReceiveConfigAck);
				registered.Register<string>("KAGAdditional_Recipes_v3", (Action<long, string>)ReceiveRecipes);
				registered.Register<string>("KAGAdditional_RecipesAck_v3", (Action<long, string>)ReceiveRecipesAck);
			}
			if (instance.IsServer())
			{
				ReloadAssignments(force: false);
				ReloadServerConfig(force: false);
				configSnapshot = authority.Snapshot();
				recipes.ServerTick();
				ServerPeers();
				if (!instance.IsDedicated())
				{
					assigned = assignments.ForPlayer(LocalSteamId());
				}
			}
			else if (registered != null && Time.realtimeSinceStartup >= nextRequest && ServerPeerId != 0L)
			{
				nextRequest = Time.realtimeSinceStartup + 3f;
				registered.InvokeRoutedRPC(ServerPeerId, "KAGAdditional_Hello_v1", new object[1] { "1.6.8" });
			}
			if (!instance.IsServer())
			{
				authority.Enforce();
				recipes.Tick();
				if (recipes.Received && recipeAck != recipes.Payload && registered != null && ServerPeerId != 0L)
				{
					registered.InvokeRoutedRPC(ServerPeerId, "KAGAdditional_RecipesAck_v3", new object[1] { "1.6.8" });
					recipeAck = recipes.Payload;
				}
			}
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null)
			{
				lastPlayer = null;
			}
			else if (assigned != null && (instance.IsServer() || authority.Received))
			{
				localPlayer.m_customData.TryGetValue("Race", out var value);
				if (((Object)(object)lastPlayer != (Object)(object)localPlayer || value != assigned) && Time.realtimeSinceStartup >= nextApply)
				{
					nextApply = Time.realtimeSinceStartup + 3f;
					ApplyRace(localPlayer, assigned);
					lastPlayer = localPlayer;
				}
			}
		}

		private void ReloadAssignments(bool force)
		{
			DateTime lastWriteTimeUtc = File.GetLastWriteTimeUtc(assignmentFile);
			if (!force && lastWriteTimeUtc == fileStamp)
			{
				return;
			}
			fileStamp = lastWriteTimeUtc;
			try
			{
				RaceAssignments raceAssignments = (assignments = RaceAssignments.Parse(File.ReadAllText(assignmentFile)));
				((BaseUnityPlugin)this).Logger.LogInfo((object)("Loaded server race assignments: " + raceAssignments.Count + " SteamID64 entries. Unlisted players: Human."));
			}
			catch (Exception ex)
			{
				((BaseUnityPlugin)this).Logger.LogError((object)("Invalid race file; keeping last valid assignments. " + ex.Message));
			}
		}

		private static string LocalSteamId()
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				return RaceAssignments.FromSocketName(((object)SteamUser.GetSteamID()/*cast due to .constrained prefix*/).ToString());
			}
			catch
			{
				return null;
			}
		}

		private void ReceiveHello(long sender, string version)
		{
			if ((Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer() || version != "1.6.8" || registered == null)
			{
				return;
			}
			ZNetPeer peer = ZNet.instance.GetPeer(sender);
			if (peer != null && peer.m_socket != null)
			{
				string text = RaceAssignments.FromSocketName(peer.m_socket.GetHostName());
				if (text == null)
				{
					((BaseUnityPlugin)this).Logger.LogWarning((object)("Cannot authenticate SteamID64 for peer " + sender + ". Use Steam networking; crossplay IDs are not Steam IDs."));
					return;
				}
				verified.Add(sender);
				SendConfig(sender);
				SendRecipes(sender);
				registered.InvokeRoutedRPC(sender, "KAGAdditional_Assign_v1", new object[1] { assignments.ForPlayer(text) });
			}
		}

		private void ReceiveAssignment(long sender, string race)
		{
			if ((Object)(object)ZNet.instance == (Object)null || registered == null || !RaceAssignments.TrustedServer(ZNet.instance.IsServer(), sender, ServerPeerId))
			{
				return;
			}
			string text = RaceAssignments.Canonical(race);
			if (text != null)
			{
				if (assigned != text)
				{
					((BaseUnityPlugin)this).Logger.LogInfo((object)("Server assigned race: " + text));
				}
				assigned = text;
			}
		}

		private void ServerPeers()
		{
			if (registered == null)
			{
				return;
			}
			List<ZNetPeer> list = new List<ZNetPeer>(ZNet.instance.GetPeers());
			HashSet<long> hashSet = new HashSet<long>();
			foreach (ZNetPeer item in list)
			{
				if (item.m_uid == 0L || item.m_server || item.m_socket == null)
				{
					continue;
				}
				long uid = item.m_uid;
				hashSet.Add(uid);
				if (!firstSeen.ContainsKey(uid))
				{
					firstSeen[uid] = Time.realtimeSinceStartup;
				}
				if (verified.Contains(uid))
				{
					SendConfig(uid);
					SendRecipes(uid);
					string text = RaceAssignments.FromSocketName(item.m_socket.GetHostName());
					if (text != null)
					{
						registered.InvokeRoutedRPC(uid, "KAGAdditional_Assign_v1", new object[1] { assignments.ForPlayer(text) });
					}
				}
				if (requireClients.Value && (!verified.Contains(uid) || !configConfirmed.Contains(uid) || !recipesConfirmed.Contains(uid)) && Time.realtimeSinceStartup - firstSeen[uid] > (float)graceSeconds.Value)
				{
					((BaseUnityPlugin)this).Logger.LogWarning((object)("Disconnecting " + item.m_playerName + ": KingsAndGodsAdditions 1.6.8 handshake/configuration sync missing or no authenticated SteamID64. Install matching mods on the client and use Steam networking."));
					AccessTools.Method(typeof(ZNet), "Disconnect", new Type[1] { typeof(ZNetPeer) }, (Type[])null).Invoke(ZNet.instance, new object[1] { item });
					firstSeen.Remove(uid);
				}
			}
			long[] array = firstSeen.Keys.ToArray();
			foreach (long num in array)
			{
				if (!hashSet.Contains(num))
				{
					firstSeen.Remove(num);
					verified.Remove(num);
					sentConfig.Remove(num);
					configConfirmed.Remove(num);
					sentRecipes.Remove(num);
					recipesConfirmed.Remove(num);
				}
			}
		}

		private void SendRecipes(long peer)
		{
			string payload = recipes.Payload;
			if (!sentRecipes.TryGetValue(peer, out var value) || !(value == payload))
			{
				recipesConfirmed.Remove(peer);
				firstSeen[peer] = Time.realtimeSinceStartup;
				registered.InvokeRoutedRPC(peer, "KAGAdditional_Recipes_v3", new object[1] { payload });
				sentRecipes[peer] = payload;
			}
		}

		private void ReceiveRecipes(long sender, string text)
		{
			if (!PathServerMessage(sender))
			{
				return;
			}
			try
			{
				recipes.Accept(text);
				recipeAck = null;
			}
			catch (Exception ex)
			{
				((BaseUnityPlugin)this).Logger.LogError((object)("Server recipes rejected: " + ex.Message));
			}
		}

		private void ReceiveRecipesAck(long sender, string version)
		{
			if ((Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsServer() && version == "1.6.8" && verified.Contains(sender))
			{
				recipesConfirmed.Add(sender);
			}
		}

		private void ReloadServerConfig(bool force)
		{
			authority.ReloadServerFiles(force);
		}

		private void SendConfig(long peer)
		{
			if (configSnapshot == null)
			{
				configSnapshot = authority.Snapshot();
			}
			string text = (IsServerAdmin(peer) ? "1\n" : "0\n") + configSnapshot;
			if (!sentConfig.TryGetValue(peer, out var value) || !(value == text))
			{
				configConfirmed.Remove(peer);
				firstSeen[peer] = Time.realtimeSinceStartup;
				registered.InvokeRoutedRPC(peer, "KAGAdditional_Config_v2", new object[1] { text });
				sentConfig[peer] = text;
			}
		}

		private void ReceiveConfig(long sender, string payload)
		{
			if (!PathServerMessage(sender) || payload == null || payload.Length < 10 || payload.Length > 1048580 || (!payload.StartsWith("0\n", StringComparison.Ordinal) && !payload.StartsWith("1\n", StringComparison.Ordinal)))
			{
				return;
			}
			try
			{
				authority.Apply(payload.Substring(2));
				clientAdmin = payload[0] == '1';
				registered.InvokeRoutedRPC(ServerPeerId, "KAGAdditional_ConfigAck_v2", new object[1] { "1.6.8" });
				LogoPositionPostfix();
				((BaseUnityPlugin)this).Logger.LogInfo((object)("Server configuration applied and locked. Server admin: " + clientAdmin));
			}
			catch (Exception ex)
			{
				((BaseUnityPlugin)this).Logger.LogError((object)("Server configuration rejected: " + ex.Message));
			}
		}

		private void ReceiveConfigAck(long sender, string version)
		{
			if ((Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsServer() && version == "1.6.8" && verified.Contains(sender))
			{
				configConfirmed.Add(sender);
			}
		}

		private bool IsServerAdmin(long sender)
		{
			if ((Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer() || !verified.Contains(sender))
			{
				return false;
			}
			ZNetPeer peer = ZNet.instance.GetPeer(sender);
			if (peer == null || peer.m_socket == null)
			{
				return false;
			}
			return (bool)AccessTools.Method(typeof(ZNet), "IsAdmin", new Type[1] { typeof(string) }, (Type[])null).Invoke(ZNet.instance, new object[1] { peer.m_socket.GetHostName() });
		}

		private void RegisterCommands()
		{
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Expected O, but got Unknown
			//IL_0020: 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_004b: Expected O, but got Unknown
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0071: Expected O, but got Unknown
			//IL_006c: Unknown result type (might be due to invalid IL or missing references)
			new ConsoleCommand("kag_setrace", "kag_setrace <SteamID64> <race> - server/admin only", (ConsoleEvent)delegate(ConsoleEventArgs args)
			{
				if (args.Args.Length != 3)
				{
					Print("Usage: kag_setrace <SteamID64> <race>");
				}
				else
				{
					SubmitAdmin("race\t" + args.Args[1] + "\t" + args.Args[2]);
				}
			}, false, false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false);
			new ConsoleCommand("kag_reload", "Reload server race and addon configuration (server/admin only)", (ConsoleEvent)delegate
			{
				SubmitAdmin("reload");
			}, false, false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false);
			new ConsoleCommand("kag_setconfig", "kag_setconfig <kag|races> <section>|<key>|<value> - server/admin only", (ConsoleEvent)delegate(ConsoleEventArgs args)
			{
				if (args.Args.Length < 3)
				{
					Print("Usage: kag_setconfig kag Interface|LogoOffsetDown|60");
				}
				else
				{
					string[] array = string.Join(" ", args.Args.Skip(2)).Split(new char[1] { '|' }, 3);
					if (array.Length != 3)
					{
						Print("Expected section|key|value");
					}
					else
					{
						SubmitAdmin("config\t" + args.Args[1] + "\t" + array[0] + "\t" + array[1] + "\t" + array[2]);
					}
				}
			}, false, false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false);
		}

		private void SubmitAdmin(string payload)
		{
			if ((Object)(object)ZNet.instance == (Object)null || registered == null)
			{
				Print("Not connected to a world.");
				return;
			}
			if (ZNet.instance.IsServer())
			{
				Print(ExecuteAdmin(payload));
				return;
			}
			registered.InvokeRoutedRPC(ServerPeerId, "KAGAdditional_Admin_v2", new object[1] { payload });
		}

		private void ReceiveAdmin(long sender, string payload)
		{
			if (!((Object)(object)ZNet.instance == (Object)null) && ZNet.instance.IsServer() && registered != null && payload != null && payload.Length <= 8192)
			{
				string text = (IsServerAdmin(sender) ? ExecuteAdmin(payload) : "Denied: only a server admin may change races/settings.");
				registered.InvokeRoutedRPC(sender, "KAGAdditional_Reply_v2", new object[1] { text });
			}
		}

		private string ExecuteAdmin(string payload)
		{
			try
			{
				if (payload == "reload")
				{
					ReloadAssignments(force: true);
					ReloadServerConfig(force: true);
					recipes.ServerTick(force: true);
					configSnapshot = authority.Snapshot();
					sentConfig.Clear();
					return "Server configuration reloaded.";
				}
				string[] array = payload.Split('\t');
				if (array.Length == 5 && array[0] == "config")
				{
					authority.SetServerValue(array[1], array[2], array[3], array[4]);
					configSnapshot = authority.Snapshot();
					sentConfig.Clear();
					return "Server setting saved: " + array[1] + " / " + array[2] + " / " + array[3];
				}
				if (array.Length != 3 || array[0] != "race")
				{
					return "Invalid admin command.";
				}
				string text = assignments.WithPlayer(array[1], array[2]);
				RaceAssignments raceAssignments = RaceAssignments.Parse(text);
				File.Copy(assignmentFile, assignmentFile + ".bak", overwrite: true);
				File.WriteAllText(assignmentFile, text);
				assignments = raceAssignments;
				fileStamp = File.GetLastWriteTimeUtc(assignmentFile);
				return "Assigned " + RaceAssignments.Canonical(array[2]) + " to " + array[1] + ". Saved on server.";
			}
			catch (Exception ex)
			{
				return "Admin command failed: " + ex.Message;
			}
		}

		private void ReceiveReply(long sender, string text)
		{
			if (PathServerMessage(sender) && text != null && text.Length < 2048)
			{
				Print(text);
			}
		}

		private void Print(string text)
		{
			((BaseUnityPlugin)this).Logger.LogInfo((object)text);
			if ((Object)(object)Console.instance != (Object)null)
			{
				Console.instance.Print(text);
			}
		}

		private void ApplyRace(Player player, string race)
		{
			if (raceInstance == null)
			{
				raceInstance = AccessTools.Field(raceType, "Instance").GetValue(null);
			}
			if (raceInstance == null)
			{
				return;
			}
			applying = true;
			try
			{
				AccessTools.Method(raceType, "ResetRace", (Type[])null, (Type[])null).Invoke(raceInstance, null);
				AccessTools.Method(raceType, "Select" + race, (Type[])null, (Type[])null).Invoke(raceInstance, null);
				((BaseUnityPlugin)this).Logger.LogInfo((object)("Applied server race " + race + " to local character."));
			}
			finally
			{
				applying = false;
			}
		}

		private Sprite GetLogo()
		{
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: Expected O, but got Unknown
			//IL_00bd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cc: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)logo != (Object)null)
			{
				return logo;
			}
			using (Stream stream = Assembly.GetExecutingAssembly().GetManifestResourceStream("KingAndGodsAdditional.KAGlogo.png"))
			{
				if (stream == null)
				{
					throw new FileNotFoundException("Embedded KAG logo missing");
				}
				using MemoryStream memoryStream = new MemoryStream();
				stream.CopyTo(memoryStream);
				logoTexture = new Texture2D(2, 2, (TextureFormat)4, false);
				if (!ImageConversion.LoadImage(logoTexture, memoryStream.ToArray(), true))
				{
					throw new InvalidDataException("Cannot decode KAG logo");
				}
			}
			((Object)logoTexture).name = "KingsAndGodsLogo";
			logo = Sprite.Create(logoTexture, new Rect(0f, 0f, (float)((Texture)logoTexture).width, (float)((Texture)logoTexture).height), new Vector2(0.5f, 0.5f), 100f);
			((Object)logo).name = "KingsAndGodsLogo";
			return logo;
		}

		private static bool LogoPrefix(ref Sprite __result)
		{
			__result = One.GetLogo();
			return false;
		}

		private static void LogoPositionPostfix()
		{
			//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_00c4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c9: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)One == (Object)null || (Object)(object)Object.FindFirstObjectByType<FejdStartup>() == (Object)null)
			{
				return;
			}
			Image[] array = Resources.FindObjectsOfTypeAll<Image>();
			Scene scene;
			foreach (Image val in array)
			{
				if ((Object)(object)val != (Object)null && (Object)(object)val.sprite == (Object)(object)One.logo)
				{
					scene = ((Component)val).gameObject.scene;
					if (((Scene)(ref scene)).IsValid())
					{
						One.MoveLogo(((Graphic)val).rectTransform);
					}
				}
			}
			RawImage[] array2 = Resources.FindObjectsOfTypeAll<RawImage>();
			foreach (RawImage val2 in array2)
			{
				if ((Object)(object)val2 != (Object)null && (Object)(object)One.logoTexture != (Object)null && (Object)(object)val2.texture == (Object)(object)One.logoTexture)
				{
					scene = ((Component)val2).gameObject.scene;
					if (((Scene)(ref scene)).IsValid())
					{
						One.MoveLogo(((Graphic)val2).rectTransform);
					}
				}
			}
		}

		private void MoveLogo(RectTransform rect)
		{
			//IL_0025: 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_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			if (!logoPositions.TryGetValue(rect, out var value))
			{
				value = rect.anchoredPosition;
				logoPositions.Add(rect, value);
			}
			rect.anchoredPosition = value + new Vector2(0f, 0f - logoOffset.Value);
		}

		private static void DiscordPostfix(FejdStartup __0)
		{
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: Expected O, but got Unknown
			//IL_006e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0073: Unknown result type (might be due to invalid IL or missing references)
			//IL_0079: Expected O, but got Unknown
			if (!(AccessTools.Field(typeof(FejdStartup), "m_menuButtons").GetValue(__0) is Button[] array))
			{
				return;
			}
			Button[] array2 = array;
			foreach (Button val in array2)
			{
				if ((Object)(object)val == (Object)null || ((Object)val).name != "PT_Discord")
				{
					continue;
				}
				val.onClick = new ButtonClickedEvent();
				ButtonClickedEvent onClick = val.onClick;
				object obj = <>c.<>9__85_0;
				if (obj == null)
				{
					UnityAction val2 = delegate
					{
						Application.OpenURL("https://discord.gg/PMPDpuPTND");
					};
					<>c.<>9__85_0 = val2;
					obj = (object)val2;
				}
				((UnityEvent)onClick).AddListener((UnityAction)obj);
			}
		}

		private static void TreePostfix(object __instance)
		{
			object? value = AccessTools.Field(__instance.GetType(), "root").GetValue(__instance);
			GameObject val = (GameObject)((value is GameObject) ? value : null);
			if ((Object)(object)val == (Object)null)
			{
				return;
			}
			string[] array = new string[2] { "Settings", "BuildsManager" };
			foreach (string text in array)
			{
				Transform val2 = val.transform.Find(text);
				if ((Object)(object)val2 != (Object)null)
				{
					((Component)val2).gameObject.SetActive(false);
				}
			}
		}

		private void OnDestroy()
		{
			authority.Restore();
			if (recipes != null)
			{
				recipes.Reset();
			}
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
			if ((Object)(object)logo != (Object)null)
			{
				Object.Destroy((Object)(object)logo);
			}
			if ((Object)(object)logoTexture != (Object)null)
			{
				Object.Destroy((Object)(object)logoTexture);
			}
			if ((Object)(object)One == (Object)(object)this)
			{
				One = null;
			}
		}
	}
	public sealed class RaceAssignments
	{
		public static readonly string[] Names = new string[12]
		{
			"human", "fenrir", "skadi", "skeleton", "aswid", "furling", "trol", "draugh", "dvergar", "trenta",
			"sverm", "alfar"
		};

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

		public int Count => players.Count;

		public static string Canonical(string race)
		{
			if (race == null)
			{
				return null;
			}
			race = race.Trim().ToLowerInvariant();
			if (race == "dvergr" || race == "dverger")
			{
				race = "dvergar";
			}
			if (race == "troll")
			{
				race = "trol";
			}
			if (race == "draugr")
			{
				race = "draugh";
			}
			string[] names = Names;
			foreach (string text in names)
			{
				if (race == text)
				{
					return char.ToUpperInvariant(text[0]) + text.Substring(1);
				}
			}
			return null;
		}

		public string ForPlayer(string steamId)
		{
			if (steamId == null || !players.TryGetValue(steamId, out var value))
			{
				return "Human";
			}
			return value;
		}

		public string WithPlayer(string steamId, string race)
		{
			string text = Canonical(race);
			if (!IsSteamId(steamId) || text == null)
			{
				throw new FormatException("Expected SteamID64 and a known race");
			}
			Dictionary<string, string> dictionary = new Dictionary<string, string>(players, StringComparer.Ordinal);
			dictionary[steamId] = text;
			StringBuilder stringBuilder = new StringBuilder("# KingAndGodsAdditional - server-owned assignments\n");
			string[] names = Names;
			foreach (string text2 in names)
			{
				stringBuilder.AppendLine().AppendLine(text2 + ":");
				foreach (KeyValuePair<string, string> item in dictionary)
				{
					if (item.Value == Canonical(text2))
					{
						stringBuilder.AppendLine("  - " + item.Key);
					}
				}
			}
			return stringBuilder.ToString();
		}

		public static bool IsSteamId(string value)
		{
			if (value != null && Regex.IsMatch(value, "^\\d{17}$") && ulong.TryParse(value, out var result))
			{
				return result != 0;
			}
			return false;
		}

		public static string FromSocketName(string value)
		{
			if (value == null)
			{
				return null;
			}
			if (value.StartsWith("Steam_", StringComparison.OrdinalIgnoreCase))
			{
				value = value.Substring(6);
			}
			if (!IsSteamId(value))
			{
				return null;
			}
			return value;
		}

		public static bool TrustedServer(bool runningServer, long sender, long serverPeer)
		{
			if (!runningServer && serverPeer != 0L)
			{
				return sender == serverPeer;
			}
			return false;
		}

		public static RaceAssignments Parse(string text)
		{
			RaceAssignments raceAssignments = new RaceAssignments();
			string text2 = null;
			HashSet<string> hashSet = new HashSet<string>(StringComparer.Ordinal);
			int num = 0;
			string[] array = text.Replace("\r", "").Split('\n');
			foreach (string obj in array)
			{
				num++;
				string text3 = obj.Split('#')[0].Trim();
				if (text3.Length == 0)
				{
					continue;
				}
				if (text3.EndsWith(":"))
				{
					text2 = Canonical(text3.Substring(0, text3.Length - 1));
					if (text2 == null)
					{
						throw new FormatException("Unknown race on line " + num);
					}
					if (!hashSet.Add(text2))
					{
						throw new FormatException("Duplicate race section on line " + num);
					}
					continue;
				}
				if (text2 == null)
				{
					throw new FormatException("SteamID outside a race section on line " + num);
				}
				if (text3.StartsWith("-"))
				{
					text3 = text3.Substring(1).Trim();
				}
				text3 = text3.Trim('"', '\'');
				if (!IsSteamId(text3))
				{
					throw new FormatException("Expected a 17-digit SteamID64 on line " + num);
				}
				if (raceAssignments.players.ContainsKey(text3))
				{
					throw new FormatException("SteamID appears more than once: " + text3);
				}
				raceAssignments.players.Add(text3, text2);
			}
			return raceAssignments;
		}

		public static string Template()
		{
			StringBuilder stringBuilder = new StringBuilder();
			stringBuilder.AppendLine("# KingAndGodsAdditional - server-owned race assignments");
			stringBuilder.AppendLine("# Edit ONLY on the server (or on the host for a hosted game).");
			stringBuilder.AppendLine("# Unlisted players become Human. One SteamID64 in exactly one section.");
			stringBuilder.AppendLine("# Use real 17-digit SteamID64 values, NOT character IDs, names or Steam friend codes.");
			stringBuilder.AppendLine("# Reloads automatically. Invalid edits keep the last valid configuration.");
			stringBuilder.AppendLine("# Example: add a line such as '  - 76561198000000000' under skadi:");
			string[] names = Names;
			foreach (string text in names)
			{
				stringBuilder.AppendLine();
				stringBuilder.AppendLine(text + ":");
			}
			return stringBuilder.ToString();
		}
	}
	public sealed class RecipeAuthority
	{
		private sealed class Original
		{
			public Requirement[] Resources;

			public bool OneIngredient;
		}

		private readonly string file;

		private readonly Action<string> log;

		private DateTime stamp = DateTime.MinValue;

		private ObjectDB database;

		private List<RecipeRule> active = new List<RecipeRule>();

		private List<RecipeRule> pending;

		private readonly Dictionary<Recipe, Original> originals = new Dictionary<Recipe, Original>();

		private readonly Dictionary<Recipe, Requirement[]> imposed = new Dictionary<Recipe, Requirement[]>();

		private readonly List<Recipe> created = new List<Recipe>();

		private readonly Dictionary<Piece, Requirement[]> originalPieces = new Dictionary<Piece, Requirement[]>();

		private readonly Dictionary<Piece, Requirement[]> imposedPieces = new Dictionary<Piece, Requirement[]>();

		private string lastError;

		private int itemCount = -1;

		private int recipeCount = -1;

		public string Payload { get; private set; } = "recipes:\n";

		public bool Received { get; private set; }

		public RecipeAuthority(string path, Action<string> logger)
		{
			file = path;
			log = logger;
			if (!File.Exists(file))
			{
				File.WriteAllText(file, RecipeRules.Template());
			}
		}

		public void ServerTick(bool force = false)
		{
			DateTime lastWriteTimeUtc = File.GetLastWriteTimeUtc(file);
			if (force || lastWriteTimeUtc != stamp)
			{
				stamp = lastWriteTimeUtc;
				try
				{
					pending = RecipeRules.Parse(File.ReadAllText(file));
					lastError = null;
				}
				catch (Exception ex)
				{
					Report("Invalid recipes YAML; keeping previous recipes: " + ex.Message);
				}
			}
			Tick();
		}

		public void Accept(string text)
		{
			pending = RecipeRules.Parse(text);
			Received = false;
			Tick();
		}

		public void Tick()
		{
			ObjectDB instance = ObjectDB.instance;
			if ((Object)(object)instance == (Object)null || instance.m_items.Count == 0)
			{
				return;
			}
			if ((Object)(object)database != (Object)(object)instance)
			{
				RestoreEdits();
				database = instance;
				if (pending == null)
				{
					pending = active;
				}
			}
			bool flag = itemCount != instance.m_items.Count || recipeCount != instance.m_recipes.Count;
			if (pending == null && flag)
			{
				pending = active;
			}
			if (pending != null)
			{
				try
				{
					Validate(instance, pending);
					List<RecipeRule> list = pending;
					RestoreEdits();
					database = instance;
					foreach (RecipeRule item in list)
					{
						ApplyRule(instance, item);
					}
					active = list;
					pending = null;
					Payload = RecipeRules.Serialize(active);
					Received = true;
					lastError = null;
					log("Applied recipe overrides: " + active.Count + " (item recipes: " + imposed.Count + ", building pieces: " + imposedPieces.Count + ")");
				}
				catch (Exception ex)
				{
					Report("Recipe overrides pending; previous valid recipes retained: " + ex.Message);
				}
			}
			foreach (KeyValuePair<Recipe, Requirement[]> item2 in imposed)
			{
				if ((Object)(object)item2.Key != (Object)null)
				{
					item2.Key.m_resources = item2.Value;
					item2.Key.m_requireOnlyOneIngredient = false;
				}
			}
			foreach (KeyValuePair<Piece, Requirement[]> imposedPiece in imposedPieces)
			{
				if ((Object)(object)imposedPiece.Key != (Object)null)
				{
					imposedPiece.Key.m_resources = imposedPiece.Value;
				}
			}
			itemCount = instance.m_items.Count;
			recipeCount = instance.m_recipes.Count;
		}

		private static void Validate(ObjectDB db, List<RecipeRule> rules)
		{
			foreach (RecipeRule rule in rules)
			{
				GameObject itemPrefab = db.GetItemPrefab(rule.Prefab);
				if ((!((Object)(object)itemPrefab != (Object)null) || !((Object)(object)itemPrefab.GetComponent<ItemDrop>() != (Object)null)) && (Object)(object)FindPiece(db, rule.Prefab) == (Object)null)
				{
					throw new InvalidOperationException("Unknown item or building Piece prefab: " + rule.Prefab);
				}
				foreach (RecipeIngredient ingredient in rule.Ingredients)
				{
					GetItem(db, ingredient.Prefab);
				}
			}
		}

		private static Piece FindPiece(ObjectDB db, string name)
		{
			if ((Object)(object)ZNetScene.instance != (Object)null)
			{
				GameObject prefab = ZNetScene.instance.GetPrefab(name);
				if ((Object)(object)prefab != (Object)null)
				{
					Piece component = prefab.GetComponent<Piece>();
					if ((Object)(object)component != (Object)null)
					{
						return component;
					}
				}
			}
			foreach (GameObject item in db.m_items)
			{
				ItemDrop val = (((Object)(object)item == (Object)null) ? null : item.GetComponent<ItemDrop>());
				PieceTable val2 = (((Object)(object)val == (Object)null) ? null : val.m_itemData.m_shared.m_buildPieces);
				if ((Object)(object)val2 == (Object)null)
				{
					continue;
				}
				foreach (GameObject piece in val2.m_pieces)
				{
					if ((Object)(object)piece != (Object)null && ((Object)piece).name == name)
					{
						Piece component2 = piece.GetComponent<Piece>();
						if ((Object)(object)component2 != (Object)null)
						{
							return component2;
						}
					}
				}
			}
			return null;
		}

		private static ItemDrop GetItem(ObjectDB db, string name)
		{
			GameObject itemPrefab = db.GetItemPrefab(name);
			ItemDrop obj = (((Object)(object)itemPrefab == (Object)null) ? null : itemPrefab.GetComponent<ItemDrop>());
			if ((Object)(object)obj == (Object)null)
			{
				throw new InvalidOperationException("Unknown ItemDrop prefab: " + name);
			}
			return obj;
		}

		private void ApplyRule(ObjectDB db, RecipeRule rule)
		{
			GameObject itemPrefab = db.GetItemPrefab(rule.Prefab);
			if ((Object)(object)itemPrefab == (Object)null || (Object)(object)itemPrefab.GetComponent<ItemDrop>() == (Object)null)
			{
				Piece val = FindPiece(db, rule.Prefab);
				if ((Object)(object)val == (Object)null)
				{
					throw new InvalidOperationException("Building prefab unavailable: " + rule.Prefab);
				}
				Requirement[] array = (Requirement[])(((object)val.m_resources) ?? ((object)new Requirement[0]));
				originalPieces[val] = array;
				val.m_resources = BuildResources(db, rule, array);
				imposedPieces[val] = val.m_resources;
				return;
			}
			ItemDrop item = GetItem(db, rule.Prefab);
			List<Recipe> list = db.m_recipes.Where((Recipe r) => (Object)(object)r != (Object)null && (Object)(object)r.m_item != (Object)null && ((Object)((Component)r.m_item).gameObject).name == rule.Prefab).ToList();
			if (list.Count == 0)
			{
				Recipe val2 = ScriptableObject.CreateInstance<Recipe>();
				((Object)val2).name = "KAGRecipe_" + rule.Prefab;
				val2.m_item = item;
				val2.m_amount = 1;
				val2.m_enabled = true;
				val2.m_minStationLevel = 1;
				val2.m_craftingStation = null;
				db.m_recipes.Add(val2);
				created.Add(val2);
				list.Add(val2);
			}
			foreach (Recipe item2 in list)
			{
				Requirement[] array2 = (Requirement[])(((object)item2.m_resources) ?? ((object)new Requirement[0]));
				originals[item2] = new Original
				{
					Resources = array2,
					OneIngredient = item2.m_requireOnlyOneIngredient
				};
				item2.m_resources = BuildResources(db, rule, array2);
				item2.m_requireOnlyOneIngredient = false;
				imposed[item2] = item2.m_resources;
			}
		}

		private static Requirement[] BuildResources(ObjectDB db, RecipeRule rule, Requirement[] before)
		{
			//IL_0089: Unknown result type (might be due to invalid IL or missing references)
			//IL_0090: Expected O, but got Unknown
			List<Requirement> list = new List<Requirement>();
			IEnumerable<string> upgradeMaterials = from r in before
				where r != null && (Object)(object)r.m_resItem != (Object)null && r.m_amountPerLevel > 0
				select ((Object)((Component)r.m_resItem).gameObject).name;
			foreach (RecipeIngredient ingredient in RecipeRules.BaseWithUpgradeOnly(rule, upgradeMaterials))
			{
				Requirement val = ((IEnumerable<Requirement>)before).FirstOrDefault((Func<Requirement, bool>)((Requirement r) => r != null && (Object)(object)r.m_resItem != (Object)null && ((Object)((Component)r.m_resItem).gameObject).name == ingredient.Prefab));
				Requirement val2 = new Requirement();
				val2.m_resItem = GetItem(db, ingredient.Prefab);
				val2.m_amount = ingredient.Amount;
				val2.m_amountPerLevel = val?.m_amountPerLevel ?? 0;
				val2.m_recover = val?.m_recover ?? true;
				val2.m_upgraderResource = val?.m_upgraderResource ?? false;
				val2.m_extraAmountOnlyOneIngredient = val?.m_extraAmountOnlyOneIngredient ?? 0;
				list.Add(val2);
			}
			return list.ToArray();
		}

		private void Report(string error)
		{
			if (!(lastError == error))
			{
				lastError = error;
				log(error);
			}
		}

		private void RestoreEdits()
		{
			foreach (KeyValuePair<Recipe, Original> original in originals)
			{
				if ((Object)(object)original.Key != (Object)null)
				{
					original.Key.m_resources = original.Value.Resources;
					original.Key.m_requireOnlyOneIngredient = original.Value.OneIngredient;
				}
			}
			foreach (KeyValuePair<Piece, Requirement[]> originalPiece in originalPieces)
			{
				if ((Object)(object)originalPiece.Key != (Object)null)
				{
					originalPiece.Key.m_resources = originalPiece.Value;
				}
			}
			if ((Object)(object)database != (Object)null)
			{
				foreach (Recipe item in created)
				{
					database.m_recipes.Remove(item);
					if ((Object)(object)item != (Object)null)
					{
						Object.Destroy((Object)(object)item);
					}
				}
			}
			originals.Clear();
			imposed.Clear();
			created.Clear();
			originalPieces.Clear();
			imposedPieces.Clear();
		}

		public void Reset()
		{
			RestoreEdits();
			database = null;
			active = new List<RecipeRule>();
			pending = null;
			Payload = "recipes:\n";
			Received = false;
			stamp = DateTime.MinValue;
			itemCount = -1;
			recipeCount = -1;
			lastError = null;
		}
	}
	public sealed class RecipeIngredient
	{
		public string Prefab;

		public int Amount;

		public RecipeIngredient(string prefab, int amount)
		{
			Prefab = prefab;
			Amount = amount;
		}
	}
	public sealed class RecipeRule
	{
		public string Prefab;

		public readonly List<RecipeIngredient> Ingredients = new List<RecipeIngredient>();
	}
	public static class RecipeRules
	{
		public static List<RecipeRule> Parse(string text)
		{
			if (text == null || text.Length > 524288)
			{
				throw new FormatException("Recipe file exceeds 512 KiB");
			}
			List<RecipeRule> list = new List<RecipeRule>();
			HashSet<string> hashSet = new HashSet<string>(StringComparer.Ordinal);
			int num = 0;
			string[] array = text.Replace("\r", "").Split('\n');
			foreach (string obj in array)
			{
				num++;
				string text2 = obj.Split('#')[0].Trim();
				if (text2.Length == 0 || text2 == "recipes:" || text2 == "recipes: []")
				{
					continue;
				}
				if (text2.StartsWith("- ", StringComparison.Ordinal))
				{
					text2 = text2.Substring(2).Trim();
				}
				if (text2.StartsWith("\"", StringComparison.Ordinal) && text2.EndsWith("\"", StringComparison.Ordinal))
				{
					text2 = text2.Substring(1, text2.Length - 2);
				}
				else if (text2.StartsWith("'", StringComparison.Ordinal) && text2.EndsWith("'", StringComparison.Ordinal))
				{
					text2 = text2.Substring(1, text2.Length - 2);
				}
				string[] array2 = (from p in text2.Split(',')
					select p.Trim()).ToArray();
				if (array2.Length < 3 || array2.Length % 2 != 1)
				{
					throw new FormatException("Line " + num + ": expected Prefab, Material, Amount, ...");
				}
				if (!NameValid(array2[0]) || !hashSet.Add(array2[0]))
				{
					throw new FormatException("Line " + num + ": invalid or duplicate output prefab");
				}
				RecipeRule recipeRule = new RecipeRule
				{
					Prefab = array2[0]
				};
				HashSet<string> hashSet2 = new HashSet<string>(StringComparer.Ordinal);
				for (int num2 = 1; num2 < array2.Length; num2 += 2)
				{
					if (!NameValid(array2[num2]) || !hashSet2.Add(array2[num2]))
					{
						throw new FormatException("Line " + num + ": invalid or duplicate material");
					}
					if (array2[num2] == array2[0])
					{
						throw new FormatException("Line " + num + ": item cannot require itself");
					}
					if (!int.TryParse(array2[num2 + 1], NumberStyles.None, CultureInfo.InvariantCulture, out var result) || result < 1 || result > 1000000)
					{
						throw new FormatException("Line " + num + ": amount must be 1..1000000");
					}
					recipeRule.Ingredients.Add(new RecipeIngredient(array2[num2], result));
				}
				list.Add(recipeRule);
			}
			if (list.Count > 2000)
			{
				throw new FormatException("At most 2000 recipe overrides are supported");
			}
			return list;
		}

		private static bool NameValid(string name)
		{
			if (!string.IsNullOrWhiteSpace(name) && name.Length <= 160)
			{
				return name.IndexOfAny(new char[11]
				{
					'\t', '\n', '\r', '"', '\'', '\\', ',', '#', ':', '[',
					']'
				}) < 0;
			}
			return false;
		}

		public static string Serialize(IEnumerable<RecipeRule> rules)
		{
			StringBuilder stringBuilder = new StringBuilder("recipes:\n");
			foreach (RecipeRule rule in rules)
			{
				stringBuilder.Append("  - \"").Append(rule.Prefab);
				foreach (RecipeIngredient ingredient in rule.Ingredients)
				{
					stringBuilder.Append(", ").Append(ingredient.Prefab).Append(", ")
						.Append(ingredient.Amount.ToString(CultureInfo.InvariantCulture));
				}
				stringBuilder.AppendLine("\"");
			}
			return stringBuilder.ToString();
		}

		public static List<RecipeIngredient> BaseWithUpgradeOnly(RecipeRule rule, IEnumerable<string> upgradeMaterials)
		{
			List<RecipeIngredient> list = rule.Ingredients.Select((RecipeIngredient i) => new RecipeIngredient(i.Prefab, i.Amount)).ToList();
			foreach (string prefab in upgradeMaterials.Distinct<string>(StringComparer.Ordinal))
			{
				if (!list.Any((RecipeIngredient i) => i.Prefab == prefab))
				{
					list.Add(new RecipeIngredient(prefab, 0));
				}
			}
			return list;
		}

		public static string Template()
		{
			return "# KingAndGodsAdditional - crafting recipes (SERVER FILE)\n# Format: ItemPrefab, MaterialPrefab, Amount, MaterialPrefab, Amount, ...\n# Use internal prefab names, case-sensitive. One item per line.\n# Existing crafting station/output amount and upgrade costs are preserved.\n# A registered item without a recipe gets a new hand-crafting recipe, output 1.\n# Server changes reload automatically. Client edits are ignored online.\n# Uncomment the example below to enable it. Empty file = no changes.\nrecipes:\n  # - \"Hammer, Tin, 20, Copper, 15, Stone, 10, Wood, 10, Resin, 4\"\n";
		}
	}
	internal static class SkillsButton
	{
		private static MethodInfo ensure;

		private static MethodInfo open;

		private static MethodInfo skillMode;

		private static FieldInfo instance;

		internal static void Install(Harmony harmony)
		{
			//IL_00b5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ce: Expected O, but got Unknown
			Type type = AccessTools.TypeByName("PassiveTree.UI.TreeWindow");
			ensure = AccessTools.Method(type, "Ensure", (Type[])null, (Type[])null);
			open = AccessTools.Method(type, "Open", (Type[])null, (Type[])null);
			skillMode = AccessTools.Method(type, "SetCraftMode", (Type[])null, (Type[])null);
			instance = AccessTools.Field(type, "Instance");
			if (ensure == null || open == null || skillMode == null || instance == null)
			{
				throw new MissingMemberException("PathOfValheiman skill tree API");
			}
			harmony.Patch((MethodBase)AccessTools.Method(typeof(InventoryGui), "OnOpenSkills", (Type[])null, (Type[])null), new HarmonyMethod(typeof(SkillsButton), "OpenTree", (Type[])null)
			{
				priority = 800
			}, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
		}

		private static bool OpenTree()
		{
			if ((Object)(object)Player.m_localPlayer == (Object)null)
			{
				return false;
			}
			ensure.Invoke(null, null);
			object value = instance.GetValue(null);
			if (value == null)
			{
				return true;
			}
			open.Invoke(value, null);
			skillMode.Invoke(value, new object[1] { false });
			return false;
		}
	}
}