Decompiled source of HowToNemo v1.0.0

BepInEx/plugins/HowToNemo/HowToNemo.dll

Decompiled 7 hours ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using UnityEngine;
using UnityEngine.Rendering;

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

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace HowToNemo
{
	internal static class NemoModel
	{
		internal struct Fit
		{
			public Quaternion Rot;

			public float Scale;

			public Vector3 Centre;

			public string Describe;
		}

		private const string MeshResource = "nemo_mesh.bin";

		private const string AtlasResource = "nemo_atlas.png";

		private static Vector3[] _pos;

		private static Vector3[] _nrm;

		private static Vector2[] _uv;

		private static int[] _tri;

		private static Texture2D _atlas;

		private static bool _tried;

		private static bool _ok;

		internal static Texture2D Atlas => _atlas;

		internal static bool Load()
		{
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: Expected O, but got Unknown
			if (_tried)
			{
				return _ok;
			}
			_tried = true;
			try
			{
				ReadMesh(GetResource("nemo_mesh.bin"));
				byte[] resource = GetResource("nemo_atlas.png");
				_atlas = new Texture2D(2, 2, (TextureFormat)4, true, false);
				if (!ImageConversion.LoadImage(_atlas, resource))
				{
					throw new Exception("atlas PNG failed to decode");
				}
				((Object)_atlas).name = "NemoAtlas";
				((Texture)_atlas).wrapMode = (TextureWrapMode)1;
				((Texture)_atlas).filterMode = (FilterMode)1;
				_atlas.Apply(true, false);
				_ok = true;
				Plugin.Log.LogInfo((object)$"Model loaded: {_pos.Length} verts, {_tri.Length / 3} tris, atlas {((Texture)_atlas).width}x{((Texture)_atlas).height}.");
			}
			catch (Exception ex)
			{
				Plugin.Log.LogError((object)("Could not load the embedded Nemo assets: " + ex));
				_ok = false;
			}
			return _ok;
		}

		private static byte[] GetResource(string suffix)
		{
			Assembly executingAssembly = Assembly.GetExecutingAssembly();
			string text = null;
			string[] manifestResourceNames = executingAssembly.GetManifestResourceNames();
			foreach (string text2 in manifestResourceNames)
			{
				if (text2.EndsWith(suffix, StringComparison.OrdinalIgnoreCase))
				{
					text = text2;
					break;
				}
			}
			if (text == null)
			{
				throw new Exception("embedded resource not found: " + suffix);
			}
			using Stream stream = executingAssembly.GetManifestResourceStream(text);
			using MemoryStream memoryStream = new MemoryStream();
			stream.CopyTo(memoryStream);
			return memoryStream.ToArray();
		}

		private static void ReadMesh(byte[] data)
		{
			//IL_009e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00de: 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)
			using BinaryReader binaryReader = new BinaryReader(new MemoryStream(data));
			if (new string(binaryReader.ReadChars(4)) != "NEMO")
			{
				throw new Exception("bad mesh magic");
			}
			ushort num = binaryReader.ReadUInt16();
			if (num != 1)
			{
				throw new Exception("unsupported mesh version " + num);
			}
			int num2 = (int)binaryReader.ReadUInt32();
			int num3 = (int)binaryReader.ReadUInt32();
			_pos = (Vector3[])(object)new Vector3[num2];
			_nrm = (Vector3[])(object)new Vector3[num2];
			_uv = (Vector2[])(object)new Vector2[num2];
			for (int i = 0; i < num2; i++)
			{
				_pos[i] = new Vector3(binaryReader.ReadSingle(), binaryReader.ReadSingle(), binaryReader.ReadSingle());
				_nrm[i] = new Vector3(binaryReader.ReadSingle(), binaryReader.ReadSingle(), binaryReader.ReadSingle());
				_uv[i] = new Vector2(binaryReader.ReadSingle(), binaryReader.ReadSingle());
			}
			_tri = new int[num3];
			for (int j = 0; j < num3; j++)
			{
				_tri[j] = binaryReader.ReadUInt16();
			}
		}

		private static Vector3 Axis(int i)
		{
			//IL_0013: 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_0007: Unknown result type (might be due to invalid IL or missing references)
			return (Vector3)(i switch
			{
				1 => Vector3.up, 
				0 => Vector3.right, 
				_ => Vector3.forward, 
			});
		}

		private static float Comp(Vector3 v, int i)
		{
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			return i switch
			{
				1 => v.y, 
				0 => v.x, 
				_ => v.z, 
			};
		}

		internal static Fit FitTo(Mesh original)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_01db: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e2: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e7: 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_01f2: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f7: Unknown result type (might be due to invalid IL or missing references)
			//IL_0221: Unknown result type (might be due to invalid IL or missing references)
			//IL_0226: Unknown result type (might be due to invalid IL or missing references)
			//IL_0228: Unknown result type (might be due to invalid IL or missing references)
			//IL_022a: Unknown result type (might be due to invalid IL or missing references)
			//IL_022f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0234: Unknown result type (might be due to invalid IL or missing references)
			//IL_023b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0256: Unknown result type (might be due to invalid IL or missing references)
			//IL_025b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00db: Unknown result type (might be due to invalid IL or missing references)
			//IL_0115: Unknown result type (might be due to invalid IL or missing references)
			//IL_0125: Unknown result type (might be due to invalid IL or missing references)
			//IL_02c1: Unknown result type (might be due to invalid IL or missing references)
			//IL_013c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0156: Unknown result type (might be due to invalid IL or missing references)
			Bounds bounds = original.bounds;
			Vector3 size = ((Bounds)(ref bounds)).size;
			int[] array = new int[3] { 0, 1, 2 };
			for (int i = 0; i < 2; i++)
			{
				for (int j = i + 1; j < 3; j++)
				{
					if (Comp(size, array[j]) > Comp(size, array[i]))
					{
						int num = array[i];
						array[i] = array[j];
						array[j] = num;
					}
				}
			}
			int num2 = array[0];
			int num3 = array[1];
			int num4 = array[2];
			float num5 = 1f;
			float num6 = 1f;
			Vector3[] vertices = original.vertices;
			if (vertices != null && vertices.Length != 0)
			{
				double num7 = 0.0;
				for (int k = 0; k < vertices.Length; k++)
				{
					num7 += (double)Comp(vertices[k], num2);
				}
				num5 = ((num7 / (double)vertices.Length >= (double)Comp(((Bounds)(ref bounds)).center, num2)) ? 1f : (-1f));
				double num8 = 0.0;
				double num9 = 0.0;
				int num10 = 0;
				int num11 = 0;
				float num12 = Comp(((Bounds)(ref bounds)).center, num3);
				float num13 = Comp(((Bounds)(ref bounds)).center, num4);
				for (int l = 0; l < vertices.Length; l++)
				{
					float num14 = Mathf.Abs(Comp(vertices[l], num4) - num13);
					if (Comp(vertices[l], num3) >= num12)
					{
						num8 += (double)num14;
						num10++;
					}
					else
					{
						num9 += (double)num14;
						num11++;
					}
				}
				if (num10 > 0 && num11 > 0)
				{
					num6 = ((num9 / (double)num11 >= num8 / (double)num10) ? 1f : (-1f));
				}
			}
			if (Plugin.CfgFlipForward.Value)
			{
				num5 = 0f - num5;
			}
			if (Plugin.CfgFlipUp.Value)
			{
				num6 = 0f - num6;
			}
			Vector3 val = Axis(num2) * num5;
			Vector3 val2 = Axis(num3) * num6;
			Fit result = default(Fit);
			result.Rot = Quaternion.Euler(Plugin.CfgRotateX.Value, Plugin.CfgRotateY.Value, Plugin.CfgRotateZ.Value) * Quaternion.LookRotation(val, val2);
			result.Scale = Comp(size, num2) * Plugin.CfgScale.Value;
			result.Centre = ((Bounds)(ref bounds)).center;
			result.Describe = string.Format("len=axis{0}{1} up=axis{2}{3} ", num2, (num5 > 0f) ? "+" : "-", num3, (num6 > 0f) ? "+" : "-") + $"wid=axis{num4} size={size} scale={result.Scale:0.###}";
			return result;
		}

		internal static Mesh Build(Mesh original, out Fit fit)
		{
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			//IL_0060: Unknown result type (might be due to invalid IL or missing references)
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0070: Unknown result type (might be due to invalid IL or missing references)
			//IL_0075: Unknown result type (might be due to invalid IL or missing references)
			//IL_0084: Unknown result type (might be due to invalid IL or missing references)
			//IL_0089: Unknown result type (might be due to invalid IL or missing references)
			//IL_0095: Expected O, but got Unknown
			fit = FitTo(original);
			Vector3[] array = (Vector3[])(object)new Vector3[_pos.Length];
			Vector3[] array2 = (Vector3[])(object)new Vector3[_nrm.Length];
			for (int i = 0; i < array.Length; i++)
			{
				array[i] = fit.Rot * (_pos[i] * fit.Scale) + fit.Centre;
				array2[i] = fit.Rot * _nrm[i];
			}
			Mesh val = new Mesh
			{
				name = "Nemo"
			};
			if (array.Length > 65000)
			{
				val.indexFormat = (IndexFormat)1;
			}
			val.vertices = array;
			val.normals = array2;
			val.uv = (Vector2[])_uv.Clone();
			val.triangles = (int[])_tri.Clone();
			val.RecalculateBounds();
			return val;
		}
	}
	internal static class Patches
	{
		[HarmonyPatch(typeof(Creature), "OnStartClient")]
		internal static class CreatureOnStartClient
		{
			[HarmonyPostfix]
			private static void Postfix(Creature __instance)
			{
				try
				{
					Swapper.Apply(__instance);
				}
				catch (Exception ex)
				{
					Plugin.Log.LogError((object)("OnStartClient swap failed: " + ex));
				}
			}
		}

		[HarmonyPatch(typeof(InventorySlot), "SetItem")]
		internal static class InventorySlotSetItem
		{
			[HarmonyPostfix]
			private static void Postfix(InventorySlot __instance)
			{
				try
				{
					Swapper.SyncSlot((Component)(object)__instance);
				}
				catch (Exception ex)
				{
					Plugin.Log.LogError((object)("Inventory slot sync failed: " + ex));
				}
			}
		}

		[HarmonyPatch(typeof(JournalSlot), "SetCreature")]
		internal static class JournalSlotSetCreature
		{
			[HarmonyPostfix]
			private static void Postfix(JournalSlot __instance)
			{
				try
				{
					Swapper.SyncSlot((Component)(object)__instance);
				}
				catch (Exception ex)
				{
					Plugin.Log.LogError((object)("Journal slot sync failed: " + ex));
				}
			}
		}

		[HarmonyPatch(typeof(MachineSlot), "SetIndex")]
		internal static class MachineSlotSetIndex
		{
			[HarmonyPostfix]
			private static void Postfix(MachineSlot __instance)
			{
				try
				{
					Swapper.SyncSlot((Component)(object)__instance);
				}
				catch (Exception ex)
				{
					Plugin.Log.LogError((object)("Machine slot sync failed: " + ex));
				}
			}
		}
	}
	[BepInPlugin("erwins.howtonemo", "How to Nemo", "1.0.0")]
	public class Plugin : BaseUnityPlugin
	{
		public const string Guid = "erwins.howtonemo";

		internal static ManualLogSource Log;

		internal static ConfigEntry<bool> CfgEnabled;

		internal static ConfigEntry<string> CfgTarget;

		internal static ConfigEntry<float> CfgScale;

		internal static ConfigEntry<bool> CfgFlipForward;

		internal static ConfigEntry<bool> CfgFlipUp;

		internal static ConfigEntry<float> CfgRotateX;

		internal static ConfigEntry<float> CfgRotateY;

		internal static ConfigEntry<float> CfgRotateZ;

		internal static ConfigEntry<bool> CfgTexture;

		internal static ConfigEntry<bool> CfgPreview;

		internal static ConfigEntry<bool> CfgHideExtras;

		internal static ConfigEntry<float> CfgBodyThreshold;

		internal static ConfigEntry<bool> CfgVerbose;

		private Harmony _harmony;

		private void Awake()
		{
			//IL_007d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0087: Expected O, but got Unknown
			//IL_00fa: Unknown result type (might be due to invalid IL or missing references)
			//IL_0104: Expected O, but got Unknown
			//IL_0137: Unknown result type (might be due to invalid IL or missing references)
			//IL_0141: Expected O, but got Unknown
			//IL_0174: Unknown result type (might be due to invalid IL or missing references)
			//IL_017e: Expected O, but got Unknown
			//IL_0211: Unknown result type (might be due to invalid IL or missing references)
			//IL_021b: Expected O, but got Unknown
			//IL_0270: Unknown result type (might be due to invalid IL or missing references)
			//IL_027a: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			CfgEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enabled", true, "Master switch. Off leaves every model stock.");
			CfgTarget = ((BaseUnityPlugin)this).Config.Bind<string>("General", "TargetPrefab", "clownfish", "Which item gets the Nemo model, by prefab name (spaces and case ignored). Any fish works if you would rather put him somewhere else. Takes effect on the next spawn.");
			CfgScale = ((BaseUnityPlugin)this).Config.Bind<float>("Placement", "Scale", 1f, new ConfigDescription("Multiplier on the automatic fit. 1 matches the original fish's length exactly.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.1f, 5f), Array.Empty<object>()));
			CfgFlipForward = ((BaseUnityPlugin)this).Config.Bind<bool>("Placement", "FlipForward", false, "Turn him around if he ends up swimming backwards.");
			CfgFlipUp = ((BaseUnityPlugin)this).Config.Bind<bool>("Placement", "FlipUp", false, "Turn him over if he ends up upside down.");
			CfgRotateX = ((BaseUnityPlugin)this).Config.Bind<float>("Placement", "RotateX", 0f, new ConfigDescription("Extra rotation in degrees, applied on top of the automatic fit.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(-180f, 180f), Array.Empty<object>()));
			CfgRotateY = ((BaseUnityPlugin)this).Config.Bind<float>("Placement", "RotateY", 0f, new ConfigDescription("Extra rotation in degrees, applied on top of the automatic fit.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(-180f, 180f), Array.Empty<object>()));
			CfgRotateZ = ((BaseUnityPlugin)this).Config.Bind<float>("Placement", "RotateZ", 0f, new ConfigDescription("Extra rotation in degrees, applied on top of the automatic fit.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(-180f, 180f), Array.Empty<object>()));
			CfgTexture = ((BaseUnityPlugin)this).Config.Bind<bool>("Appearance", "ReplaceTexture", true, "Put Nemo's own texture on the model. Off keeps the game's clownfish texture, which will not line up with his UVs.");
			CfgPreview = ((BaseUnityPlugin)this).Config.Bind<bool>("Appearance", "ReplaceInventoryPreview", true, "Also replace the small preview model in the inventory bar, the journal and the shop.");
			CfgHideExtras = ((BaseUnityPlugin)this).Config.Bind<bool>("Appearance", "HideLeftoverParts", true, "Hide any small stock renderers left over after the body is replaced — stray fins or eyes that Nemo already brings his own of. Takes effect on the next spawn.");
			CfgBodyThreshold = ((BaseUnityPlugin)this).Config.Bind<float>("Appearance", "BodyThreshold", 0.4f, new ConfigDescription("A renderer counts as a body (and gets replaced) when it is at least this fraction of the largest one. Lower catches more parts. Takes effect on the next spawn.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.05f, 1f), Array.Empty<object>()));
			CfgVerbose = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "VerboseLogging", false, "Log every renderer found on the fish and how the model was fitted to it. Turn this on before reporting a placement problem.");
			((BaseUnityPlugin)this).Config.SettingChanged += delegate
			{
				Runner.RefitWanted = true;
			};
			_harmony = new Harmony("erwins.howtonemo");
			_harmony.PatchAll(typeof(Patches.CreatureOnStartClient));
			_harmony.PatchAll(typeof(Patches.InventorySlotSetItem));
			_harmony.PatchAll(typeof(Patches.JournalSlotSetCreature));
			_harmony.PatchAll(typeof(Patches.MachineSlotSetIndex));
			((Component)this).gameObject.AddComponent<Runner>();
			Log.LogInfo((object)"How to Nemo loaded. He is out there somewhere.");
		}

		private void OnDestroy()
		{
			Harmony harmony = _harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
		}
	}
	internal class Runner : MonoBehaviour
	{
		internal static bool RefitWanted;

		private float _nextSweep;

		private void Update()
		{
			if (RefitWanted)
			{
				RefitWanted = false;
				try
				{
					Swapper.Refit();
				}
				catch (Exception ex)
				{
					Plugin.Log.LogError((object)("Re-fit failed: " + ex));
				}
			}
			if (!(Time.unscaledTime < _nextSweep))
			{
				_nextSweep = Time.unscaledTime + 30f;
				Swapper.Sweep();
			}
		}
	}
	internal static class Swapper
	{
		private struct Tracked
		{
			public Renderer Renderer;

			public SkinnedMeshRenderer Skinned;

			public MeshFilter Filter;

			public Mesh Original;
		}

		private struct Candidate
		{
			public Renderer Renderer;

			public SkinnedMeshRenderer Skinned;

			public MeshFilter Filter;

			public Mesh Mesh;

			public float Area;
		}

		private const string MeshName = "Nemo";

		private static readonly List<Tracked> Live = new List<Tracked>();

		private static readonly HashSet<int> Handled = new HashSet<int>();

		private static readonly Dictionary<int, Mesh> FitCache = new Dictionary<int, Mesh>();

		private static FieldInfo _fMesh;

		private static FieldInfo _fDripMesh;

		private static Item _prefab;

		private static readonly Dictionary<string, Mesh> PrefabOriginals = new Dictionary<string, Mesh>();

		private static bool _prefabDone;

		private static readonly Dictionary<int, Texture> OriginalSlotTex = new Dictionary<int, Texture>();

		internal static string TargetKey => Normalise(Plugin.CfgTarget.Value);

		internal static string Normalise(string s)
		{
			if (string.IsNullOrEmpty(s))
			{
				return string.Empty;
			}
			return s.Replace("(Clone)", string.Empty).Replace(" ", string.Empty).ToLowerInvariant();
		}

		internal static bool IsTarget(Item item)
		{
			if ((Object)(object)item == (Object)null)
			{
				return false;
			}
			return Normalise(((Object)((Component)item).gameObject).name) == TargetKey;
		}

		private static bool IsOurs(Mesh m)
		{
			if ((Object)(object)m != (Object)null && ((Object)m).name != null)
			{
				return ((Object)m).name.StartsWith("Nemo", StringComparison.Ordinal);
			}
			return false;
		}

		internal static void Apply(Creature creature)
		{
			if (!Plugin.CfgEnabled.Value || (Object)(object)creature == (Object)null || !IsTarget((Item)(object)creature) || !NemoModel.Load() || !Handled.Add(((Object)((Component)creature).gameObject).GetInstanceID()))
			{
				return;
			}
			PatchPrefabOnce();
			List<Candidate> list = Collect(((Component)creature).gameObject);
			if (list.Count == 0)
			{
				Plugin.Log.LogWarning((object)(((Object)((Component)creature).gameObject).name + ": no renderer with a mesh found; nothing to replace."));
				return;
			}
			float num = 0f;
			foreach (Candidate item in list)
			{
				num = Mathf.Max(num, item.Area);
			}
			int num2 = 0;
			int num3 = 0;
			foreach (Candidate item2 in list)
			{
				bool flag = item2.Area >= num * Plugin.CfgBodyThreshold.Value;
				if (Plugin.CfgVerbose.Value)
				{
					Plugin.Log.LogInfo((object)("  renderer '" + ((Object)item2.Renderer).name + "' mesh '" + ((Object)item2.Mesh).name + "' " + $"area={item2.Area:0.######} skinned={(Object)(object)item2.Skinned != (Object)null} body={flag}"));
				}
				if (flag)
				{
					Tracked tracked = new Tracked
					{
						Renderer = item2.Renderer,
						Skinned = item2.Skinned,
						Filter = item2.Filter,
						Original = item2.Mesh
					};
					Live.Add(tracked);
					Fit(tracked);
					num2++;
				}
				else if (Plugin.CfgHideExtras.Value)
				{
					item2.Renderer.enabled = false;
					num3++;
				}
			}
			Plugin.Log.LogInfo((object)($"{((Object)((Component)creature).gameObject).name}: replaced {num2} renderer(s)" + ((num3 > 0) ? $", hid {num3} leftover(s)" : string.Empty) + "."));
		}

		private static List<Candidate> Collect(GameObject root)
		{
			List<Candidate> list = new List<Candidate>();
			SkinnedMeshRenderer[] componentsInChildren = root.GetComponentsInChildren<SkinnedMeshRenderer>(true);
			foreach (SkinnedMeshRenderer val in componentsInChildren)
			{
				if (!((Object)(object)val == (Object)null) && !((Object)(object)val.sharedMesh == (Object)null) && !IsOurs(val.sharedMesh))
				{
					list.Add(Make((Renderer)(object)val, val, null, val.sharedMesh));
				}
			}
			MeshFilter[] componentsInChildren2 = root.GetComponentsInChildren<MeshFilter>(true);
			foreach (MeshFilter val2 in componentsInChildren2)
			{
				if (!((Object)(object)val2 == (Object)null) && !((Object)(object)val2.sharedMesh == (Object)null) && !IsOurs(val2.sharedMesh))
				{
					Renderer component = ((Component)val2).GetComponent<Renderer>();
					if (!((Object)(object)component == (Object)null))
					{
						list.Add(Make(component, null, val2, val2.sharedMesh));
					}
				}
			}
			return list;
		}

		private static Candidate Make(Renderer r, SkinnedMeshRenderer smr, MeshFilter mf, Mesh mesh)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			Bounds bounds = mesh.bounds;
			Vector3 size = ((Bounds)(ref bounds)).size;
			return new Candidate
			{
				Renderer = r,
				Skinned = smr,
				Filter = mf,
				Mesh = mesh,
				Area = Mathf.Max(size.x * size.y, Mathf.Max(size.y * size.z, size.x * size.z))
			};
		}

		private static void Fit(Tracked t)
		{
			Mesh val = BuildCached(t.Original);
			if ((Object)(object)t.Skinned != (Object)null)
			{
				BindSkinned(t.Skinned, val, t.Original);
			}
			else
			{
				t.Filter.sharedMesh = val;
			}
			ApplyTexture(t.Renderer);
		}

		private static Mesh BuildCached(Mesh original)
		{
			int instanceID = ((Object)original).GetInstanceID();
			if (FitCache.TryGetValue(instanceID, out var value) && (Object)(object)value != (Object)null)
			{
				return value;
			}
			NemoModel.Fit fit;
			Mesh val = NemoModel.Build(original, out fit);
			((Object)val).name = "Nemo";
			FitCache[instanceID] = val;
			if (Plugin.CfgVerbose.Value)
			{
				Plugin.Log.LogInfo((object)("    fit against '" + ((Object)original).name + "': " + fit.Describe));
			}
			return val;
		}

		private static void BindSkinned(SkinnedMeshRenderer smr, Mesh nemo, Mesh original)
		{
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c3: Unknown result type (might be due to invalid IL or missing references)
			//IL_006e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0073: Unknown result type (might be due to invalid IL or missing references)
			//IL_0077: 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_00c8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00db: Unknown result type (might be due to invalid IL or missing references)
			//IL_0119: Unknown result type (might be due to invalid IL or missing references)
			//IL_0193: Unknown result type (might be due to invalid IL or missing references)
			//IL_0195: Unknown result type (might be due to invalid IL or missing references)
			Matrix4x4[] bindposes = original.bindposes;
			Transform[] bones = smr.bones;
			if (bindposes == null || bindposes.Length == 0 || bones == null || bones.Length == 0)
			{
				smr.sharedMesh = nemo;
				((Renderer)smr).localBounds = nemo.bounds;
				return;
			}
			if (nemo.boneWeights == null || nemo.boneWeights.Length != nemo.vertexCount)
			{
				int num = Mathf.Min(bindposes.Length, bones.Length);
				Vector3[] array = (Vector3[])(object)new Vector3[num];
				for (int i = 0; i < num; i++)
				{
					int num2 = i;
					Matrix4x4 inverse = ((Matrix4x4)(ref bindposes[i])).inverse;
					array[num2] = ((Matrix4x4)(ref inverse)).MultiplyPoint3x4(Vector3.zero);
				}
				Vector3[] vertices = nemo.vertices;
				BoneWeight[] array2 = (BoneWeight[])(object)new BoneWeight[vertices.Length];
				for (int j = 0; j < vertices.Length; j++)
				{
					int num3 = -1;
					int num4 = -1;
					float num5 = float.MaxValue;
					float num6 = float.MaxValue;
					for (int k = 0; k < num; k++)
					{
						Vector3 val = array[k] - vertices[j];
						float sqrMagnitude = ((Vector3)(ref val)).sqrMagnitude;
						if (sqrMagnitude < num5)
						{
							num6 = num5;
							num4 = num3;
							num5 = sqrMagnitude;
							num3 = k;
						}
						else if (sqrMagnitude < num6)
						{
							num6 = sqrMagnitude;
							num4 = k;
						}
					}
					BoneWeight val2 = default(BoneWeight);
					((BoneWeight)(ref val2)).boneIndex0 = Mathf.Max(num3, 0);
					if (num4 < 0)
					{
						((BoneWeight)(ref val2)).weight0 = 1f;
					}
					else
					{
						float num7 = Mathf.Sqrt(num5);
						float num8 = Mathf.Sqrt(num6);
						float num9 = num7 + num8;
						((BoneWeight)(ref val2)).weight0 = ((num9 <= 1E-06f) ? 1f : (num8 / num9));
						((BoneWeight)(ref val2)).weight1 = 1f - ((BoneWeight)(ref val2)).weight0;
						((BoneWeight)(ref val2)).boneIndex1 = num4;
					}
					array2[j] = val2;
				}
				nemo.bindposes = bindposes;
				nemo.boneWeights = array2;
			}
			smr.sharedMesh = nemo;
			((Renderer)smr).localBounds = nemo.bounds;
		}

		internal static void ApplyTexture(Renderer r)
		{
			if (!((Object)(object)r == (Object)null) && Plugin.CfgTexture.Value)
			{
				ApplyTexture(r.material);
			}
		}

		internal static void ApplyTexture(Material m)
		{
			//IL_0064: 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)
			if (!((Object)(object)m == (Object)null) && !((Object)(object)NemoModel.Atlas == (Object)null))
			{
				if (m.HasProperty("_BaseMap"))
				{
					m.SetTexture("_BaseMap", (Texture)(object)NemoModel.Atlas);
				}
				if (m.HasProperty("_MainTex"))
				{
					m.SetTexture("_MainTex", (Texture)(object)NemoModel.Atlas);
				}
				if (m.HasProperty("_BaseColor"))
				{
					m.SetColor("_BaseColor", Color.white);
				}
				if (m.HasProperty("_Color"))
				{
					m.SetColor("_Color", Color.white);
				}
				if (m.HasProperty("_Use_Skin"))
				{
					m.SetFloat("_Use_Skin", 0f);
				}
			}
		}

		internal static void Sweep()
		{
			Live.RemoveAll((Tracked t) => (Object)(object)t.Renderer == (Object)null || (Object)(object)t.Original == (Object)null);
		}

		internal static void Refit()
		{
			if (!NemoModel.Load())
			{
				return;
			}
			FitCache.Clear();
			Sweep();
			foreach (Tracked item in Live)
			{
				Fit(item);
			}
			RefitPrefab();
			if (Live.Count > 0 || PrefabOriginals.Count > 0)
			{
				Plugin.Log.LogInfo((object)$"Re-fitted {Live.Count} renderer(s) and the inventory preview.");
			}
		}

		internal static void PatchPrefabOnce()
		{
			if (_prefabDone)
			{
				return;
			}
			_prefabDone = true;
			try
			{
				_prefab = GameInfo.GetSpawnable(TargetKey);
				if ((Object)(object)_prefab == (Object)null)
				{
					Plugin.Log.LogWarning((object)("GameInfo has no spawnable named '" + TargetKey + "'; inventory preview left alone."));
					return;
				}
				_fMesh = AccessTools.Field(typeof(Item), "_mesh");
				_fDripMesh = AccessTools.Field(typeof(Creature), "_dripMesh");
				Remember(_fMesh);
				if (_prefab is Creature)
				{
					Remember(_fDripMesh);
				}
				RefitPrefab();
			}
			catch (Exception ex)
			{
				Plugin.Log.LogError((object)("Could not patch the preview mesh: " + ex));
			}
		}

		private static void Remember(FieldInfo field)
		{
			if (!(field == null) && !((Object)(object)_prefab == (Object)null))
			{
				object? value = field.GetValue(_prefab);
				Mesh val = (Mesh)((value is Mesh) ? value : null);
				if (val != null && !IsOurs(val))
				{
					PrefabOriginals[field.Name] = val;
				}
			}
		}

		private static void RefitPrefab()
		{
			if ((Object)(object)_prefab == (Object)null)
			{
				return;
			}
			foreach (KeyValuePair<string, Mesh> prefabOriginal in PrefabOriginals)
			{
				FieldInfo fieldInfo = ((prefabOriginal.Key == "_mesh") ? _fMesh : _fDripMesh);
				if (!(fieldInfo == null) && !((Object)(object)prefabOriginal.Value == (Object)null))
				{
					fieldInfo.SetValue(_prefab, Plugin.CfgPreview.Value ? BuildCached(prefabOriginal.Value) : prefabOriginal.Value);
				}
			}
		}

		internal static void SyncSlot(Component slot)
		{
			if ((Object)(object)slot == (Object)null || !Plugin.CfgPreview.Value || !Plugin.CfgTexture.Value || (Object)(object)NemoModel.Atlas == (Object)null)
			{
				return;
			}
			MeshFilter[] componentsInChildren = slot.GetComponentsInChildren<MeshFilter>(true);
			foreach (MeshFilter val in componentsInChildren)
			{
				Renderer component = ((Component)val).GetComponent<Renderer>();
				if ((Object)(object)component == (Object)null)
				{
					continue;
				}
				Material material = component.material;
				if ((Object)(object)material == (Object)null)
				{
					continue;
				}
				int instanceID = ((Object)material).GetInstanceID();
				Texture value;
				if (IsOurs(val.sharedMesh))
				{
					if (!OriginalSlotTex.ContainsKey(instanceID))
					{
						OriginalSlotTex[instanceID] = (material.HasProperty("_BaseMap") ? material.GetTexture("_BaseMap") : null);
					}
					ApplyTexture(material);
				}
				else if (OriginalSlotTex.TryGetValue(instanceID, out value))
				{
					if (material.HasProperty("_BaseMap"))
					{
						material.SetTexture("_BaseMap", value);
					}
					if (material.HasProperty("_MainTex"))
					{
						material.SetTexture("_MainTex", value);
					}
					OriginalSlotTex.Remove(instanceID);
				}
			}
		}
	}
}