Decompiled source of Kynda v1.0.2

plugins/Kynda/Kynda.dll

Decompiled 13 hours ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using Ezomic.Core;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using SoftReferenceableAssets;
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.SceneManagement;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.6.2", FrameworkDisplayName = ".NET Framework 4.6.2")]
[assembly: AssemblyCompany("Thijssen Software")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyCopyright("Copyright (c) 2026 Robbin Thijssen")]
[assembly: AssemblyDescription("Load smelters, kilns and fires several at a time, and build upgrades to make them hold more.")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+df6ae78643f784eea9d41d4d1ef029efbac06ea6")]
[assembly: AssemblyProduct("Kynda")]
[assembly: AssemblyTitle("Kynda")]
[assembly: AssemblyVersion("1.0.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace Ezomic.Shared
{
	public static class Prefabs
	{
		private sealed class Kept
		{
			internal string Name;

			internal Func<GameObject> Build;

			internal bool Item;

			internal string Tool;

			internal GameObject Prefab;

			internal int Failures;

			internal bool Abandoned;
		}

		private static ManualLogSource _log;

		private static GameObject _holder;

		private static readonly List<Kept> Standing = new List<Kept>();

		private const int MaxFailures = 5;

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

		private static FieldRef<ZNetScene, Dictionary<int, GameObject>> _named;

		private static MethodInfo _updateRegisters;

		public static ManualLogSource Log
		{
			get
			{
				return _log ?? (_log = Logger.CreateLogSource("Prefabs"));
			}
			set
			{
				_log = value;
			}
		}

		public static Transform Holder
		{
			get
			{
				//IL_0021: Unknown result type (might be due to invalid IL or missing references)
				//IL_002b: Expected O, but got Unknown
				if ((Object)(object)_holder == (Object)null)
				{
					_holder = new GameObject(Log.SourceName + "Prefabs");
					_holder.SetActive(false);
					Object.DontDestroyOnLoad((Object)(object)_holder);
				}
				return _holder.transform;
			}
		}

		public static void Keep(string name, Func<GameObject> build, bool item = false, string buildTool = null)
		{
			if (string.IsNullOrEmpty(name))
			{
				throw new ArgumentNullException("name");
			}
			if (build == null)
			{
				throw new ArgumentNullException("build");
			}
			foreach (Kept item2 in Standing)
			{
				if (!(item2.Name != name))
				{
					item2.Build = build;
					item2.Item = item;
					item2.Tool = buildTool;
					item2.Failures = 0;
					item2.Abandoned = false;
					return;
				}
			}
			Standing.Add(new Kept
			{
				Name = name,
				Build = build,
				Item = item,
				Tool = buildTool
			});
			Log.LogInfo((object)("Keeping " + name + " registered."));
		}

		public static void Drop(string name)
		{
			Standing.RemoveAll((Kept k) => k.Name == name);
		}

		public static void Tick()
		{
			if (Standing.Count == 0)
			{
				return;
			}
			ZNetScene instance = ZNetScene.instance;
			if (!((Object)(object)instance == (Object)null))
			{
				for (int i = 0; i < Standing.Count; i++)
				{
					Apply(Standing[i], instance);
				}
			}
		}

		private static void Apply(Kept kept, ZNetScene scene)
		{
			if (!kept.Abandoned && (!((Object)(object)kept.Prefab == (Object)null) || TryBuild(kept)))
			{
				if ((Object)(object)scene.GetPrefab(kept.Name) == (Object)null)
				{
					Register(kept.Prefab);
				}
				if (kept.Item)
				{
					RegisterItem(kept.Prefab);
				}
				if (!string.IsNullOrEmpty(kept.Tool))
				{
					AddToTool(kept.Prefab, kept.Tool);
				}
			}
		}

		private static bool TryBuild(Kept kept)
		{
			GameObject val = null;
			try
			{
				val = kept.Build();
			}
			catch (Exception ex)
			{
				Log.LogWarning((object)("Building " + kept.Name + " failed: " + ex.Message));
			}
			if ((Object)(object)val != (Object)null)
			{
				kept.Prefab = val;
				kept.Failures = 0;
				if (((Object)val).name != kept.Name)
				{
					((Object)val).name = kept.Name;
				}
				return true;
			}
			if (++kept.Failures < 5)
			{
				return false;
			}
			kept.Abandoned = true;
			Log.LogError((object)(kept.Name + " could not be built after " + 5 + " attempts and will not be retried. Anything already built with it in a world is untouched; it simply will not be placeable this session."));
			return false;
		}

		public static bool Known(string name)
		{
			if ((Object)(object)ZNetScene.instance != (Object)null)
			{
				return (Object)(object)ZNetScene.instance.GetPrefab(name) != (Object)null;
			}
			return false;
		}

		public static GameObject Clone(GameObject source, string name)
		{
			if ((Object)(object)source == (Object)null)
			{
				return null;
			}
			bool forceDisableInit = ZNetView.m_forceDisableInit;
			ZNetView.m_forceDisableInit = true;
			GameObject val;
			try
			{
				val = Object.Instantiate<GameObject>(source, Holder);
			}
			finally
			{
				ZNetView.m_forceDisableInit = forceDisableInit;
			}
			((Object)val).name = name;
			return val;
		}

		public static GameObject Donor(string commaSeparated, out string chosen)
		{
			chosen = null;
			if ((Object)(object)ZNetScene.instance == (Object)null)
			{
				return null;
			}
			string[] array = (commaSeparated ?? "").Split(new char[1] { ',' });
			for (int i = 0; i < array.Length; i++)
			{
				string text = array[i].Trim();
				if (text.Length != 0)
				{
					GameObject prefab = ZNetScene.instance.GetPrefab(text);
					if (!((Object)(object)prefab == (Object)null))
					{
						chosen = text;
						return prefab;
					}
				}
			}
			return null;
		}

		public static bool Register(GameObject prefab)
		{
			ZNetScene instance = ZNetScene.instance;
			if ((Object)(object)instance == (Object)null || (Object)(object)prefab == (Object)null)
			{
				return false;
			}
			if ((Object)(object)instance.GetPrefab(((Object)prefab).name) != (Object)null)
			{
				return true;
			}
			if (!instance.m_prefabs.Contains(prefab))
			{
				instance.m_prefabs.Add(prefab);
			}
			try
			{
				NamedPrefabs(instance)[StringExtensionMethods.GetStableHashCode(((Object)prefab).name)] = prefab;
			}
			catch (Exception ex)
			{
				ComplainOnce("scene:" + ((Object)prefab).name, "Could not register " + ((Object)prefab).name + " with ZNetScene: " + ex.Message);
				return false;
			}
			Log.LogInfo((object)("Registered " + ((Object)prefab).name + " with ZNetScene."));
			return true;
		}

		public static bool RegisterItem(GameObject prefab)
		{
			ObjectDB instance = ObjectDB.instance;
			if ((Object)(object)instance == (Object)null || (Object)(object)prefab == (Object)null)
			{
				return false;
			}
			if (instance.m_items == null || instance.m_items.Count == 0)
			{
				return false;
			}
			if ((Object)(object)instance.GetItemPrefab(((Object)prefab).name) != (Object)null)
			{
				return true;
			}
			if (!instance.m_items.Contains(prefab))
			{
				instance.m_items.Add(prefab);
			}
			try
			{
				UpdateRegisters(instance);
			}
			catch (Exception ex)
			{
				ComplainOnce("db:" + ((Object)prefab).name, "Could not refresh ObjectDB for " + ((Object)prefab).name + ": " + ex.Message);
				return false;
			}
			Log.LogInfo((object)("Registered " + ((Object)prefab).name + " with ObjectDB."));
			return true;
		}

		public static PieceTable ToolPieces(string toolPrefab)
		{
			if ((Object)(object)ObjectDB.instance == (Object)null || string.IsNullOrEmpty(toolPrefab))
			{
				return null;
			}
			GameObject itemPrefab = ObjectDB.instance.GetItemPrefab(toolPrefab);
			ItemDrop val = (((Object)(object)itemPrefab != (Object)null) ? itemPrefab.GetComponent<ItemDrop>() : null);
			if ((Object)(object)val == (Object)null || val.m_itemData == null || val.m_itemData.m_shared == null)
			{
				return null;
			}
			PieceTable buildPieces = val.m_itemData.m_shared.m_buildPieces;
			if (!((Object)(object)buildPieces != (Object)null) || buildPieces.m_pieces == null)
			{
				return null;
			}
			return buildPieces;
		}

		public static bool InTool(GameObject prefab, string toolPrefab)
		{
			PieceTable val = ToolPieces(toolPrefab);
			if ((Object)(object)val != (Object)null && (Object)(object)prefab != (Object)null)
			{
				return val.m_pieces.Contains(prefab);
			}
			return false;
		}

		public static bool AddToTool(GameObject prefab, string toolPrefab)
		{
			if ((Object)(object)prefab == (Object)null)
			{
				return false;
			}
			PieceTable val = ToolPieces(toolPrefab);
			if ((Object)(object)val == (Object)null)
			{
				return false;
			}
			if (val.m_pieces.Contains(prefab))
			{
				return true;
			}
			val.m_pieces.Add(prefab);
			Log.LogInfo((object)(((Object)prefab).name + " added to the " + toolPrefab + "."));
			return true;
		}

		private static void ComplainOnce(string key, string message)
		{
			if (Complained.Add(key))
			{
				Log.LogError((object)message);
			}
		}

		private static Dictionary<int, GameObject> NamedPrefabs(ZNetScene scene)
		{
			if (_named == null)
			{
				_named = AccessTools.FieldRefAccess<ZNetScene, Dictionary<int, GameObject>>("m_namedPrefabs");
			}
			return _named.Invoke(scene);
		}

		private static void UpdateRegisters(ObjectDB db)
		{
			if (_updateRegisters == null)
			{
				_updateRegisters = AccessTools.Method(typeof(ObjectDB), "UpdateRegisters", (Type[])null, (Type[])null);
			}
			if (_updateRegisters == null)
			{
				throw new MissingMethodException("ObjectDB.UpdateRegisters");
			}
			_updateRegisters.Invoke(db, null);
		}
	}
}
namespace Kynda
{
	internal static class BatchAdd
	{
		private static readonly MethodInfo SmelterGetFuel = AccessTools.Method(typeof(Smelter), "GetFuel", (Type[])null, (Type[])null);

		private static readonly MethodInfo SmelterGetQueueSize = AccessTools.Method(typeof(Smelter), "GetQueueSize", (Type[])null, (Type[])null);

		private static readonly MethodInfo SmelterFindCookable = AccessTools.Method(typeof(Smelter), "FindCookableItem", (Type[])null, (Type[])null);

		private static readonly FieldInfo SmelterNView = AccessTools.Field(typeof(Smelter), "m_nview");

		private static readonly FieldInfo FireplaceNView = AccessTools.Field(typeof(Fireplace), "m_nview");

		private static bool Ready
		{
			get
			{
				if (SmelterGetFuel != null && SmelterGetQueueSize != null && SmelterFindCookable != null && SmelterNView != null)
				{
					return FireplaceNView != null;
				}
				return false;
			}
		}

		public static bool Verify()
		{
			List<string> list = new List<string>();
			if (SmelterGetFuel == null)
			{
				list.Add("Smelter.GetFuel");
			}
			if (SmelterGetQueueSize == null)
			{
				list.Add("Smelter.GetQueueSize");
			}
			if (SmelterFindCookable == null)
			{
				list.Add("Smelter.FindCookableItem");
			}
			if (SmelterNView == null)
			{
				list.Add("Smelter.m_nview");
			}
			if (FireplaceNView == null)
			{
				list.Add("Fireplace.m_nview");
			}
			if (list.Count == 0)
			{
				return true;
			}
			KyndaPlugin.Log.LogError((object)("Game members this mod reflects on are missing - batching is disabled: " + string.Join(", ", list.ToArray())));
			return false;
		}

		private static int Extra(int perAdd)
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			KeyCode value = KyndaConfig.BatchModifier.Value;
			if ((int)value != 0 && !Input.GetKey(value))
			{
				return 0;
			}
			return Mathf.Max(0, perAdd - 1);
		}

		internal unsafe static string BatchHint(int perAdd)
		{
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			if (perAdd <= 1)
			{
				return "";
			}
			KeyCode value = KyndaConfig.BatchModifier.Value;
			if ((int)value != 0)
			{
				return "\n[<color=yellow><b>" + ((object)(*(KeyCode*)(&value))/*cast due to .constrained prefix*/).ToString() + "</b></color>] x" + perAdd;
			}
			return "\n<color=grey>x" + perAdd + " per press</color>";
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(Smelter), "OnAddFuel")]
		private static void BatchFuel(Smelter __instance, bool __result, Humanoid user)
		{
			if (!__result || (Object)(object)user == (Object)null || !Ready)
			{
				return;
			}
			int num = Extra(KyndaConfig.SmelterItemsPerAdd.Value);
			if (num <= 0)
			{
				return;
			}
			object? value = SmelterNView.GetValue(__instance);
			ZNetView val = (ZNetView)((value is ZNetView) ? value : null);
			if ((Object)(object)val == (Object)null || !val.IsValid())
			{
				return;
			}
			Inventory inventory = user.GetInventory();
			if (inventory == null || (Object)(object)__instance.m_fuelItem == (Object)null)
			{
				return;
			}
			string name = __instance.m_fuelItem.m_itemData.m_shared.m_name;
			float num2 = (float)SmelterGetFuel.Invoke(__instance, null) + 1f;
			int num3 = 0;
			for (int i = 0; i < num; i++)
			{
				if (num2 > (float)(__instance.m_maxFuel - 1))
				{
					break;
				}
				if (!inventory.HaveItem(name, true))
				{
					break;
				}
				inventory.RemoveItem(name, 1, -1, true);
				val.InvokeRPC("RPC_AddFuel", Array.Empty<object>());
				num2 += 1f;
				num3++;
			}
			Report(__instance.m_name, "fuel", num3);
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(Smelter), "OnAddOre")]
		private static void BatchOre(Smelter __instance, bool __result, Humanoid user)
		{
			if (!__result || (Object)(object)user == (Object)null || !Ready)
			{
				return;
			}
			int num = Extra(KyndaConfig.SmelterItemsPerAdd.Value);
			if (num <= 0)
			{
				return;
			}
			object? value = SmelterNView.GetValue(__instance);
			ZNetView val = (ZNetView)((value is ZNetView) ? value : null);
			if ((Object)(object)val == (Object)null || !val.IsValid())
			{
				return;
			}
			Inventory inventory = user.GetInventory();
			if (inventory == null)
			{
				return;
			}
			int num2 = (int)SmelterGetQueueSize.Invoke(__instance, null) + 1;
			int num3 = 0;
			for (int i = 0; i < num; i++)
			{
				if (num2 >= __instance.m_maxOre)
				{
					break;
				}
				object? obj = SmelterFindCookable.Invoke(__instance, new object[1] { inventory });
				ItemData val2 = (ItemData)((obj is ItemData) ? obj : null);
				if (val2 == null || (Object)(object)val2.m_dropPrefab == (Object)null)
				{
					break;
				}
				inventory.RemoveItem(val2, 1);
				val.InvokeRPC("RPC_AddOre", new object[1] { ((Object)val2.m_dropPrefab).name });
				num2++;
				num3++;
			}
			Report(__instance.m_name, "ore", num3);
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(Fireplace), "UseItem")]
		private static void BatchFireplaceUseItem(Fireplace __instance, bool __result, Humanoid user, ItemData item)
		{
			if (__result && !((Object)(object)user == (Object)null) && item != null && !((Object)(object)__instance.m_fuelItem == (Object)null) && !(item.m_shared.m_name != __instance.m_fuelItem.m_itemData.m_shared.m_name))
			{
				TopUpFire(__instance, user);
			}
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(Fireplace), "Interact")]
		private static void BatchFireplaceInteract(Fireplace __instance, bool __result, Humanoid user, bool hold, bool alt)
		{
			if (!(!__result || hold) && !((Object)(object)user == (Object)null) && Ready)
			{
				object? value = FireplaceNView.GetValue(__instance);
				ZNetView val = (ZNetView)((value is ZNetView) ? value : null);
				if (!((Object)(object)val == (Object)null) && val.IsValid() && (!__instance.m_canTurnOff || alt || !(val.GetZDO().GetFloat(ZDOVars.s_fuel, 0f) > 0f)))
				{
					TopUpFire(__instance, user);
				}
			}
		}

		private static void TopUpFire(Fireplace fireplace, Humanoid user)
		{
			int num = Extra(KyndaConfig.FireplaceItemsPerAdd.Value);
			if (num <= 0 || !Ready || fireplace.m_infiniteFuel || !fireplace.m_canRefill)
			{
				return;
			}
			object? value = FireplaceNView.GetValue(fireplace);
			ZNetView val = (ZNetView)((value is ZNetView) ? value : null);
			if ((Object)(object)val == (Object)null || !val.IsValid())
			{
				return;
			}
			Inventory inventory = user.GetInventory();
			if (inventory == null || (Object)(object)fireplace.m_fuelItem == (Object)null)
			{
				return;
			}
			string name = fireplace.m_fuelItem.m_itemData.m_shared.m_name;
			float num2 = val.GetZDO().GetFloat(ZDOVars.s_fuel, 0f) + 1f;
			int num3 = 0;
			for (int i = 0; i < num; i++)
			{
				if ((float)Mathf.CeilToInt(num2) >= fireplace.m_maxFuel)
				{
					break;
				}
				if (!inventory.HaveItem(name, true))
				{
					break;
				}
				inventory.RemoveItem(name, 1, -1, true);
				val.InvokeRPC("RPC_AddFuel", Array.Empty<object>());
				num2 += 1f;
				num3++;
			}
			Report(fireplace.m_name, "logs", num3);
		}

		private static void Report(string station, string what, int added)
		{
			if (KyndaConfig.Verbose.Value && added != 0)
			{
				KyndaPlugin.Log.LogInfo((object)(station + ": batched " + added + " extra " + what + "."));
			}
		}
	}
	internal static class HoverHint
	{
		[HarmonyPostfix]
		[HarmonyPatch(typeof(Smelter), "OnHoverAddFuel")]
		private static void SmelterFuelHover(Smelter __instance, ref string __result)
		{
			if (__instance.m_maxFuel > 0)
			{
				__result += BatchAdd.BatchHint(KyndaConfig.SmelterItemsPerAdd.Value);
			}
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(Smelter), "OnHoverAddOre")]
		private static void SmelterOreHover(Smelter __instance, ref string __result)
		{
			if (__instance.m_maxOre > 0)
			{
				__result += BatchAdd.BatchHint(KyndaConfig.SmelterItemsPerAdd.Value);
			}
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(Fireplace), "GetHoverText")]
		private static void FireplaceHover(Fireplace __instance, ref string __result)
		{
			if (!string.IsNullOrEmpty(__result) && __instance.m_canRefill && !__instance.m_infiniteFuel)
			{
				__result += BatchAdd.BatchHint(KyndaConfig.FireplaceItemsPerAdd.Value);
			}
		}

		public static void Apply(Harmony harmony)
		{
			try
			{
				harmony.PatchAll(typeof(HoverHint));
			}
			catch (Exception ex)
			{
				KyndaPlugin.Log.LogWarning((object)("Could not add the batch hint to hover text - batching still works, it just will not announce itself: " + ex.Message));
			}
		}
	}
	internal static class IconRender
	{
		private const int Size = 512;

		private const int Layer = 31;

		public static Sprite Shoot(GameObject prefab, string name)
		{
			//IL_020f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0216: Unknown result type (might be due to invalid IL or missing references)
			//IL_0050: 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_0073: Unknown result type (might be due to invalid IL or missing references)
			//IL_0078: Unknown result type (might be due to invalid IL or missing references)
			//IL_007c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0081: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a6: Expected O, but got Unknown
			//IL_00ae: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cd: Unknown result type (might be due to invalid IL or missing references)
			//IL_0105: Unknown result type (might be due to invalid IL or missing references)
			//IL_010a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0148: Unknown result type (might be due to invalid IL or missing references)
			//IL_0154: 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_0165: Unknown result type (might be due to invalid IL or missing references)
			//IL_016a: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a4: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a9: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d4: Unknown result type (might be due to invalid IL or missing references)
			//IL_01eb: Unknown result type (might be due to invalid IL or missing references)
			//IL_01fa: Unknown result type (might be due to invalid IL or missing references)
			//IL_034c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0351: Unknown result type (might be due to invalid IL or missing references)
			//IL_0358: Unknown result type (might be due to invalid IL or missing references)
			//IL_035f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0370: Unknown result type (might be due to invalid IL or missing references)
			//IL_037a: Expected O, but got Unknown
			//IL_03af: Unknown result type (might be due to invalid IL or missing references)
			//IL_03c3: Unknown result type (might be due to invalid IL or missing references)
			//IL_03d7: Unknown result type (might be due to invalid IL or missing references)
			//IL_0426: Unknown result type (might be due to invalid IL or missing references)
			//IL_0435: Unknown result type (might be due to invalid IL or missing references)
			//IL_0316: Unknown result type (might be due to invalid IL or missing references)
			//IL_02fb: Unknown result type (might be due to invalid IL or missing references)
			//IL_031b: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = null;
			GameObject val2 = null;
			RenderTexture val3 = null;
			RenderTexture active = RenderTexture.active;
			try
			{
				bool forceDisableInit = ZNetView.m_forceDisableInit;
				ZNetView.m_forceDisableInit = true;
				try
				{
					val = Object.Instantiate<GameObject>(prefab);
				}
				finally
				{
					ZNetView.m_forceDisableInit = forceDisableInit;
				}
				((Object)val).name = name + "_icon_subject";
				val.transform.position = new Vector3(0f, -8000f, 0f);
				val.transform.rotation = Quaternion.identity;
				SetLayer(val, 31);
				Bounds val4 = Framing(val);
				if (((Bounds)(ref val4)).size == Vector3.zero)
				{
					return null;
				}
				val2 = new GameObject(name + "_icon_rig");
				val2.transform.position = ((Bounds)(ref val4)).center;
				val2.transform.rotation = Quaternion.Euler(22f, 32f, 0f);
				Camera val5 = val2.AddComponent<Camera>();
				val5.orthographic = true;
				val5.clearFlags = (CameraClearFlags)2;
				val5.cullingMask = int.MinValue;
				((Behaviour)val5).enabled = false;
				Vector3 extents = ((Bounds)(ref val4)).extents;
				float magnitude = ((Vector3)(ref extents)).magnitude;
				val5.orthographicSize = magnitude * 1.06f;
				val5.nearClipPlane = 0.01f;
				val5.farClipPlane = magnitude * 8f;
				((Component)val5).transform.position = ((Bounds)(ref val4)).center - ((Component)val5).transform.forward * magnitude * 4f;
				Light(val2.transform, magnitude);
				val3 = (val5.targetTexture = RenderTexture.GetTemporary(512, 512, 24, (RenderTextureFormat)0, (RenderTextureReadWrite)2));
				bool fog = RenderSettings.fog;
				AmbientMode ambientMode = RenderSettings.ambientMode;
				Color ambientLight = RenderSettings.ambientLight;
				float ambientIntensity = RenderSettings.ambientIntensity;
				Color[] array;
				Color[] array2;
				try
				{
					RenderSettings.fog = false;
					RenderSettings.ambientMode = (AmbientMode)3;
					RenderSettings.ambientLight = new Color(0.34f, 0.35f, 0.38f);
					RenderSettings.ambientIntensity = 1f;
					array = Expose(val5, val3, Color.black);
					array2 = Expose(val5, val3, Color.white);
				}
				finally
				{
					RenderSettings.fog = fog;
					RenderSettings.ambientMode = ambientMode;
					RenderSettings.ambientLight = ambientLight;
					RenderSettings.ambientIntensity = ambientIntensity;
				}
				Color[] array3 = (Color[])(object)new Color[array.Length];
				int num = 0;
				for (int i = 0; i < array3.Length; i++)
				{
					float num2 = 1f - (array2[i].r - array[i].r);
					num2 = Mathf.Clamp01(Mathf.Max(num2, Mathf.Max(1f - (array2[i].g - array[i].g), 1f - (array2[i].b - array[i].b))));
					array3[i] = ((num2 <= 0.004f) ? new Color(0f, 0f, 0f, 0f) : new Color(array[i].r / num2, array[i].g / num2, array[i].b / num2, num2));
					if (num2 > 0.5f)
					{
						num++;
					}
				}
				Texture2D val6 = new Texture2D(512, 512, (TextureFormat)4, false)
				{
					filterMode = (FilterMode)1,
					wrapMode = (TextureWrapMode)1,
					name = name + "_icon",
					hideFlags = (HideFlags)61
				};
				val6.SetPixels(array3);
				val6.Apply();
				KyndaPlugin.Log.LogInfo((object)$"Icon for {name}: {512}px, subject {((Bounds)(ref val4)).size.x:0.00}x{((Bounds)(ref val4)).size.y:0.00}x{((Bounds)(ref val4)).size.z:0.00}m, {(float)num * 100f / (float)array3.Length:0}% of the frame covered.");
				Dump(val6, name);
				return Sprite.Create(val6, new Rect(0f, 0f, 512f, 512f), new Vector2(0.5f, 0.5f));
			}
			catch (Exception ex)
			{
				KyndaPlugin.Log.LogWarning((object)("Could not photograph " + name + " for its icon: " + ex.Message + " - falling back to the png beside the dll."));
				return null;
			}
			finally
			{
				RenderTexture.active = active;
				if ((Object)(object)val3 != (Object)null)
				{
					RenderTexture.ReleaseTemporary(val3);
				}
				if ((Object)(object)val2 != (Object)null)
				{
					Object.DestroyImmediate((Object)(object)val2);
				}
				if ((Object)(object)val != (Object)null)
				{
					Object.DestroyImmediate((Object)(object)val);
				}
			}
		}

		private static Color[] Expose(Camera camera, RenderTexture target, Color background)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: Expected O, but got Unknown
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			camera.backgroundColor = new Color(background.r, background.g, background.b, 1f);
			camera.Render();
			RenderTexture.active = target;
			Texture2D val = new Texture2D(512, 512, (TextureFormat)4, false);
			try
			{
				val.ReadPixels(new Rect(0f, 0f, 512f, 512f), 0, 0);
				val.Apply();
				return val.GetPixels();
			}
			finally
			{
				Object.DestroyImmediate((Object)(object)val);
			}
		}

		private static void Dump(Texture2D texture, string name)
		{
			if (KyndaConfig.Verbose == null || !KyndaConfig.Verbose.Value)
			{
				return;
			}
			try
			{
				Type type = AccessTools.TypeByName("UnityEngine.ImageConversion");
				MethodInfo methodInfo = ((type != null) ? AccessTools.Method(type, "EncodeToPNG", new Type[1] { typeof(Texture2D) }, (Type[])null) : null);
				if (!(methodInfo == null) && methodInfo.Invoke(null, new object[1] { texture }) is byte[] bytes)
				{
					string text = Path.Combine(Path.GetDirectoryName(typeof(IconRender).Assembly.Location), name + "_rendered.png");
					File.WriteAllBytes(text, bytes);
					KyndaPlugin.Log.LogInfo((object)("Wrote " + text + " to look at."));
				}
			}
			catch (Exception ex)
			{
				KyndaPlugin.Log.LogWarning((object)("Could not dump the icon: " + ex.Message));
			}
		}

		private static Bounds Framing(GameObject subject)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0077: Unknown result type (might be due to invalid IL or missing references)
			//IL_0067: Unknown result type (might be due to invalid IL or missing references)
			//IL_006c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0071: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: 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_0041: Unknown result type (might be due to invalid IL or missing references)
			Bounds result = default(Bounds);
			bool flag = false;
			Renderer[] componentsInChildren = subject.GetComponentsInChildren<Renderer>(false);
			foreach (Renderer val in componentsInChildren)
			{
				if (!((Object)(object)val == (Object)null) && val.enabled && !(val is ParticleSystemRenderer))
				{
					if (!flag)
					{
						result = val.bounds;
						flag = true;
					}
					else
					{
						((Bounds)(ref result)).Encapsulate(val.bounds);
					}
				}
			}
			if (!flag)
			{
				return new Bounds(subject.transform.position, Vector3.zero);
			}
			return result;
		}

		private static void Light(Transform rig, float reach)
		{
			//IL_0005: 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_0052: Unknown result type (might be due to invalid IL or missing references)
			//IL_0076: Unknown result type (might be due to invalid IL or missing references)
			//IL_0098: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c3: Unknown result type (might be due to invalid IL or missing references)
			Transform transform = new GameObject("key").transform;
			transform.SetParent(rig, false);
			transform.localRotation = Quaternion.Euler(28f, -22f, 0f);
			Light obj = ((Component)transform).gameObject.AddComponent<Light>();
			obj.type = (LightType)1;
			obj.color = new Color(1f, 0.96f, 0.88f);
			obj.intensity = 1.35f;
			obj.cullingMask = int.MinValue;
			Transform transform2 = new GameObject("fill").transform;
			transform2.SetParent(rig, false);
			transform2.localRotation = Quaternion.Euler(-14f, 158f, 0f);
			Light obj2 = ((Component)transform2).gameObject.AddComponent<Light>();
			obj2.type = (LightType)1;
			obj2.color = new Color(0.72f, 0.78f, 0.92f);
			obj2.intensity = 0.55f;
			obj2.cullingMask = int.MinValue;
		}

		private static void SetLayer(GameObject root, int layer)
		{
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			root.layer = layer;
			foreach (Transform item in root.transform)
			{
				SetLayer(((Component)item).gameObject, layer);
			}
		}
	}
	internal static class KyndaConfig
	{
		public static ConfigEntry<int> SmelterItemsPerAdd;

		public static ConfigEntry<int> FireplaceItemsPerAdd;

		public static ConfigEntry<KeyCode> BatchModifier;

		public static ConfigEntry<bool> Verbose;

		public static ConfigEntry<string> DonorCarrierLocations;

		public static ConfigEntry<bool> Enabled;

		public static ConfigEntry<string> Donor;

		public static ConfigEntry<string> Station;

		public static ConfigEntry<float> Range;

		public static ConfigEntry<int> MaxPerStation;

		public static ConfigEntry<float> TexelsPerMetre;

		public static ConfigEntry<bool> ShowLink;

		public static ConfigEntry<float> LinkHeight;

		public static ConfigEntry<string> PrefabSearch;

		public static ConfigEntry<bool> TestMode;

		public static ConfigEntry<bool> VariantMode;

		public static ConfigEntry<string> SkinTrials;

		public static ConfigEntry<bool> DumpShader;

		private const string TestCost = "Wood:1";

		public static string CostNow(UpgradeDef def)
		{
			if (!TestMode.Value && def.Cost != null)
			{
				return def.Cost.Value;
			}
			return "Wood:1";
		}

		public static void Bind(ConfigFile config)
		{
			TestMode = config.Bind<bool>("Diagnostics", "TestMode", false, "Makes both upgrades cost one wood, so they can be built and looked at without bronze. Announced in the log on startup so it is hard to leave on.");
			DonorCarrierLocations = config.Bind<string>("Diagnostics", "DonorCarrierLocations", "Vendor,Hildir", "Fallback only, and normally unused. A missing donor material is now loaded straight out of its own bundle by name, so no location has to be summoned for it. These location prefabs are soft-ref loaded only if that direct load finds nothing under the wanted name - a game update renaming an asset, or one of the few assets that exist solely inside a containing prefab. Comma-separated name fragments; blank turns the fallback off.");
			Verbose = config.Bind<bool>("Diagnostics", "Verbose", false, "Log each batched add and why it stopped.");
			VariantMode = config.Bind<bool>("Diagnostics", "VariantMode", false, "Put every candidate model on the hammer as its own piece at one wood each, named 'var: ...', so they can be built side by side and compared. DESTRUCTIVE WHEN TURNED OFF: anything built from a variant vanishes with it, because its prefab name stops existing. Build them somewhere you do not mind losing.");
			SkinTrials = config.Bind<string>("Diagnostics", "SkinTrials", "", "Comma-separated donor prefabs. Puts one copy of each upgrade on the hammer per donor, named 'skin: ...', so the same model can be seen wearing every candidate surface side by side. Empty turns it off. DESTRUCTIVE WHEN TURNED OFF, exactly as VariantMode is: anything built from a trial vanishes when its prefab name stops existing.");
			DumpShader = config.Bind<bool>("Diagnostics", "DumpShader", false, "List every property of each borrowed material's shader, with its type. Needed before our own texture can be written into one.");
			PrefabSearch = config.Bind<string>("Diagnostics", "PrefabSearch", "", "Comma-separated words. Every loaded prefab whose name contains one is listed in the log, which is how to find a prefab worth borrowing a material from. Empty turns it off. Scans everything loaded, so empty it again when you are done.");
			Enabled = config.Bind<bool>("Upgrades", "Enabled", true, "Add the two buildable upgrades that raise a nearby station's capacity. They register prefabs, so a world that later loads without this mod discards every one already built rather than reporting an error - which is what the version gate exists to prevent.");
			Donor = config.Bind<string>("Upgrades", "Donor", "piece_chest_barrel", "Prefab cloned for its machinery - ZNetView, Piece, WearNTear, placement rules. Its look, collision and icon are all replaced, so this is not a visual choice. Falls back to piece_chest_wood. Needs a restart.");
			Station = config.Bind<string>("Upgrades", "Station", "forge", "Prefab name of the crafting station you must stand near to build these. The forge, because both upgrades are held together with nails and a workbench could never have made them. Empty or an unknown name leaves the donor's, which is the workbench.");
			Range = config.Bind<float>("Upgrades", "Range", 4f, "How close an upgrade must be to the station it feeds.");
			MaxPerStation = config.Bind<int>("Upgrades", "MaxPerStation", 1, "How many upgrades of one kind count for a single station. One, because these are a one-time improvement rather than something to stack - the capacity figures are chosen to land on a round number exactly once. Raising it stacks them again; a bin that is not counting says so when you look at it.");
			TexelsPerMetre = config.Bind<float>("Upgrades", "TexelsPerMetre", 28f, "How coarse the borrowed texture is drawn, in texels per metre. Vanilla's props and piles run 24 to 54; its structural pieces run far finer and are the wrong thing to match. Higher is finer and eventually reads as flat colour, because the grain becomes smaller than a pixel on screen. A group too big for its donor's slice of the atlas is drawn coarser than this rather than tiled - the log says when.");
			ShowLink = config.Bind<bool>("Upgrades", "ShowLink", true, "Draw the game's own station-link effect from an upgrade to the station it feeds when you look at it - the same run of motes a chopping block draws to its workbench. Off is silent.");
			LinkHeight = config.Bind<float>("Upgrades", "LinkHeight", 0.8f, "How far up the upgrade the link starts, in metres. The default leaves it around the top of both pieces; at 0 it comes out of the ground.");
			UpgradePrefabs.Trough.Name = config.Bind<string>("Trough", "Name", "Tun", "Name shown on the hammer and when you look at one.");
			UpgradePrefabs.Trough.Stations = config.Bind<string>("Trough", "Stations", "smelter", "Station prefabs this upgrades, comma separated. A station not named here is left alone even if it is the right kind, and a bin standing next to one says it is feeding nothing rather than pretending.");
			UpgradePrefabs.Trough.Cost = config.Bind<string>("Trough", "Cost", "FineWood:20,IronNails:15", "Build cost, as Item:Amount pairs. The iron nails put it a biome beyond the smelter it upgrades, so it is an improvement you return to make rather than part of the original build.");
			UpgradePrefabs.Trough.Model = config.Bind<string>("Trough", "Model", "kynda_tun_camp.obj", "The OBJ loaded from beside the DLL. Its .col sidecar supplies the collision and its _icon.png the hammer icon, both matched by name - so dropping in a new model brings its own shape and picture with it.");
			UpgradePrefabs.Trough.Scale = config.Bind<float>("Trough", "Scale", 1f, "Overall size of the trough. Scales the collision with it, since the boxes are children of the piece.");
			UpgradePrefabs.Trough.SkinDonors = config.Bind<string>("Trough", "SkinDonors", "@fi_village_wood:keep,coal=@coal_pile:keep,ore=@copper_ore:0.02/0.30/0.46/0.22", "Which vanilla prefab this piece borrows its surface from. A bare prefab name covers the whole piece, which is the usual case and matches how vanilla builds a piece - one texture carrying every substance it is made of. group=prefab pairs override a single group. Empty uses the general list. Ore and coal keep their own surfaces either way: they are what is in the piece rather than what it is made of, and vanilla gives a smelter's ore heap its own material too.");
			UpgradePrefabs.Trough.OreCapacity = config.Bind<int>("Trough", "OreCapacity", 20, "Extra ore a smelter or furnace holds per trough. Vanilla's 10 becomes 30.");
			UpgradePrefabs.Trough.FuelCapacity = config.Bind<int>("Trough", "FuelCapacity", 40, "Extra coal a smelter or furnace holds per trough. Vanilla's 20 becomes 60. Twice the ore figure on purpose - a smelter burns two coal per ore, so matching them would run the fuel out before the ore.");
			UpgradePrefabs.Woodrack.Stations = config.Bind<string>("Woodrack", "Stations", "charcoal_kiln", "Station prefabs this upgrades, comma separated. A station not named here is left alone even if it is the right kind.");
			UpgradePrefabs.Woodrack.Name = config.Bind<string>("Woodrack", "Name", "Woodrack", "Name shown on the hammer and when you look at one.");
			UpgradePrefabs.Woodrack.Cost = config.Bind<string>("Woodrack", "Cost", "FineWood:25,DeerHide:20,BronzeNails:25", "Build cost, as Item:Amount pairs. The bronze nails put it a tier behind the charcoal kiln it serves, so it is something you come back and add rather than raise alongside the kiln itself.");
			UpgradePrefabs.Woodrack.Model = config.Bind<string>("Woodrack", "Model", "kynda_rack_camp.obj", "The OBJ loaded from beside the DLL, with its .col and _icon.png matched by name.");
			UpgradePrefabs.Woodrack.Scale = config.Bind<float>("Woodrack", "Scale", 1f, "Overall size of the woodrack. Scales the collision with it, since the boxes are children of the piece.");
			UpgradePrefabs.Woodrack.SkinDonors = config.Bind<string>("Woodrack", "SkinDonors", "@wood_item:keep,frame=@woodwall:keep,roof=@straw_roof:keep,roofalpha=@straw_roof_alpha:keep", "Which vanilla prefab this piece borrows its surface from. A bare prefab name covers the whole piece; group=prefab pairs override a single group. Empty uses the general list.");
			UpgradePrefabs.Woodrack.OreCapacity = config.Bind<int>("Woodrack", "OreCapacity", 25, "Extra wood a charcoal kiln holds per woodrack. Vanilla's 25 becomes 50.");
			BatchModifier = config.Bind<KeyCode>("Batching", "BatchModifier", (KeyCode)304, "Hold this while interacting to add a batch instead of one. Plain use stays vanilla, so nothing is taken away - the batch is an option you reach for.");
			SmelterItemsPerAdd = config.Bind<int>("Batching", "SmelterItemsPerAdd", 3, "Ore or coal added per press at a smelter, kiln, blast furnace, windmill, spinning wheel or eitr refinery. Stops early at the station's capacity or when you run out. 1 restores vanilla.");
			FireplaceItemsPerAdd = config.Bind<int>("Batching", "FireplaceItemsPerAdd", 3, "Logs added per press at a campfire, hearth or torch. 1 restores vanilla.");
		}
	}
	[BepInPlugin("ezomic.valheim.kynda", "Kynda", "1.0.2")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public class KyndaPlugin : BaseUnityPlugin
	{
		public const string PluginGuid = "ezomic.valheim.kynda";

		public const string PluginName = "Kynda";

		public const string PluginVersion = "1.0.2";

		public const string PluginAuthor = "Robbin Thijssen";

		private const string CoreGuid = "ezomic.valheim.core";

		internal static bool CorePresent;

		internal static ManualLogSource Log;

		private Harmony _harmony;

		private void Awake()
		{
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			KyndaConfig.Bind(((BaseUnityPlugin)this).Config);
			SoftAssets.MakeEverythingLoadable();
			TryRegisterWithCore();
			_harmony = new Harmony("ezomic.valheim.kynda");
			_harmony.PatchAll(typeof(ScenePatches));
			if (BatchAdd.Verify())
			{
				_harmony.PatchAll(typeof(BatchAdd));
				HoverHint.Apply(_harmony);
			}
			Log.LogInfo((object)"Kynda 1.0.2 by Robbin Thijssen - ready.");
			if (KyndaConfig.TestMode.Value)
			{
				Log.LogWarning((object)"TEST MODE: both upgrades cost one wood. Turn TestMode off in the config before playing for real.");
			}
		}

		private void TryRegisterWithCore()
		{
			CorePresent = Chainloader.PluginInfos.ContainsKey("ezomic.valheim.core");
			if (!CorePresent)
			{
				Log.LogWarning((object)"Core is not installed, so there is no version gate. The upgrades still work, but a world loaded without this mod discards every one already built - and nothing will stop that happening.");
			}
			else
			{
				RegisterWithCore();
			}
		}

		[MethodImpl(MethodImplOptions.NoInlining)]
		private void RegisterWithCore()
		{
			Suite.Register("ezomic.valheim.kynda", "Kynda", "1.0.2", ((BaseUnityPlugin)this).Config, (Requirement)0, (Assembly)null);
		}

		private void OnDestroy()
		{
			if (_harmony != null)
			{
				_harmony.UnpatchSelf();
			}
		}

		private void Update()
		{
			if (!((Object)(object)ZNetScene.instance == (Object)null) && !((Object)(object)ObjectDB.instance == (Object)null))
			{
				UpgradePrefabs.Register();
				Skins.Tick();
				UpgradePrefabs.RefreshIcons();
			}
		}
	}
	internal sealed class ModelData
	{
		public Mesh Mesh;

		public string[] Groups;
	}
	internal static class ObjMesh
	{
		public static ModelData Load(string path)
		{
			if (!File.Exists(path))
			{
				KyndaPlugin.Log.LogWarning((object)("No model at " + path));
				return null;
			}
			try
			{
				return Parse(File.ReadAllLines(path), Path.GetFileNameWithoutExtension(path));
			}
			catch (Exception ex)
			{
				KyndaPlugin.Log.LogError((object)("Could not read " + path + ": " + ex.Message));
				return null;
			}
		}

		private static ModelData Parse(string[] lines, string name)
		{
			//IL_029c: Unknown result type (might be due to invalid IL or missing references)
			//IL_02a1: Unknown result type (might be due to invalid IL or missing references)
			//IL_02aa: Expected O, but got Unknown
			//IL_0148: Unknown result type (might be due to invalid IL or missing references)
			//IL_0183: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b3: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e8: Unknown result type (might be due to invalid IL or missing references)
			//IL_037c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0381: Unknown result type (might be due to invalid IL or missing references)
			List<Vector3> list = new List<Vector3>();
			List<Vector2> list2 = new List<Vector2>();
			List<Vector3> list3 = new List<Vector3>();
			List<Color> list4 = new List<Color>();
			List<string> list5 = new List<string>();
			List<List<int>> list6 = new List<List<int>>();
			Dictionary<string, int> index = new Dictionary<string, int>();
			Dictionary<string, int> lookup = new Dictionary<string, int>();
			List<Vector3> list7 = new List<Vector3>();
			List<Vector2> list8 = new List<Vector2>();
			List<Vector3> list9 = new List<Vector3>();
			List<Color> outShades = new List<Color>();
			CultureInfo invariantCulture = CultureInfo.InvariantCulture;
			List<int> triangles = Bucket("", list5, list6, index);
			for (int i = 0; i < lines.Length; i++)
			{
				string text = lines[i].Trim();
				if (text.Length == 0 || text[0] == '#')
				{
					continue;
				}
				string[] array = text.Split(new char[2] { ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries);
				if (array.Length < 2)
				{
					continue;
				}
				switch (array[0])
				{
				case "v":
					if (array.Length >= 4)
					{
						list.Add(new Vector3(float.Parse(array[1], invariantCulture), float.Parse(array[2], invariantCulture), float.Parse(array[3], invariantCulture)));
						if (array.Length >= 7)
						{
							list4.Add(new Color(float.Parse(array[4], invariantCulture), float.Parse(array[5], invariantCulture), float.Parse(array[6], invariantCulture), 1f));
						}
					}
					break;
				case "vt":
					if (array.Length >= 3)
					{
						list2.Add(new Vector2(float.Parse(array[1], invariantCulture), float.Parse(array[2], invariantCulture)));
					}
					break;
				case "vn":
					if (array.Length >= 4)
					{
						list3.Add(new Vector3(float.Parse(array[1], invariantCulture), float.Parse(array[2], invariantCulture), float.Parse(array[3], invariantCulture)));
					}
					break;
				case "usemtl":
					triangles = Bucket(array[1].Trim(), list5, list6, index);
					break;
				case "f":
					AddFace(array, list, list2, list3, list4, lookup, list7, list8, list9, outShades, triangles);
					break;
				}
			}
			for (int num = list6.Count - 1; num >= 0; num--)
			{
				if (list6[num].Count == 0)
				{
					list6.RemoveAt(num);
					list5.RemoveAt(num);
				}
			}
			if (list7.Count == 0 || list6.Count == 0)
			{
				KyndaPlugin.Log.LogError((object)("Model " + name + " has no geometry."));
				return null;
			}
			Mesh val = new Mesh
			{
				name = name
			};
			if (list7.Count > 65535)
			{
				val.indexFormat = (IndexFormat)1;
				KyndaPlugin.Log.LogInfo((object)(name + " needs 32 bit indices (" + list7.Count + " verts)."));
			}
			val.SetVertices(list7);
			if (list8.Count == list7.Count)
			{
				val.SetUVs(0, list8);
			}
			val.subMeshCount = list6.Count;
			for (int j = 0; j < list6.Count; j++)
			{
				val.SetTriangles(list6[j], j);
			}
			if (list9.Count == list7.Count)
			{
				val.SetNormals(list9);
			}
			else
			{
				val.RecalculateNormals();
			}
			Color[] array2 = (Color[])(object)new Color[list7.Count];
			for (int k = 0; k < array2.Length; k++)
			{
				array2[k] = Color.white;
			}
			val.colors = array2;
			val.RecalculateBounds();
			val.RecalculateTangents();
			int num2 = 0;
			for (int l = 0; l < list6.Count; l++)
			{
				num2 += list6[l].Count;
			}
			KyndaPlugin.Log.LogInfo((object)string.Format("Loaded {0}: {1} verts, {2} tris, parts [{3}]", name, list7.Count, num2 / 3, string.Join(", ", list5.ToArray())));
			return new ModelData
			{
				Mesh = val,
				Groups = list5.ToArray()
			};
		}

		private static List<int> Bucket(string material, List<string> names, List<List<int>> buckets, Dictionary<string, int> index)
		{
			if (index.TryGetValue(material, out var value))
			{
				return buckets[value];
			}
			index[material] = buckets.Count;
			names.Add(material);
			buckets.Add(new List<int>());
			return buckets[buckets.Count - 1];
		}

		private static void AddFace(string[] parts, List<Vector3> positions, List<Vector2> uvs, List<Vector3> normals, List<Color> shades, Dictionary<string, int> lookup, List<Vector3> outPositions, List<Vector2> outUvs, List<Vector3> outNormals, List<Color> outShades, List<int> triangles)
		{
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			//IL_0077: Unknown result type (might be due to invalid IL or missing references)
			//IL_006c: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a2: Unknown result type (might be due to invalid IL or missing references)
			//IL_0098: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c3: Unknown result type (might be due to invalid IL or missing references)
			List<int> list = new List<int>(parts.Length - 1);
			for (int i = 1; i < parts.Length; i++)
			{
				string text = parts[i];
				if (!lookup.TryGetValue(text, out var value))
				{
					string[] fields = text.Split(new char[1] { '/' });
					int num = Index(fields, 0, positions.Count);
					if (num < 0)
					{
						continue;
					}
					outPositions.Add(positions[num]);
					outShades.Add((num < shades.Count) ? shades[num] : Color.white);
					int num2 = Index(fields, 1, uvs.Count);
					outUvs.Add((num2 >= 0) ? uvs[num2] : Vector2.zero);
					int num3 = Index(fields, 2, normals.Count);
					outNormals.Add((num3 >= 0) ? normals[num3] : Vector3.up);
					value = (lookup[text] = outPositions.Count - 1);
				}
				list.Add(value);
			}
			for (int j = 2; j < list.Count; j++)
			{
				triangles.Add(list[0]);
				triangles.Add(list[j - 1]);
				triangles.Add(list[j]);
			}
		}

		private static int Index(string[] fields, int slot, int count)
		{
			if (slot >= fields.Length)
			{
				return -1;
			}
			string text = fields[slot];
			if (string.IsNullOrEmpty(text))
			{
				return -1;
			}
			if (!int.TryParse(text, NumberStyles.Integer, CultureInfo.InvariantCulture, out var result))
			{
				return -1;
			}
			if (result > 0)
			{
				result--;
			}
			else if (result < 0)
			{
				result = count + result;
			}
			if (result < 0 || result >= count)
			{
				return -1;
			}
			return result;
		}
	}
	internal static class PropIndex
	{
		private static Dictionary<string, GameObject> _index;

		public static GameObject Find(string name)
		{
			if ((Object)(object)ZNetScene.instance != (Object)null)
			{
				GameObject prefab = ZNetScene.instance.GetPrefab(name);
				if ((Object)(object)prefab != (Object)null)
				{
					return prefab;
				}
			}
			if (_index == null)
			{
				BuildIndex();
			}
			if (!_index.TryGetValue(name, out var value))
			{
				return null;
			}
			return value;
		}

		public static void Forget()
		{
			_index = null;
		}

		private static void BuildIndex()
		{
			//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_0071: Unknown result type (might be due to invalid IL or missing references)
			//IL_0076: Unknown result type (might be due to invalid IL or missing references)
			_index = new Dictionary<string, GameObject>(StringComparer.OrdinalIgnoreCase);
			GameObject[] array = Resources.FindObjectsOfTypeAll<GameObject>();
			foreach (GameObject val in array)
			{
				if ((Object)(object)val == (Object)null || (Object)(object)val.transform.parent != (Object)null || (Object)(object)val.GetComponentInChildren<MeshRenderer>(true) == (Object)null)
				{
					continue;
				}
				Scene scene = val.scene;
				bool flag = ((Scene)(ref scene)).IsValid();
				if (_index.TryGetValue(((Object)val).name, out var value))
				{
					if (flag)
					{
						continue;
					}
					scene = value.scene;
					if (!((Scene)(ref scene)).IsValid())
					{
						continue;
					}
				}
				_index[((Object)val).name] = val;
			}
			KyndaPlugin.Log.LogInfo((object)("Prop index built: " + _index.Count + " candidates with meshes."));
		}

		public static void Search(string keywords)
		{
			if (string.IsNullOrEmpty(keywords))
			{
				return;
			}
			if (_index == null)
			{
				BuildIndex();
			}
			string[] array = keywords.Split(new char[1] { ',' });
			for (int i = 0; i < array.Length; i++)
			{
				string text = array[i].Trim();
				if (text.Length == 0)
				{
					continue;
				}
				List<string> list = new List<string>();
				foreach (KeyValuePair<string, GameObject> item in _index)
				{
					if (item.Key.IndexOf(text, StringComparison.OrdinalIgnoreCase) >= 0)
					{
						string text2 = item.Key.ToLowerInvariant();
						if (!text2.Contains("destruction") && !text2.Contains("broken") && !text2.Contains("lod") && !text2.Contains("vfx") && !text2.Contains("sfx"))
						{
							list.Add(item.Key);
						}
					}
				}
				list.Sort();
				KyndaPlugin.Log.LogInfo((object)("Prefabs matching '" + text + "' (" + list.Count + "): " + string.Join(", ", list.GetRange(0, Math.Min(40, list.Count)).ToArray())));
			}
		}
	}
	internal static class ScenePatches
	{
		[HarmonyPostfix]
		[HarmonyPatch(typeof(ZNetScene), "Awake")]
		private static void OnSceneAwake()
		{
			int num = SmelterCapacity.AttachToPrefabs();
			if (num > 0)
			{
				KyndaPlugin.Log.LogInfo((object)("Capacity component added to " + num + " station prefab(s)."));
			}
			UpgradePrefabs.Register();
			PropIndex.Search(KyndaConfig.PrefabSearch.Value);
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(ObjectDB), "Awake")]
		private static void OnObjectDbAwake()
		{
			Skins.Invalidate();
			UpgradeBin.ForgetConnectionPrefab();
			UpgradePrefabs.Register();
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(ObjectDB), "CopyOtherDB")]
		private static void OnObjectDbCopy()
		{
			Skins.Invalidate();
			UpgradeBin.ForgetConnectionPrefab();
			UpgradePrefabs.Register();
		}
	}
	internal static class Skins
	{
		private sealed class LateSkin
		{
			public MeshRenderer Renderer;

			public Mesh Mesh;

			public Vector2[] OriginalUv;

			public string[] Groups;

			public IDictionary<string, string> Overrides;
		}

		private static readonly Dictionary<string, string[]> Donors = new Dictionary<string, string[]>(StringComparer.OrdinalIgnoreCase)
		{
			{
				"wood",
				new string[3] { "piece_chest_wood", "wood_wall_log", "darkwood_beam" }
			},
			{
				"frame",
				new string[3] { "wood_beam", "wood_wall_log", "wood_pole" }
			},
			{
				"iron",
				new string[3] { "piece_artisanstation", "forge", "piece_cauldron" }
			},
			{
				"stone",
				new string[3] { "stone_wall_2x1", "piece_stonecutter", "smelter" }
			},
			{
				"coal",
				new string[3] { "coal_pile", "Coal", "charcoal_kiln" }
			},
			{
				"ore",
				new string[3] { "CopperOre", "TinOre", "IronOre" }
			}
		};

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

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

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

		private const string EndSuffix = "_end";

		public const string Everything = "*";

		private static readonly HashSet<string> Contents = new HashSet<string>(StringComparer.OrdinalIgnoreCase) { "ore", "coal" };

		private static float _nextSummon;

		private static string _summonFault;

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

		private static readonly List<LateSkin> _late = new List<LateSkin>();

		private static float _nextLate;

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

		public static void Invalidate()
		{
			Cache.Clear();
			Atlas.Clear();
			TexPx.Clear();
			PropIndex.Forget();
		}

		private static string Named(string group, IDictionary<string, string> overrides)
		{
			if (overrides == null)
			{
				return null;
			}
			if (overrides.TryGetValue(group, out var value) && !string.IsNullOrEmpty(value))
			{
				return value;
			}
			string text = Base(group);
			if (overrides.TryGetValue(text, out value) && !string.IsNullOrEmpty(value))
			{
				return value;
			}
			if (Contents.Contains(text))
			{
				return null;
			}
			if (!overrides.TryGetValue("*", out value) || string.IsNullOrEmpty(value))
			{
				return null;
			}
			return value;
		}

		private static bool IsEnd(string group)
		{
			if (group != null && group.Length > "_end".Length)
			{
				return group.EndsWith("_end", StringComparison.OrdinalIgnoreCase);
			}
			return false;
		}

		private static string Base(string group)
		{
			if (!IsEnd(group))
			{
				return group;
			}
			return group.Substring(0, group.Length - "_end".Length);
		}

		private static string Key(string group, IDictionary<string, string> overrides)
		{
			string text = Named(group, overrides);
			if (!string.IsNullOrEmpty(text))
			{
				return group + "|" + text;
			}
			return group;
		}

		private static Material FindMaterial(string name)
		{
			if (string.IsNullOrEmpty(name))
			{
				return null;
			}
			Material val = null;
			Material[] array = Resources.FindObjectsOfTypeAll<Material>();
			foreach (Material val2 in array)
			{
				if ((Object)(object)val2 == (Object)null || (Object)(object)val2.shader == (Object)null)
				{
					continue;
				}
				if (((Object)val2).name == name)
				{
					return val2;
				}
				if (!((Object)(object)val != (Object)null))
				{
					if (string.Equals(((Object)val2).name, name, StringComparison.OrdinalIgnoreCase))
					{
						val = val2;
					}
					else if (((Object)val2).name.StartsWith(name, StringComparison.OrdinalIgnoreCase))
					{
						val = val2;
					}
				}
			}
			if ((Object)(object)val == (Object)null)
			{
				Material val3 = SoftAssets.LoadMaterial(name);
				if ((Object)(object)val3 != (Object)null)
				{
					return val3;
				}
				List<string> list = new List<string>();
				array = Resources.FindObjectsOfTypeAll<Material>();
				foreach (Material val4 in array)
				{
					if (!((Object)(object)val4 == (Object)null) && ((Object)val4).name != null && ((Object)val4).name.IndexOf(name.Split(new char[1] { '_' })[0], StringComparison.OrdinalIgnoreCase) >= 0)
					{
						if (!list.Contains(((Object)val4).name))
						{
							list.Add(((Object)val4).name);
						}
						if (list.Count >= 12)
						{
							break;
						}
					}
				}
				KyndaPlugin.Log.LogWarning((object)("No loaded material called '" + name + "'. It only exists once something using it has loaded. Loaded names sharing its prefix: " + ((list.Count == 0) ? "none" : string.Join(", ", list.ToArray()))));
				SummonDonorCarriers();
			}
			return val;
		}

		private static void SummonDonorCarriers()
		{
			try
			{
				SummonDonorCarriersInner();
			}
			catch (Exception ex)
			{
				if (_summonFault == null)
				{
					_summonFault = ex.ToString();
					KyndaPlugin.Log.LogError((object)("Carrier summon failed: " + _summonFault));
				}
			}
		}

		private static void SummonDonorCarriersInner()
		{
			if (Time.time < _nextSummon)
			{
				return;
			}
			ZoneSystem instance = ZoneSystem.instance;
			if ((Object)(object)instance == (Object)null || instance.m_locations == null)
			{
				return;
			}
			_nextSummon = Time.time + 30f;
			string[] array = (KyndaConfig.DonorCarrierLocations.Value ?? "").Split(new char[1] { ',' }, StringSplitOptions.RemoveEmptyEntries);
			if (array.Length == 0)
			{
				return;
			}
			int num = 0;
			int num2 = 0;
			foreach (ZoneLocation location in instance.m_locations)
			{
				if (location == null)
				{
					continue;
				}
				num++;
				string prefabName = location.m_prefabName;
				if (string.IsNullOrEmpty(prefabName))
				{
					continue;
				}
				string[] array2 = array;
				foreach (string text in array2)
				{
					if (prefabName.IndexOf(text.Trim(), StringComparison.OrdinalIgnoreCase) < 0)
					{
						continue;
					}
					num2++;
					try
					{
						if (location.m_prefab.IsValid && !location.m_prefab.IsLoaded && !location.m_prefab.IsLoading)
						{
							location.m_prefab.LoadAsync();
							KyndaPlugin.Log.LogInfo((object)("Summoned location asset '" + prefabName + "' for its donor materials."));
						}
					}
					catch (Exception)
					{
					}
					break;
				}
			}
			KyndaPlugin.Log.LogInfo((object)("Carrier summon pass: " + num + " locations, " + num2 + " matched."));
		}

		public static Material[] SkinAndWatch(MeshRenderer renderer, Mesh mesh, string[] groups, IDictionary<string, string> overrides)
		{
			Material[] array = Skin(groups, overrides);
			for (int i = 0; i < groups.Length; i++)
			{
				if (!((Object)(object)array[i] != (Object)null) && Key(groups[i], overrides).Contains("@"))
				{
					_late.Add(new LateSkin
					{
						Renderer = renderer,
						Mesh = mesh,
						OriginalUv = (((Object)(object)mesh != (Object)null) ? mesh.uv : null),
						Groups = groups,
						Overrides = overrides
					});
					KyndaPlugin.Log.LogInfo((object)"A skin donor is not loaded yet - it will be applied when its location streams in.");
					break;
				}
			}
			return array;
		}

		public static void Tick()
		{
			if (_late.Count == 0 || Time.realtimeSinceStartup < _nextLate)
			{
				return;
			}
			_nextLate = Time.realtimeSinceStartup + 5f;
			for (int num = _late.Count - 1; num >= 0; num--)
			{
				LateSkin lateSkin = _late[num];
				if ((Object)(object)lateSkin.Renderer == (Object)null || (Object)(object)lateSkin.Mesh == (Object)null)
				{
					_late.RemoveAt(num);
				}
				else
				{
					Material[] array = Skin(lateSkin.Groups, lateSkin.Overrides);
					bool flag = false;
					for (int i = 0; i < lateSkin.Groups.Length; i++)
					{
						if ((Object)(object)array[i] == (Object)null && Key(lateSkin.Groups[i], lateSkin.Overrides).Contains("@"))
						{
							flag = true;
						}
					}
					if (!flag)
					{
						if (lateSkin.OriginalUv != null)
						{
							lateSkin.Mesh.uv = lateSkin.OriginalUv;
						}
						((Renderer)lateSkin.Renderer).sharedMaterials = array;
						Remap(lateSkin.Mesh, lateSkin.Groups, lateSkin.Overrides);
						MeshRenderer[] array2 = Object.FindObjectsOfType<MeshRenderer>();
						foreach (MeshRenderer val in array2)
						{
							if (!((Object)(object)val == (Object)null) && !((Object)(object)val == (Object)(object)lateSkin.Renderer))
							{
								MeshFilter component = ((Component)val).GetComponent<MeshFilter>();
								if (!((Object)(object)component == (Object)null) && !((Object)(object)component.sharedMesh != (Object)(object)lateSkin.Mesh))
								{
									((Renderer)val).sharedMaterials = array;
								}
							}
						}
						_late.RemoveAt(num);
						KyndaPlugin.Log.LogInfo((object)"A late skin donor arrived and was applied, standing pieces included.");
					}
				}
			}
		}

		public static Material[] Skin(string[] groups, IDictionary<string, string> overrides)
		{
			Material[] array = (Material[])(object)new Material[groups.Length];
			for (int i = 0; i < groups.Length; i++)
			{
				array[i] = For(groups[i], overrides);
			}
			return array;
		}

		public unsafe static Material For(string group, IDictionary<string, string> overrides)
		{
			//IL_041a: Unknown result type (might be due to invalid IL or missing references)
			//IL_041c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0437: Unknown result type (might be due to invalid IL or missing references)
			//IL_0439: Unknown result type (might be due to invalid IL or missing references)
			//IL_042c: Unknown result type (might be due to invalid IL or missing references)
			//IL_042e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0433: Unknown result type (might be due to invalid IL or missing references)
			//IL_044e: Unknown result type (might be due to invalid IL or missing references)
			//IL_04b1: Unknown result type (might be due to invalid IL or missing references)
			//IL_02e4: Unknown result type (might be due to invalid IL or missing references)
			//IL_036c: Unknown result type (might be due to invalid IL or missing references)
			//IL_036e: Unknown result type (might be due to invalid IL or missing references)
			string text = Key(group, overrides);
			if (Cache.TryGetValue(text, out var value))
			{
				return value;
			}
			string text2 = Base(group);
			string text3 = Named(group, overrides);
			string[] value2;
			if (!string.IsNullOrEmpty(text3))
			{
				value2 = new string[1] { text3 };
			}
			else if (!Donors.TryGetValue(text2, out value2))
			{
				value2 = Donors["wood"];
			}
			string[] array = value2;
			Rect val = default(Rect);
			foreach (string text4 in array)
			{
				string text5 = text4.Trim();
				if (text5.StartsWith("@"))
				{
					string text6 = text5.Substring(1);
					((Rect)(ref val))..ctor(0f, 0f, 1f, 1f);
					if (text6.EndsWith(":keep", StringComparison.OrdinalIgnoreCase))
					{
						Material val2 = FindMaterial(text6.Substring(0, text6.Length - 5));
						if (!((Object)(object)val2 == (Object)null))
						{
							Texture texture = val2.GetTexture("_MainTex");
							if (!((Object)(object)texture == (Object)null))
							{
								Cache[text] = val2;
								Atlas.Remove(text);
								FlipV.Remove(text);
								TexPx[text] = Mathf.Max(1, texture.width);
								KyndaPlugin.Log.LogInfo((object)("'" + text + "' skinned with the material " + ((Object)val2).name + " found by name, vanilla UVs kept, " + texture.width + "px."));
								return val2;
							}
						}
						continue;
					}
					int num = text6.IndexOf(':');
					bool flag = false;
					if (num > 0)
					{
						string[] array2 = text6.Substring(num + 1).Split(new char[1] { '/' });
						if (array2.Length == 4 && array2[3].StartsWith("~"))
						{
							flag = true;
							array2[3] = array2[3].Substring(1);
						}
						if (array2.Length == 4 && float.TryParse(array2[0], NumberStyles.Float, CultureInfo.InvariantCulture, out var result) && float.TryParse(array2[1], NumberStyles.Float, CultureInfo.InvariantCulture, out var result2) && float.TryParse(array2[2], NumberStyles.Float, CultureInfo.InvariantCulture, out var result3) && float.TryParse(array2[3], NumberStyles.Float, CultureInfo.InvariantCulture, out var result4) && result3 > 0f && result4 > 0f)
						{
							((Rect)(ref val))..ctor(result, result2, result3, result4);
						}
						else
						{
							KyndaPlugin.Log.LogWarning((object)("Could not read the rect in '" + text5 + "' - expected @name:x/y/w/h. Using the whole sheet, which will look like every tile at once."));
						}
						text6 = text6.Substring(0, num);
					}
					Material val3 = FindMaterial(text6);
					if ((Object)(object)val3 == (Object)null)
					{
						continue;
					}
					Texture texture2 = val3.GetTexture("_MainTex");
					if (!((Object)(object)texture2 == (Object)null))
					{
						Cache[text] = val3;
						Atlas[text] = val;
						TexPx[text] = Mathf.Max(1, texture2.width);
						if (flag)
						{
							FlipV.Add(text);
						}
						else
						{
							FlipV.Remove(text);
						}
						ManualLogSource log = KyndaPlugin.Log;
						string[] obj = new string[12]
						{
							"'",
							text,
							"' skinned with the material ",
							((Object)val3).name,
							" found by name (shader ",
							((Object)val3.shader).name,
							"), rect ",
							null,
							null,
							null,
							null,
							null
						};
						Rect val4 = val;
						obj[7] = ((object)(*(Rect*)(&val4))/*cast due to .constrained prefix*/).ToString();
						obj[8] = (flag ? ", V flipped" : "");
						obj[9] = ", ";
						obj[10] = texture2.width.ToString();
						obj[11] = "px.";
						log.LogInfo((object)string.Concat(obj));
						return val3;
					}
					continue;
				}
				GameObject val5 = PropIndex.Find(text5);
				if (!((Object)(object)val5 == (Object)null))
				{
					MeshRenderer val6 = MainRenderer(val5);
					if ((Object)(object)val6 != (Object)null)
					{
						Material sharedMaterial = ((Renderer)val6).sharedMaterial;
						Texture texture3 = sharedMaterial.GetTexture("_MainTex");
						Regions((Renderer)(object)val6, out var side, out var cap);
						Rect value3 = (IsEnd(group) ? cap : ((!IsMetal(text2)) ? side : MetalRegion(texture3, text4, side)));
						Cache[text] = sharedMaterial;
						Atlas[text] = value3;
						TexPx[text] = Mathf.Max(1, texture3.width);
						KyndaPlugin.Log.LogInfo((object)$"'{text}' skinned with {((Object)sharedMaterial).name} from {text4}/{((Object)val6).name} (shader {((Object)sharedMaterial.shader).name}), atlas {Atlas[text]}, {TexPx[text]}px.");
						DumpShader(sharedMaterial);
						return sharedMaterial;
					}
				}
			}
			KyndaPlugin.Log.LogWarning((object)("No material found for group '" + text + "'."));
			if (!text.Contains("@"))
			{
				Cache[text] = null;
			}
			return null;
		}

		private unsafe static void DumpShader(Material material)
		{
			//IL_009c: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00aa: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ad: Invalid comparison between Unknown and I4
			//IL_0120: Unknown result type (might be due to invalid IL or missing references)
			//IL_0148: Unknown result type (might be due to invalid IL or missing references)
			//IL_014b: Invalid comparison between Unknown and I4
			//IL_012b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0130: Unknown result type (might be due to invalid IL or missing references)
			//IL_014d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0150: Invalid comparison between Unknown and I4
			if (!KyndaConfig.DumpShader.Value || (Object)(object)material == (Object)null || (Object)(object)material.shader == (Object)null || !_dumped.Add(((Object)material.shader).name))
			{
				return;
			}
			Shader shader = material.shader;
			int propertyCount = shader.GetPropertyCount();
			KyndaPlugin.Log.LogInfo((object)("SHADER " + ((Object)shader).name + ": " + propertyCount + " properties."));
			for (int i = 0; i < propertyCount; i++)
			{
				string propertyName = shader.GetPropertyName(i);
				ShaderPropertyType propertyType = shader.GetPropertyType(i);
				string text = "";
				if ((int)propertyType == 4)
				{
					Texture texture = material.GetTexture(propertyName);
					text = (((Object)(object)texture == (Object)null) ? "  (unset)" : ("  = " + ((Object)texture).name + " " + texture.width + "x" + texture.height));
				}
				else if ((int)propertyType == 0)
				{
					text = "  = " + ((object)material.GetColor(propertyName)/*cast due to .constrained prefix*/).ToString();
				}
				else if ((int)propertyType == 2 || (int)propertyType == 3)
				{
					text = "  = " + material.GetFloat(propertyName);
				}
				KyndaPlugin.Log.LogInfo((object)("    " + propertyName + " (" + ((object)(*(ShaderPropertyType*)(&propertyType))/*cast due to .constrained prefix*/).ToString() + ")" + text));
			}
		}

		private static List<List<int>> Islands(int[] indices, Vector2[] uv)
		{
			Dictionary<int, int> dictionary = new Dictionary<int, int>();
			foreach (int num in indices)
			{
				if (num >= 0 && num < uv.Length && !dictionary.ContainsKey(num))
				{
					dictionary[num] = num;
				}
			}
			for (int j = 0; j + 2 < indices.Length; j += 3)
			{
				int num2 = indices[j];
				int num3 = indices[j + 1];
				int num4 = indices[j + 2];
				if (dictionary.ContainsKey(num2) && dictionary.ContainsKey(num3) && dictionary.ContainsKey(num4))
				{
					Join(dictionary, num2, num3);
					Join(dictionary, num2, num4);
				}
			}
			List<int> list = new List<int>(dictionary.Keys);
			Dictionary<int, List<int>> dictionary2 = new Dictionary<int, List<int>>();
			foreach (int item in list)
			{
				int key = Find(dictionary, item);
				if (!dictionary2.TryGetValue(key, out var value))
				{
					value = (dictionary2[key] = new List<int>());
				}
				value.Add(item);
			}
			return new List<List<int>>(dictionary2.Values);
		}

		private static int Find(Dictionary<int, int> parent, int v)
		{
			while (parent[v] != v)
			{
				parent[v] = parent[parent[v]];
				v = parent[v];
			}
			return v;
		}

		private static void Join(Dictionary<int, int> parent, int a, int b)
		{
			int num = Find(parent, a);
			int num2 = Find(parent, b);
			if (num != num2)
			{
				parent[num] = num2;
			}
		}

		private static Rect Cluster(List<Rect> rects)
		{
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_0169: Unknown result type (might be due to invalid IL or missing references)
			//IL_016a: 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_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_005c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0178: Unknown result type (might be due to invalid IL or missing references)
			//IL_017d: Unknown result type (might be due to invalid IL or missing references)
			//IL_019f: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a1: Unknown result type (might be due to invalid IL or missing references)
			//IL_01bc: Unknown result type (might be due to invalid IL or missing references)
			//IL_011c: Unknown result type (might be due to invalid IL or missing references)
			Rect val = default(Rect);
			((Rect)(ref val))..ctor(0f, 0f, 0f, 0f);
			if (rects == null || rects.Count == 0)
			{
				return val;
			}
			List<Rect> list = new List<Rect>(rects);
			int num = 0;
			bool flag;
			do
			{
				flag = false;
				num++;
				for (int i = 0; i < list.Count; i++)
				{
					if (flag)
					{
						break;
					}
					for (int j = i + 1; j < list.Count; j++)
					{
						Rect val2 = list[i];
						Rect val3 = list[j];
						if (!(((Rect)(ref val2)).xMin > ((Rect)(ref val3)).xMax + 1f / 128f) && !(((Rect)(ref val3)).xMin > ((Rect)(ref val2)).xMax + 1f / 128f) && !(((Rect)(ref val2)).yMin > ((Rect)(ref val3)).yMax + 1f / 128f) && !(((Rect)(ref val3)).yMin > ((Rect)(ref val2)).yMax + 1f / 128f))
						{
							float num2 = Mathf.Min(((Rect)(ref val2)).xMin, ((Rect)(ref val3)).xMin);
							float num3 = Mathf.Min(((Rect)(ref val2)).yMin, ((Rect)(ref val3)).yMin);
							list[i] = new Rect(num2, num3, Mathf.Max(((Rect)(ref val2)).xMax, ((Rect)(ref val3)).xMax) - num2, Mathf.Max(((Rect)(ref val2)).yMax, ((Rect)(ref val3)).yMax) - num3);
							list.RemoveAt(j);
							flag = true;
							break;
						}
					}
				}
			}
			while (flag && num < 4096);
			Rect result = val;
			foreach (Rect item in list)
			{
				Rect current = item;
				if (((Rect)(ref current)).width * ((Rect)(ref current)).height > ((Rect)(ref result)).width * ((Rect)(ref result)).height)
				{
					result = current;
				}
			}
			return result;
		}

		private static MeshRenderer MainRenderer(GameObject donor)
		{
			MeshRenderer result = null;
			int num = 0;
			MeshRenderer[] componentsInChildren = donor.GetComponentsInChildren<MeshRenderer>(true);
			foreach (MeshRenderer val in componentsInChildren)
			{
				Material sharedMaterial = ((Renderer)val).sharedMaterial;
				if ((Object)(object)sharedMaterial == (Object)null || (Object)(object)sharedMaterial.shader == (Object)null || !sharedMaterial.HasProperty("_MainTex") || (Object)(object)sharedMaterial.GetTexture("_MainTex") == (Object)null)
				{
					continue;
				}
				MeshFilter component = ((Component)val).GetComponent<MeshFilter>();
				Mesh val2 = (((Object)(object)component != (Object)null) ? component.sharedMesh : null);
				if ((Object)(object)val2 == (Object)null)
				{
					continue;
				}
				int num2;
				try
				{
					if (!val2.isReadable)
					{
						continue;
					}
					num2 = val2.triangles.Length;
					goto IL_00a4;
				}
				catch
				{
				}
				continue;
				IL_00a4:
				if (num2 > num)
				{
					num = num2;
					result = val;
				}
			}
			return result;
		}

		private static bool IsMetal(string family)
		{
			return string.Equals(family, "iron", StringComparison.OrdinalIgnoreCase);
		}

		private static Rect MetalRegion(Texture sheet, string donor, Rect fallback)
		{
			//IL_00a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a9: Unknown result type (might be due to invalid IL or missing references)
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			//IL_029e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: Expected O, but got Unknown
			//IL_0068: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e3: Unknown result type (might be due to invalid IL or missing references)
			//IL_015d: Unknown result type (might be due to invalid IL or missing references)
			//IL_015e: Unknown result type (might be due to invalid IL or missing references)
			//IL_010f: Unknown result type (might be due to invalid IL or missing references)
			//IL_029b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0188: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)sheet == (Object)null)
			{
				return fallback;
			}
			int num = Mathf.Clamp(sheet.width, 8, 64);
			int num2 = Mathf.Clamp(sheet.height, 8, 64);
			RenderTexture val = null;
			RenderTexture active = RenderTexture.active;
			Texture2D val2 = null;
			Color[] pixels;
			try
			{
				val = RenderTexture.GetTemporary(num, num2, 0, (RenderTextureFormat)0, (RenderTextureReadWrite)2);
				Graphics.Blit(sheet, val);
				RenderTexture.active = val;
				val2 = new Texture2D(num, num2, (TextureFormat)5, false);
				val2.ReadPixels(new Rect(0f, 0f, (float)num, (float)num2), 0, 0);
				val2.Apply();
				pixels = val2.GetPixels();
			}
			catch (Exception ex)
			{
				KyndaPlugin.Log.LogWarning((object)("Could not read " + donor + "'s sheet to find its metal: " + ex.Message));
				return fallback;
			}
			finally
			{
				RenderTexture.active = active;
				if ((Object)(object)val != (Object)null)
				{
					RenderTexture.ReleaseTemporary(val);
				}
				if ((Object)(object)val2 != (Object)null)
				{
					Object.Destroy((Object)(object)val2);
				}
			}
			if (pixels == null || pixels.Length < num * num2)
			{
				return fallback;
			}
			float[] array = new float[num];
			float num3 = 0f;
			for (int i = 0; i < num; i++)
			{
				float num4 = 0f;
				for (int j = 0; j < num2; j++)
				{
					num4 += Saturation(pixels[j * num + i]);
				}
				array[i] = num4 / (float)num2;
				num3 += array[i];
			}
			num3 /= (float)num;
			if (!Run(array, num3, out var from, out var to))
			{
				return Grey(donor, fallback);
			}
			float[] array2 = new float[num2];
			for (int k = 0; k < num2; k++)
			{
				float num5 = 0f;
				for (int l = from; l <= to; l++)
				{
					num5 += Saturation(pixels[k * num + l]);
				}
				array2[k] = num5 / (float)(to - from + 1);
			}
			if (!Run(array2, num3, out var from2, out var to2))
			{
				from2 = 0;
				to2 = num2 - 1;
			}
			Rect result = default(Rect);
			((Rect)(ref result))..ctor(((float)from + 1f) / (float)num, ((float)from2 + 1f) / (float)num2, Mathf.Max(1f, (float)(to - from) - 1f) / (float)num, Mathf.Max(1f, (float)(to2 - from2) - 1f) / (float)num2);
			KyndaPlugin.Log.LogInfo((object)$"{donor}'s metal is the {to - from + 1}x{to2 - from2 + 1} px block at {((Rect)(ref result)).x:0.000},{((Rect)(ref result)).y:0.000} - saturation {Mean(array, from, to):0.00} against {num3:0.00} across the sheet.");
			return result;
		}

		private static bool Run(float[] slices, float overall, out int from, out int to)
		{
			from = 0;
			to = -1;
			float num = Mathf.Min(overall * 0.5f, 0.18f);
			int num2 = 0;
			int num3 = -1;
			int num4 = -1;
			for (int i = 0; i < slices.Length; i++)
			{
				if (slices[i] <= num)
				{
					if (num4 < 0)
					{
						num4 = i;
					}
					if (i - num4 > num3 - num2)
					{
						num2 = num4;
						num3 = i;
					}
				}
				else
				{
					num4 = -1;
				}
			}
			if (num3 - num2 < 2)
			{
				return false;
			}
			from = num2;
			to = num3;
			return true;
		}

		private static float Saturation(Color c)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			float num = Mathf.Max(c.r, Mathf.Max(c.g, c.b));
			float num2 = Mathf.Min(c.r, Mathf.Min(c.g, c.b));
			if (!(num <= 0.0001f))
			{
				return (num - num2) / num;
			}
			return 0f;
		}

		private static float Mean(float[] slices, int from, int to)
		{
			float num = 0f;
			for (int i = from; i <= to; i++)
			{
				num += slices[i];
			}
			return num / (float)Mathf.Max(1, to - from + 1);
		}

		private static Rect Grey(string donor, Rect fallback)
		{
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			KyndaPlugin.Log.LogInfo((object)(donor + " has no unsaturated block - it is all one substance, so metal parts will wear the same surface as the rest of it."));
			return fallback;
		}

		private static void Regions(Renderer renderer, out Rect side, out Rect cap)
		{
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_0361: Unknown result type (might be due to invalid IL or missing references)
			//IL_0366: Unknown result type (might be due to invalid IL or missing references)
			//IL_0371: Unknown result type (might be due to invalid IL or missing references)
			//IL_0376: Unknown result type (might be due to invalid IL or missing references)
			//IL_0381: Unknown result type (might be due to invalid IL or missing references)
			//IL_0386: Unknown result type (might be due to invalid IL or missing references)
			//IL_0390: Unknown result type (might be due to invalid IL or missing references)
			//IL_0395: Unknown result type (might be due to invalid IL or missing references)
			//IL_03a4: Unknown result type (might be due to invalid IL or missing references)
			//IL_03a9: Unknown result type (might be due to invalid IL or missing references)
			//IL_012e: 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_013b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0143: Unknown result type (might be due to invalid IL or missing references)
			//IL_014b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0150: Unknown result type (might be due to invalid IL or missing references)
			//IL_0155: Unknown result type (might be due to invalid IL or missing references)
			//IL_015a: Unknown result type (might be due to invalid IL or missing references)
			//IL_03cb: Unknown result type (might be due to invalid IL or missing references)
			//IL_03cd: Unknown result type (might be due to invalid IL or missing references)
			//IL_04e3: Unknown result type (might be due to invalid IL or missing references)
			//IL_04e8: Unknown result type (might be due to invalid IL or missing references)
			//IL_03ec: Unknown result type (might be due to invalid IL or missing references)
			//IL_03ee: Unknown result type (might be due to invalid IL or missing references)
			//IL_0171: Unknown result type (might be due to invalid IL or missing references)
			//IL_0175: Unknown result type (might be due to invalid IL or missing references)
			//IL_017a: Unknown result type (might be due to invalid IL or missing references)
			//IL_017c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0188: Unknown result type (might be due to invalid IL or missing references)
			//IL_051b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0520: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_01bc: Unknown result type (might be due to invalid IL or missing references)
			//IL_0196: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a2: Unknown result type (might be due to invalid IL or missing references)
			//IL_0501: Unknown result type (might be due to invalid IL or missing references)
			//IL_0504: Unknown result type (might be due to invalid IL or missing references)
			//IL_0511: Unknown result type (might be due to invalid IL or missing references)
			//IL_0513: Unknown result type (might be due to invalid IL or missing references)
			//IL_02fb: Unknown result type (might be due to invalid IL or missing references)
			Rect val = default(Rect);
			((Rect)(ref val))..ctor(0f, 0f, 1f, 1f);
			side = val;
			cap = val;
			MeshFilter val2 = (((Object)(object)renderer != (Object)null) ? ((Component)renderer).GetComponent<MeshFilter>() : null);
			Mesh val3 = (((Object)(object)val2 != (Object)null) ? val2.sharedMesh : null);
			if ((Object)(object)val3 == (Object)null)
			{
				return;
			}
			Vector3[] vertices;
			Vector2[] uv;
			int[] triangles;
			try
			{
				if (!val3.isReadable)
				{
					return;
				}
				vertices = val3.vertices;
				uv = val3.uv;
				triangles = val3.triangles;
			}
			catch
			{
				return;
			}
			if (uv == null || uv.Length == 0 || triangles == null || triangles.Length < 3 || vertices == null || vertices.Length == 0)
			{
				return;
			}
			float[] array = new float[3];
			List<Rect>[] array2 = new List<Rect>[3]
			{
				new List<Rect>(),
				new List<Rect>(),
				new List<Rect>()
			};
			for (int i = 0; i + 2 < triangles.Length; i += 3)
			{
				int num = triangles[i];
				int num2 = triangles[i + 1];
				int num3 = triangles[i + 2];
				if (num >= uv.Length || num2 >= uv.Length || num3 >= uv.Length || num >= vertices.Length || num2 >= vertices.Length || num3 >= vertices.Length)
				{
					continue;
				}
				Vector3 val4 = Vector3.Cross(vertices[num2] - vertices[num], vertices[num3] - vertices[num]);
				float magnitude = ((Vector3)(ref val4)).magnitude;
				if (!(magnitude <= 1E-09f))
				{
					Vector3 val5 = val4 / magnitude;
					int num4 = ((!(Mathf.Abs(val5.x) >= Mathf.Abs(val5.y)) || !(Mathf.Abs(val5.x) >= Mathf.Abs(val5.z))) ? ((Mathf.Abs(val5.y) >= Mathf.Abs(val5.z)) ? 1 : 2) : 0);
					array[num4] += magnitude * 0.5f;
					float num5 = Mathf.Min(uv[num].x, Mathf.Min(uv[num2].x, uv[num3].x));
					float num6 = Mathf.Max(uv[num].x, Mathf.Max(uv[num2].x, uv[num3].x));
					float num7 = Mathf.Min(uv[num].y, Mathf.Min(uv[num2].y, uv[num3].y));
					float num8 = Mathf.Max(uv[num].y, Mathf.Max(uv[num2].y, uv[num3].y));
					float num9 = num6 - num5;
					float num10 = num8 - num7;
					if (!(num9 <= 0.005f) && !(num10 <= 0.005f) && !(num9 > 1f) && !(num10 > 1f))
					{
						array2[num4].Add(new Rect(num5, num7, num9, num10));
					}
				}
			}
			float num11 = array[0] + array[1] + array[2];
			if (num11 <= 0f)
			{
				return;
			}
			int num12 = 0;
			for (int j = 1; j < 3; j++)
			{
				if (array[j] < array[num12])
				{
					num12 = j;
				}
			}
			Rect[] array3 = (Rect[])(object)new Rect[3]
			{
				Cluster(array2[0]),
				Cluster(array2[1]),
				Cluster(array2[2])
			};
			Rect val6 = array3[0];
			Rect[] array4 = array3;
			for (int k = 0; k < array4.Length; k++)
			{
				Rect val7 = array4[k];
				if (((Rect)(ref val7)).width * ((Rect)(ref val7)).height > ((Rect)(ref val6)).width * ((Rect)(ref val6)).height)
				{
					val6 = val7;
				}
			}
			if (((Rect)(ref val6)).width > 0f)
			{
				side = val6;
			}
			KyndaPlugin.Log.LogInfo((object)$"{((Object)renderer).name}: surface {array[0] / num11 * 100f:0}/{array[1] / num11 * 100f:0}/{array[2] / num11 * 100f:0}% by axis, fields {((Rect)(ref array3[0])).width:0.000}x{((Rect)(ref array3[0])).height:0.000} / {((Rect)(ref array3[1])).width:0.000}x{((Rect)(ref array3[1])).height:0.000} / {((Rect)(ref array3[2])).width:0.000}x{((Rect)(ref array3[2])).height:0.000}.");
			float num13 = array[num12] / num11;
			Rect val8 = array3[num12];
			if (num13 <= 0.3f && ((Rect)(ref val8)).width > 0f && val8 != side)
			{
				cap = val8;
				return;
			}
			cap = side;
			KyndaPlugin.Log.LogInfo((object)$"{((Object)renderer).name} has no separate end patch - its thinnest axis is {num13 * 100f:0}% of its surface. Sawn ends will use the side grain.");
		}

		public static void Remap(Mesh mesh, string[] groups, IDictionary<string, string> overrides)
		{
			//IL_0230: Unknown result type (might be due to invalid IL or missing references)
			//IL_0235: Unknown result type (might be due to invalid IL or missing references)
			//IL_023a: Unknown result type (might be due to invalid IL or missing references)
			//IL_023f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0241: Unknown result type (might be due to invalid IL or missing references)
			//IL_0246: Unknown result type (might be due to invalid IL or missing references)
			//IL_024b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0250: Unknown result type (might be due to invalid IL or missing references)
			//IL_0138: Unknown result type (might be due to invalid IL or missing references)
			//IL_013d: Unknown result type (might be due to invalid IL or missing references)
			//IL_026b: Unknown result type (might be due to invalid IL or missing references)
			//IL_026d: Unknown result type (might be due to invalid IL or missing references)
			//IL_026f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0274: Unknown result type (might be due to invalid IL or missing references)
			//IL_0276: Unknown result type (might be due to invalid IL or missing references)
			//IL_0287: Unknown result type (might be due to invalid IL or missing references)
			//IL_02a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_02c0: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ed: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ef: Unknown result type (might be due to invalid IL or missing references)
			//IL_02f1: Unknown result type (might be due to invalid IL or missing references)
			//IL_02fb: Unknown result type (might be due to invalid IL or missing references)
			//IL_0300: Unknown result type (might be due to invalid IL or missing references)
			//IL_032c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0331: Unknown result type (might be due to invalid IL or missing references)
			//IL_0335: Unknown result type (might be due to invalid IL or missing references)
			//IL_033a: Unknown result type (might be due to invalid IL or missing references)
			//IL_033f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0379: Unknown result type (might be due to invalid IL or missing references)
			//IL_03e6: Unknown result type (might be due to invalid IL or missing references)
			//IL_03b2: Unknown result type (might be due to invalid IL or missing references)
			//IL_040a: Unknown result type (might be due to invalid IL or missing references)
			//IL_040f: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)mesh == (Object)null || groups == null)
			{
				return;
			}
			Vector2[] uv = mesh.uv;
			if (uv == null || uv.Length == 0)
			{
				return;
			}
			int num = Mathf.Min(groups.Length, mesh.subMeshCount);
			float num2 = Mathf.Max(1f, KyndaConfig.TexelsPerMetre.Value);
			bool[] array = new bool[uv.Length];
			Vector2 val = default(Vector2);
			Vector2 val2 = default(Vector2);
			for (int i = 0; i < num; i++)
			{
				string text = Key(groups[i], overrides);
				if (!Atlas.TryGetValue(text, out var value) || !TexPx.TryGetValue(text, out var value2) || value2 <= 0)
				{
					continue;
				}
				int[] triangles = mesh.GetTriangles(i);
				if (triangles.Length == 0)
				{
					continue;
				}
				if (IsEnd(groups[i]))
				{
					int[] array2 = triangles;
					foreach (int num3 in array2)
					{
						if (num3 >= 0 && num3 < uv.Length && !array[num3])
						{
							array[num3] = true;
							float num4 = Mathf.Clamp01(uv[num3].y);
							if (FlipV.Contains(text))
							{
								num4 = 1f - num4;
							}
							uv[num3] = new Vector2(((Rect)(ref value)).x + Mathf.Clamp01(uv[num3].x) * ((Rect)(ref value)).width, ((Rect)(ref value)).y + num4 * ((Rect)(ref value)).height);
						}
					}
					KyndaPlugin.Log.LogInfo((object)$"'{text}' aimed at the end-grain patch at {((Rect)(ref value)).x:0.000},{((Rect)(ref value)).y:0.000} {((Rect)(ref value)).width:0.000}x{((Rect)(ref value)).height:0.000} ({Mathf.RoundToInt(((Rect)(ref value)).width * (float)value2)} texels across).");
					continue;
				}
				List<List<int>> list = Islands(triangles, uv);
				float num5 = float.MaxValue;
				float num6 = 0f;
				foreach (List<int> item in list)
				{
					((Vector2)(ref val))..ctor(float.MaxValue, float.MaxValue);
					((Vector2)(ref val2))..ctor(float.MinValue, float.MinValue);
					foreach (int item2 in item)
					{
						val = Vector2.Min(val, uv[item2]);
						val2 = Vector2.Max(val2, uv[item2]);
					}
					Vector2 val3 = val2 - val;
					if (val3.x <= 0f || val3.y <= 0f)
					{
						continue;
					}
					float num7 = num2 / (float)value2;
					num7 = Mathf.Min(num7, ((Rect)(ref value)).width / val3.x);
					num7 = Mathf.Min(num7, ((Rect)(ref value)).height / val3.y);
					num5 = Mathf.Min(num5, num7 * (float)value2);
					num6 = Mathf.Max(num6, num7 * (float)value2);
					Vector2 val4 = (val + val2) * 0.5f;
					Vector2 val5 = new Vector2(((Rect)(ref value)).x + ((Rect)(ref value)).width * 0.5f, ((Rect)(ref value)).y + ((Rect)(ref value)).height * 0.5f) - val4 * num7;
					foreach (int item3 in item)
					{
						if (!array[item3])
						{
							array[item3] = true;
							uv[item3] = new Vector2(Mathf.Clamp(uv[item3].x * num7 + val5.x, ((Rect)(ref value)).xMin, ((Rect)(ref value)).xMax), FlipV.Contains(text) ? (((Rect)(ref value)).yMax - (Mathf.Clamp(uv[item3].y * num7 + val5.y, ((Rect)(ref value)).yMin, ((Rect)(ref value)).yMax) - ((Rect)(ref value)).yMin)) : Mathf.Clamp(uv[item3].y * num7 + val5.y, ((Rect)(ref value)).yMin, ((Rect)(ref value)).yMax));
						}
					}
				}
				if (!(num6 <= 0f))
				{
					KyndaPlugin.Log.LogInfo((object)$"'{text}' laid out at {num5:0}-{num6:0} texels/m (wanted {num2:0}) across {list.Count} parts, in a {((Rect)(ref value)).width:0.000}x{((Rect)(ref value)).height:0.000} rect.");
				}
			}
			mesh.uv = uv;
		}
	}
	internal class SmelterCapacity : MonoBehaviour
	{
		private static readonly List<SmelterCapacity> All = new List<SmelterCapacity>();

		private Smelter _smelter;

		private int _baseOre;

		private int _baseFuel;

		private bool Fuelled => _baseFuel > 0;

		public Vector3 ConnectionPoint
		{
			get
			{
				//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_002f: 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_0019: Unknown result type (might be due to invalid IL or missing references)
				Collider componentInChildren = ((Component)this).GetComponentInChildren<Collider>();
				if ((Object)(object)componentInChildren != (Object)null)
				{
					Bounds bounds = componentInChildren.bounds;
					return ((Bounds)(ref bounds)).center;
				}
				return ((Component)this).transform.position + Vector3.up;
			}
		}

		private void Awake()
		{
			_smelter = ((Component)this).GetComponent<Smelter>();
			if ((Object)(object)_smelter == (Object)null)
			{
				((Behaviour)this).enabled = false;
				return;
			}
			_baseOre = _smelter.m_maxOre;
			_baseFuel = _smelter.m_maxFuel;
			All.Add(this);
			((MonoBehaviour)this).InvokeRepeating("Recompute", 1f, 3f);
		}

		private void OnDestroy()
		{
			All.Remove(this);
		}

		private bool Serves(UpgradeDef def)
		{
			if (def == null || def.Stations == null)
			{
				return false;
			}
			string prefabName = Utils.GetPrefabName(((Component)this).gameObject);
			string[] array = (def.Stations.Value ?? "").Split(new char[1] { ',' });
			for (int i = 0; i < array.Length; i++)
			{
				if (string.Equals(array[i].Trim(), prefabName, StringComparison.OrdinalIgnoreCase))
				{
					return true;
				}
			}
			return false;
		}

		private void Recompute()
		{
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)_smelter == (Object)null))
			{
				UpgradeDef upgradeDef = UpgradePrefabs.For(Fuelled);
				int num = ((KyndaConfig.Enabled.Value && Serves(upgradeDef)) ? Mathf.Min(UpgradeBin.CountNear(((Component)this).transform.position, Fuelled), Mathf.Max(0, KyndaConfig.MaxPerStation.Value)) : 0);
				int num2 = num * Mathf.Max(0, upgradeDef.OreCapacity.Value);
				int num3 = ((upgradeDef.FuelCapacity != null) ? (num * Mathf.Max(0, upgradeDef.FuelCapacity.Value)) : 0);
				if (_baseOre > 0)
				{
					_smelter.m_maxOre = _baseOre + num2;
				}
				if (_baseFuel > 0)
				{
					_smelter.m_maxFuel = _baseFuel + num3;
				}
			}
		}

		public static SmelterCapacity Nearest(Vector3 point, bool fuelled)
		{
			//IL_0062: Unknown result type (might be due to invalid IL or missing references)
			//IL_0067: Unknown result type (might be due to invalid IL or missing references)
			float value = KyndaConfig.Range.Value;
			SmelterCapacity result = null;
			float num = float.MaxValue;
			foreach (SmelterCapacity item in All)
			{
				if (!((Object)(object)item == (Object)null) && !((Object)(object)item._smelter == (Object)null) && item.Fuelled == fuelled && item.Serves(UpgradePrefabs.For(fuelled)))
				{
					float num2 = Vector3.Distance(((Component)item).transform.position, point);
					if (!(num2 > value) && !(num2 >= num))
					{
						result = item;
						num = num2;
					}
				}
			}
			return result;
		}

		public static string NearestUsing(Vector3 point, bool fuelled)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			SmelterCapacity smelterCapacity = Nearest(point, fuelled);
			if ((Object)(object)smelterCapacity == (Object)null)
			{
				return null;
			}
			Smelter smelter = smelterCapacity._smelter;
			List<string> list = new List<string>();
			if (smelter.m_maxOre > 0)
			{
				list.Add("ore " + smelter.m_maxOre);
			}
			if (smelter.m_maxFuel > 0)
			{
				list.Add("fuel " + smelter.m_maxFuel);
			}
			return smelter.m_name + " (" + string.Join(", ", list.ToArray()) + ")";
		}

		public static int AttachToPrefabs()
		{
			ZNetScene instance = ZNetScene.instance;
			if ((Object)(object)instance == (Object)null)
			{
				return 0;
			}
			int num = 0;
			foreach (GameObject prefab in instance.m_prefabs)
			{
				if (!((Object)(object)prefab == (Object)null) && !((Object)(object)prefab.GetComponent<Smelter>() == (Object)null) && !((Object)(object)prefab.GetComponent<SmelterCapacity>() != (Object)null))
				{
					prefab.AddComponent<SmelterCapacity>();
					num++;
				}
			}
			return num;
		}
	}
	internal static class SoftAssets
	{
		private static Dictionary<string, AssetID> _paths;

		private static bool _pathsFailed;

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

		public static void MakeEverythingLoadable()
		{
			try
			{
				Runtime.MakeAllAssetsLoadable();
			}
			catch (Exception ex)
			{
				KyndaPlugin.Log.LogWarning((object)("Could not enable the extended asset manifest: " + ex.Message + ". Location-only materials will fall back to summoning their carrier."));
			}
		}

		public unsafe static Material LoadMaterial(string name)
		{
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: Unknown result type (might be