Decompiled source of ValheimTattoos v1.0.1

plugins/ValheimTattoos.dll

Decompiled 9 hours ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using TMPro;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.Rendering;
using UnityEngine.UI;

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

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace ValheimTattoos
{
	internal static class ClothMask
	{
		private const int Size = 256;

		internal static readonly int ChestTexId = Shader.PropertyToID("_ChestTex");

		internal static readonly int LegsTexId = Shader.PropertyToID("_LegsTex");

		private static readonly Dictionary<int, byte[]> Cache = new Dictionary<int, byte[]>();

		private static MaterialPropertyBlock _mpb;

		internal static Texture GetClothTexture(SkinnedMeshRenderer body, int propId)
		{
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Expected O, but got Unknown
			if ((Object)(object)body == (Object)null)
			{
				return null;
			}
			try
			{
				if (_mpb == null)
				{
					_mpb = new MaterialPropertyBlock();
				}
				((Renderer)body).GetPropertyBlock(_mpb);
				if (!_mpb.isEmpty && _mpb.HasTexture(propId))
				{
					Texture texture = _mpb.GetTexture(propId);
					if ((Object)(object)texture != (Object)null)
					{
						return texture;
					}
				}
			}
			catch
			{
			}
			Material sharedMaterial = ((Renderer)body).sharedMaterial;
			if (!((Object)(object)sharedMaterial != (Object)null) || !sharedMaterial.HasProperty(propId))
			{
				return null;
			}
			return sharedMaterial.GetTexture(propId);
		}

		internal static byte[] Get(Texture tex)
		{
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0060: Unknown result type (might be due to invalid IL or missing references)
			//IL_0075: Unknown result type (might be due to invalid IL or missing references)
			//IL_0082: Unknown result type (might be due to invalid IL or missing references)
			//IL_0089: Unknown result type (might be due to invalid IL or missing references)
			//IL_0096: Expected O, but got Unknown
			if ((Object)(object)tex == (Object)null)
			{
				return null;
			}
			int instanceID = ((Object)tex).GetInstanceID();
			if (Cache.TryGetValue(instanceID, out var value))
			{
				return value;
			}
			byte[] array = null;
			RenderTexture val = null;
			RenderTexture active = RenderTexture.active;
			try
			{
				val = RenderTexture.GetTemporary(256, 256, 0, (RenderTextureFormat)0, (RenderTextureReadWrite)1);
				Graphics.Blit(tex, val);
				RenderTexture.active = val;
				Texture2D val2 = new Texture2D(256, 256, (TextureFormat)4, false, true);
				val2.ReadPixels(new Rect(0f, 0f, 256f, 256f), 0, 0, false);
				val2.Apply(false);
				Color32[] pixels = val2.GetPixels32();
				Object.Destroy((Object)val2);
				byte[] array2 = new byte[65536];
				bool flag = false;
				for (int i = 0; i < array2.Length; i++)
				{
					array2[i] = pixels[i].a;
					if (array2[i] > 20)
					{
						flag = true;
					}
				}
				array = (flag ? array2 : null);
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("Não deu pra ler a roupa '" + ((Object)tex).name + "': " + ex.Message));
			}
			finally
			{
				RenderTexture.active = active;
				if ((Object)(object)val != (Object)null)
				{
					RenderTexture.ReleaseTemporary(val);
				}
			}
			Cache[instanceID] = array;
			return array;
		}

		private static float Sample(byte[] m, Vector2 uv)
		{
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			if (m == null)
			{
				return 0f;
			}
			int num = Mathf.Clamp((int)(Mathf.Repeat(uv.x, 1f) * 256f), 0, 255);
			int num2 = Mathf.Clamp((int)(Mathf.Repeat(uv.y, 1f) * 256f), 0, 255);
			return (float)(int)m[num2 * 256 + num] / 255f;
		}

		internal static Texture2D Apply(string tattooId, Mesh shell, byte[] chest, byte[] legs)
		{
			//IL_00c1: 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_00e8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f8: 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_011f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0132: Unknown result type (might be due to invalid IL or missing references)
			//IL_0139: Unknown result type (might be due to invalid IL or missing references)
			//IL_0140: Unknown result type (might be due to invalid IL or missing references)
			//IL_0160: Unknown result type (might be due to invalid IL or missing references)
			//IL_0167: Unknown result type (might be due to invalid IL or missing references)
			//IL_016e: Unknown result type (might be due to invalid IL or missing references)
			//IL_018c: 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_019a: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c1: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c8: 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_01fe: Unknown result type (might be due to invalid IL or missing references)
			//IL_0206: Unknown result type (might be due to invalid IL or missing references)
			//IL_020d: 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_021d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0225: Unknown result type (might be due to invalid IL or missing references)
			//IL_022c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0436: Unknown result type (might be due to invalid IL or missing references)
			//IL_043b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0452: Unknown result type (might be due to invalid IL or missing references)
			//IL_0457: Unknown result type (might be due to invalid IL or missing references)
			//IL_046a: Expected O, but got Unknown
			//IL_0298: Unknown result type (might be due to invalid IL or missing references)
			//IL_029f: Unknown result type (might be due to invalid IL or missing references)
			//IL_02a9: Unknown result type (might be due to invalid IL or missing references)
			//IL_02b2: Unknown result type (might be due to invalid IL or missing references)
			//IL_02b9: Unknown result type (might be due to invalid IL or missing references)
			//IL_02c3: Unknown result type (might be due to invalid IL or missing references)
			//IL_02d2: Unknown result type (might be due to invalid IL or missing references)
			//IL_02d9: Unknown result type (might be due to invalid IL or missing references)
			//IL_02e3: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ec: Unknown result type (might be due to invalid IL or missing references)
			//IL_02f3: Unknown result type (might be due to invalid IL or missing references)
			//IL_02fd: Unknown result type (might be due to invalid IL or missing references)
			//IL_0341: Unknown result type (might be due to invalid IL or missing references)
			//IL_0348: 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_035d: 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_036d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0372: Unknown result type (might be due to invalid IL or missing references)
			//IL_0377: Unknown result type (might be due to invalid IL or missing references)
			//IL_037a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0382: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)shell == (Object)null || (chest == null && legs == null))
			{
				return null;
			}
			int width;
			int height;
			Color32[] pixels = TattooLibrary.GetPixels(tattooId, out width, out height);
			if (pixels == null)
			{
				return null;
			}
			List<Vector2> list = new List<Vector2>();
			List<Vector2> list2 = new List<Vector2>();
			shell.GetUVs(0, list);
			shell.GetUVs(1, list2);
			if (list.Count == 0 || list2.Count != list.Count)
			{
				return null;
			}
			int[] triangles = shell.triangles;
			byte[] array = new byte[pixels.Length];
			for (int i = 0; i < pixels.Length; i++)
			{
				array[i] = pixels[i].a;
			}
			bool[] array2 = new bool[width * height];
			bool flag = false;
			Vector2 val = default(Vector2);
			Vector2 val2 = default(Vector2);
			Vector2 val3 = default(Vector2);
			for (int j = 0; j < triangles.Length; j += 3)
			{
				int index = triangles[j];
				int index2 = triangles[j + 1];
				int index3 = triangles[j + 2];
				((Vector2)(ref val))..ctor(list[index].x * (float)width, list[index].y * (float)height);
				((Vector2)(ref val2))..ctor(list[index2].x * (float)width, list[index2].y * (float)height);
				((Vector2)(ref val3))..ctor(list[index3].x * (float)width, list[index3].y * (float)height);
				int num = Mathf.Max(0, Mathf.FloorToInt(Mathf.Min(val.x, Mathf.Min(val2.x, val3.x))));
				int num2 = Mathf.Min(width - 1, Mathf.CeilToInt(Mathf.Max(val.x, Mathf.Max(val2.x, val3.x))));
				int num3 = Mathf.Max(0, Mathf.FloorToInt(Mathf.Min(val.y, Mathf.Min(val2.y, val3.y))));
				int num4 = Mathf.Min(height - 1, Mathf.CeilToInt(Mathf.Max(val.y, Mathf.Max(val2.y, val3.y))));
				if (num2 < num || num4 < num3)
				{
					continue;
				}
				float num5 = (val2.y - val3.y) * (val.x - val3.x) + (val3.x - val2.x) * (val.y - val3.y);
				if (Mathf.Abs(num5) < 1E-08f)
				{
					continue;
				}
				float num6 = 1f / num5;
				for (int k = num3; k <= num4; k++)
				{
					float num7 = (float)k + 0.5f;
					for (int l = num; l <= num2; l++)
					{
						int num8 = k * width + l;
						if (array2[num8] || array[num8] == 0)
						{
							continue;
						}
						float num9 = (float)l + 0.5f;
						float num10 = ((val2.y - val3.y) * (num9 - val3.x) + (val3.x - val2.x) * (num7 - val3.y)) * num6;
						float num11 = ((val3.y - val.y) * (num9 - val3.x) + (val.x - val3.x) * (num7 - val3.y)) * num6;
						float num12 = 1f - num10 - num11;
						if (!(num10 < -0.002f) && !(num11 < -0.002f) && !(num12 < -0.002f))
						{
							Vector2 uv = list2[index] * num10 + list2[index2] * num11 + list2[index3] * num12;
							float num13 = Mathf.Max(Sample(chest, uv), Sample(legs, uv));
							array2[num8] = true;
							if (!(num13 <= 0.02f))
							{
								array[num8] = (byte)((float)(int)array[num8] * (1f - Mathf.Clamp01(num13 * 1.5f)));
								flag = true;
							}
						}
					}
				}
			}
			if (!flag)
			{
				return null;
			}
			Color32[] array3 = (Color32[])(object)new Color32[width * height];
			for (int m = 0; m < array3.Length; m++)
			{
				array3[m] = new Color32(pixels[m].r, pixels[m].g, pixels[m].b, array[m]);
			}
			Texture2D val4 = new Texture2D(width, height, (TextureFormat)4, true)
			{
				name = "ValheimTattoo_Masked_" + tattooId
			};
			((Texture)val4).wrapMode = (TextureWrapMode)1;
			((Texture)val4).filterMode = (FilterMode)2;
			((Texture)val4).anisoLevel = 4;
			val4.SetPixels32(array3);
			val4.Apply(true, false);
			if (width % 4 == 0 && height % 4 == 0)
			{
				try
				{
					val4.Compress(true);
				}
				catch
				{
				}
			}
			val4.Apply(false, true);
			return val4;
		}
	}
	internal static class Loc
	{
		private static readonly string[] Keys = new string[19]
		{
			"title", "none", "tattoo", "paint", "slot_face", "slot_chest", "slot_back", "slot_left_arm", "slot_right_arm", "slot_left_leg",
			"slot_right_leg", "red", "green", "blue", "size", "height", "side", "rotate", "zoom"
		};

		private static readonly Dictionary<string, string> English = new Dictionary<string, string>
		{
			{ "title", "Tattoos" },
			{ "none", "None" },
			{ "tattoo", "Tattoo" },
			{ "paint", "War paint" },
			{ "slot_face", "Face" },
			{ "slot_chest", "Chest" },
			{ "slot_back", "Back" },
			{ "slot_left_arm", "Left arm" },
			{ "slot_right_arm", "Right arm" },
			{ "slot_left_leg", "Left leg" },
			{ "slot_right_leg", "Right leg" },
			{ "red", "Red" },
			{ "green", "Green" },
			{ "blue", "Blue" },
			{ "size", "Size" },
			{ "height", "Height" },
			{ "side", "Side" },
			{ "rotate", "Rotate" },
			{ "zoom", "Zoom" }
		};

		private static readonly Dictionary<string, string> Portuguese = new Dictionary<string, string>
		{
			{ "title", "Tatuagens" },
			{ "none", "Nenhuma" },
			{ "tattoo", "Tatuagem" },
			{ "paint", "Pintura" },
			{ "slot_face", "Rosto" },
			{ "slot_chest", "Peito" },
			{ "slot_back", "Costas" },
			{ "slot_left_arm", "Braço esquerdo" },
			{ "slot_right_arm", "Braço direito" },
			{ "slot_left_leg", "Perna esquerda" },
			{ "slot_right_leg", "Perna direita" },
			{ "red", "Vermelho" },
			{ "green", "Verde" },
			{ "blue", "Azul" },
			{ "size", "Tamanho" },
			{ "height", "Altura" },
			{ "side", "Lado" },
			{ "rotate", "Girar" },
			{ "zoom", "Zoom" }
		};

		private static string _folder;

		private static string _loadedLang;

		private static Dictionary<string, string> _current = English;

		internal static string Title => T("title");

		internal static string None => T("none");

		internal static string Tattoo => T("tattoo");

		internal static string Paint => T("paint");

		internal static string Zoom => T("zoom");

		internal static void Init(string configFolder)
		{
			_folder = Path.Combine(configFolder, "Translations");
			try
			{
				Directory.CreateDirectory(_folder);
				WriteDefault("English", English);
				WriteDefault("Portuguese_Brazilian", Portuguese);
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("Não deu pra criar os arquivos de tradução: " + ex.Message));
			}
		}

		private static void WriteDefault(string lang, Dictionary<string, string> texts)
		{
			string path = Path.Combine(_folder, lang + ".txt");
			if (File.Exists(path))
			{
				HashSet<string> hashSet = new HashSet<string>();
				string[] array = File.ReadAllLines(path, Encoding.UTF8);
				foreach (string text in array)
				{
					int num = text.IndexOf('=');
					if (num > 0 && !text.TrimStart(Array.Empty<char>()).StartsWith("#"))
					{
						hashSet.Add(text.Substring(0, num).Trim());
					}
				}
				StringBuilder stringBuilder = new StringBuilder();
				array = Keys;
				foreach (string text2 in array)
				{
					if (!hashSet.Contains(text2))
					{
						stringBuilder.AppendLine(text2 + " = " + texts[text2]);
					}
				}
				if (stringBuilder.Length > 0)
				{
					File.AppendAllText(path, Environment.NewLine + stringBuilder, new UTF8Encoding(encoderShouldEmitUTF8Identifier: false));
				}
			}
			else
			{
				StringBuilder stringBuilder2 = new StringBuilder();
				stringBuilder2.AppendLine("# ValheimTattoos - " + lang);
				stringBuilder2.AppendLine("# To translate: copy English.txt, rename it to your game language and translate the right side.");
				stringBuilder2.AppendLine("# The file name is the language name Valheim uses (see the log: \"ValheimTattoos language\").");
				stringBuilder2.AppendLine("# Pra traduzir: copie o English.txt, renomeie pro idioma do jogo e traduza o lado direito.");
				stringBuilder2.AppendLine("# Ex: Spanish.txt, German.txt, French.txt, Russian.txt, Polish.txt, Portuguese_European.txt");
				stringBuilder2.AppendLine();
				string[] array = Keys;
				foreach (string text3 in array)
				{
					stringBuilder2.AppendLine(text3 + " = " + texts[text3]);
				}
				File.WriteAllText(path, stringBuilder2.ToString(), new UTF8Encoding(encoderShouldEmitUTF8Identifier: false));
			}
		}

		private static string GameLanguage()
		{
			try
			{
				return (Localization.instance != null) ? (Localization.instance.GetSelectedLanguage() ?? "English") : "English";
			}
			catch
			{
				return "English";
			}
		}

		private static void Refresh()
		{
			string text = GameLanguage();
			if (text == _loadedLang)
			{
				return;
			}
			_loadedLang = text;
			Dictionary<string, string> dictionary = new Dictionary<string, string>(text.StartsWith("Portuguese") ? Portuguese : English);
			string text2 = ((_folder != null) ? Path.Combine(_folder, text + ".txt") : null);
			if (text2 != null && File.Exists(text2))
			{
				try
				{
					int num = 0;
					string[] array = File.ReadAllLines(text2, Encoding.UTF8);
					for (int i = 0; i < array.Length; i++)
					{
						string text3 = array[i].Trim();
						if (text3.Length == 0 || text3.StartsWith("#"))
						{
							continue;
						}
						int num2 = text3.IndexOf('=');
						if (num2 > 0)
						{
							string text4 = text3.Substring(0, num2).Trim();
							string text5 = text3.Substring(num2 + 1).Trim();
							if (text4.Length != 0 && text5.Length != 0)
							{
								dictionary[text4] = text5;
								num++;
							}
						}
					}
					Plugin.Log.LogInfo((object)$"ValheimTattoos language: {text} ({num} textos de {text2})");
				}
				catch (Exception ex)
				{
					Plugin.Log.LogWarning((object)("Erro lendo a tradução '" + text2 + "': " + ex.Message));
				}
			}
			else
			{
				Plugin.Log.LogInfo((object)("ValheimTattoos language: " + text + " (sem arquivo " + text + ".txt em " + _folder + ", usando o padrão)"));
			}
			_current = dictionary;
		}

		private static string T(string key)
		{
			Refresh();
			if (!_current.TryGetValue(key, out var value))
			{
				if (!English.TryGetValue(key, out var value2))
				{
					return key;
				}
				return value2;
			}
			return value;
		}

		internal static string SlotName(TattooSlot slot)
		{
			return slot switch
			{
				TattooSlot.Face => T("slot_face"), 
				TattooSlot.Chest => T("slot_chest"), 
				TattooSlot.Back => T("slot_back"), 
				TattooSlot.LeftArm => T("slot_left_arm"), 
				TattooSlot.RightArm => T("slot_right_arm"), 
				TattooSlot.LeftLeg => T("slot_left_leg"), 
				TattooSlot.RightLeg => T("slot_right_leg"), 
				_ => slot.ToString(), 
			};
		}

		internal static string[] SliderNames(TattooSlot slot)
		{
			bool flag = TattooSet.IsLimb(slot);
			return new string[6]
			{
				T("red"),
				T("green"),
				T("blue"),
				T("size"),
				T("height"),
				flag ? T("rotate") : T("side")
			};
		}
	}
	internal static class Patches
	{
		private static readonly MethodInfo GetRagdollMethod = AccessTools.Method(typeof(Player), "GetRagdoll", (Type[])null, (Type[])null);

		internal static void Apply(Harmony h)
		{
			TryPatch(h, typeof(Player), "Awake", null, "Player_Awake_Postfix");
			TryPatch(h, typeof(PlayerCustomizaton), "Update", null, "Customization_Update_Postfix");
			TryPatch(h, typeof(PlayerCustomizaton), "OnCancel", "Customization_OnCancel_Prefix");
			TryPatch(h, typeof(PlayerCustomizaton), "OnApply", null, "Customization_OnApply_Postfix");
			TryPatch(h, typeof(Player), "CreateDeathEffects", null, "Player_CreateDeathEffects_Postfix");
			TryPatch(h, typeof(Ragdoll), "Awake", null, "Ragdoll_Awake_Postfix");
		}

		private static void TryPatch(Harmony h, Type type, string method, string prefix = null, string postfix = null)
		{
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_0062: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				MethodInfo methodInfo = AccessTools.DeclaredMethod(type, method, (Type[])null, (Type[])null);
				if (methodInfo == null)
				{
					Plugin.Log.LogWarning((object)(type.Name + "." + method + " não encontrado, patch ignorado."));
				}
				else
				{
					h.Patch((MethodBase)methodInfo, (prefix == null) ? ((HarmonyMethod)null) : new HarmonyMethod(typeof(Patches), prefix, (Type[])null), (postfix == null) ? ((HarmonyMethod)null) : new HarmonyMethod(typeof(Patches), postfix, (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				}
			}
			catch (Exception arg)
			{
				Plugin.Log.LogError((object)$"Falha ao patchear {type.Name}.{method}: {arg}");
			}
		}

		private static void Player_Awake_Postfix(Player __instance)
		{
			try
			{
				if ((Object)(object)__instance != (Object)null && (Object)(object)((Component)__instance).GetComponent<TattooRenderer>() == (Object)null)
				{
					((Component)__instance).gameObject.AddComponent<TattooRenderer>();
				}
			}
			catch (Exception ex)
			{
				Plugin.Log.LogError((object)ex);
			}
		}

		private static void Player_CreateDeathEffects_Postfix(Player __instance)
		{
			try
			{
				if ((Object)(object)__instance == (Object)null)
				{
					return;
				}
				string playerData = TattooSet.GetPlayerData(__instance);
				if (string.IsNullOrEmpty(playerData))
				{
					return;
				}
				object? obj = GetRagdollMethod?.Invoke(__instance, null);
				Ragdoll val = (Ragdoll)((obj is Ragdoll) ? obj : null);
				if (!((Object)(object)val == (Object)null))
				{
					ZNetView component = ((Component)val).GetComponent<ZNetView>();
					if ((Object)(object)component != (Object)null && component.IsValid() && component.GetZDO() != null)
					{
						component.GetZDO().Set("ValheimTattoos_Data", playerData);
					}
					AddRagdollRenderer(val);
				}
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("Tatuagens no ragdoll: " + ex.Message));
			}
		}

		private static void Ragdoll_Awake_Postfix(Ragdoll __instance)
		{
			try
			{
				AddRagdollRenderer(__instance);
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("Tatuagens no ragdoll: " + ex.Message));
			}
		}

		private static void AddRagdollRenderer(Ragdoll ragdoll)
		{
			if ((Object)(object)ragdoll == (Object)null)
			{
				return;
			}
			VisEquipment val = ((Component)ragdoll).GetComponent<VisEquipment>() ?? ((Component)ragdoll).GetComponentInChildren<VisEquipment>(true);
			if (!((Object)(object)val == (Object)null))
			{
				GameObject gameObject = ((Component)val).gameObject;
				if ((Object)(object)gameObject.GetComponent<TattooRenderer>() == (Object)null)
				{
					gameObject.AddComponent<TattooRenderer>();
				}
			}
		}

		private static void Customization_Update_Postfix(PlayerCustomizaton __instance)
		{
			try
			{
				TattooPanel.PostUpdate(__instance);
			}
			catch (Exception ex)
			{
				Plugin.Log.LogError((object)ex);
			}
		}

		private static void Customization_OnCancel_Prefix()
		{
			try
			{
				TattooPanel.OnCancel();
			}
			catch (Exception ex)
			{
				Plugin.Log.LogError((object)ex);
			}
		}

		private static void Customization_OnApply_Postfix()
		{
			try
			{
				TattooPanel.OnApply();
			}
			catch (Exception ex)
			{
				Plugin.Log.LogError((object)ex);
			}
		}
	}
	[BepInPlugin("morgnar.valheimtattoos", "ValheimTattoos", "1.0.1")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public class Plugin : BaseUnityPlugin
	{
		public const string ModGUID = "morgnar.valheimtattoos";

		public const string ModName = "ValheimTattoos";

		public const string ModVersion = "1.0.1";

		internal static ManualLogSource Log;

		internal static Harmony Harmony;

		internal static ConfigEntry<bool> CfgHideUnderChestArmor;

		internal static ConfigEntry<bool> CfgHideFaceUnderHelmet;

		internal static ConfigEntry<bool> CfgHideLegsUnderLegArmor;

		internal static ConfigFile Cfg;

		internal static string CustomFolder;

		private void Awake()
		{
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Invalid comparison between Unknown and I4
			//IL_00aa: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b4: Expected O, but got Unknown
			//IL_015b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0165: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			if ((int)SystemInfo.graphicsDeviceType == 4)
			{
				Log.LogInfo((object)"ValheimTattoos: servidor dedicado detectado, mod inativo (é só client-side).");
				return;
			}
			CustomFolder = Path.Combine(Paths.ConfigPath, "ValheimTattoos");
			try
			{
				Directory.CreateDirectory(CustomFolder);
			}
			catch
			{
			}
			string text = Path.Combine(CustomFolder, "morgnar.valheimtattoos.cfg");
			string text2 = Path.Combine(Paths.ConfigPath, "morgnar.valheimtattoos.cfg");
			try
			{
				if (!File.Exists(text) && File.Exists(text2))
				{
					File.Move(text2, text);
				}
			}
			catch (Exception ex)
			{
				Log.LogWarning((object)("Não deu pra mover a config antiga: " + ex.Message));
			}
			Cfg = new ConfigFile(text, true, ((BaseUnityPlugin)this).Info.Metadata);
			CfgHideUnderChestArmor = Cfg.Bind<bool>("Geral", "EsconderTudoComArmaduraDePeito", false, "Esconde as tatuagens de peito, costas e braços com QUALQUER armadura de peito. Desligado (padrão): a tatuagem só some onde a roupa cobre (ex: farrapos deixam os braços à mostra).");
			CfgHideFaceUnderHelmet = Cfg.Bind<bool>("Geral", "EsconderRostoComCapacete", false, "Esconde a tatuagem do rosto quando o personagem usa capacete.");
			CfgHideLegsUnderLegArmor = Cfg.Bind<bool>("Geral", "EsconderPernasComArmaduraDePernas", false, "Esconde as tatuagens das pernas com QUALQUER armadura de pernas. Desligado (padrão): a tatuagem só some onde a roupa cobre.");
			Loc.Init(CustomFolder);
			string text3 = Path.Combine(Path.GetDirectoryName(((BaseUnityPlugin)this).Info.Location) ?? "", "Tattoos");
			TattooLibrary.Load(text3, CustomFolder);
			Harmony = new Harmony("morgnar.valheimtattoos");
			Patches.Apply(Harmony);
			TattooCamera.Init();
			Log.LogInfo((object)string.Format("{0} {1} carregado ({2} tatuagens).", "ValheimTattoos", "1.0.1", TattooLibrary.Count));
		}

		private void OnDestroy()
		{
			Harmony harmony = Harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
			TattooCamera.Shutdown();
		}
	}
	internal static class ShellBuilder
	{
		private class BodyInfo
		{
			public Vector3[] Verts;

			public Vector2[] BodyUV;

			public Vector3[] Normals;

			public BoneWeight[] Weights;

			public int[] Tris;

			public int[] Dominant;

			public Matrix4x4[] BindPoses;

			public Dictionary<string, int> BoneIndex;

			public Dictionary<string, Vector3> Joints;

			public Vector3 Up;

			public Vector3 Left;

			public Vector3 Front;

			public float Height;

			public bool Valid;
		}

		private class SlotDef
		{
			public bool Cylinder;

			public string[] Bones;

			public float Facing;

			public float Size;

			public string Start;

			public string End;

			public string[] LimbBones;

			public float Center = 0.68f;
		}

		private const float PushOut = 0.0015f;

		private static readonly Dictionary<TattooSlot, SlotDef> Defs = new Dictionary<TattooSlot, SlotDef>
		{
			{
				TattooSlot.Face,
				new SlotDef
				{
					Bones = new string[3] { "Head", "Jaw", "Neck" },
					Facing = 1f,
					Size = 0.07f
				}
			},
			{
				TattooSlot.Chest,
				new SlotDef
				{
					Bones = new string[5] { "Spine", "Spine1", "Spine2", "LeftShoulder", "RightShoulder" },
					Facing = 1f,
					Size = 0.13f
				}
			},
			{
				TattooSlot.Back,
				new SlotDef
				{
					Bones = new string[5] { "Spine", "Spine1", "Spine2", "LeftShoulder", "RightShoulder" },
					Facing = -1f,
					Size = 0.16f
				}
			},
			{
				TattooSlot.LeftArm,
				new SlotDef
				{
					Cylinder = true,
					Start = "LeftArm",
					End = "LeftHand",
					LimbBones = new string[3] { "LeftArm", "LeftForeArm", "LeftHand" },
					Size = 0.15f
				}
			},
			{
				TattooSlot.RightArm,
				new SlotDef
				{
					Cylinder = true,
					Start = "RightArm",
					End = "RightHand",
					LimbBones = new string[3] { "RightArm", "RightForeArm", "RightHand" },
					Size = 0.15f
				}
			},
			{
				TattooSlot.LeftLeg,
				new SlotDef
				{
					Cylinder = true,
					Start = "LeftUpLeg",
					End = "LeftFoot",
					LimbBones = new string[2] { "LeftUpLeg", "LeftLeg" },
					Size = 0.16f,
					Center = 0.6f
				}
			},
			{
				TattooSlot.RightLeg,
				new SlotDef
				{
					Cylinder = true,
					Start = "RightUpLeg",
					End = "RightFoot",
					LimbBones = new string[2] { "RightUpLeg", "RightLeg" },
					Size = 0.16f,
					Center = 0.6f
				}
			}
		};

		private static readonly Dictionary<Mesh, BodyInfo> Infos = new Dictionary<Mesh, BodyInfo>();

		private static int _diagBones;

		private static int _diagFacing;

		private static int _diagQuad;

		internal static Mesh Build(SkinnedMeshRenderer body, TattooSlot slot, TattooEntry e)
		{
			//IL_00a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b0: 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_00c0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c5: Unknown result type (might be due to invalid IL or missing references)
			//IL_0321: Unknown result type (might be due to invalid IL or missing references)
			//IL_0326: Unknown result type (might be due to invalid IL or missing references)
			//IL_032f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0334: 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_0337: Unknown result type (might be due to invalid IL or missing references)
			//IL_033c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0340: Unknown result type (might be due to invalid IL or missing references)
			//IL_0345: Unknown result type (might be due to invalid IL or missing references)
			//IL_0347: Unknown result type (might be due to invalid IL or missing references)
			//IL_0349: Unknown result type (might be due to invalid IL or missing references)
			//IL_034e: Unknown result type (might be due to invalid IL or missing references)
			//IL_035a: 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_0362: Unknown result type (might be due to invalid IL or missing references)
			//IL_0367: Unknown result type (might be due to invalid IL or missing references)
			//IL_036e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0373: Unknown result type (might be due to invalid IL or missing references)
			//IL_0378: 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)
			//IL_0383: Unknown result type (might be due to invalid IL or missing references)
			//IL_0385: Unknown result type (might be due to invalid IL or missing references)
			//IL_0387: Unknown result type (might be due to invalid IL or missing references)
			//IL_038c: Unknown result type (might be due to invalid IL or missing references)
			//IL_038e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0391: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00da: Unknown result type (might be due to invalid IL or missing references)
			//IL_03a2: 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_00f3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f8: Unknown result type (might be due to invalid IL or missing references)
			//IL_0102: Unknown result type (might be due to invalid IL or missing references)
			//IL_0107: Unknown result type (might be due to invalid IL or missing references)
			//IL_010c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0154: Unknown result type (might be due to invalid IL or missing references)
			//IL_0156: Unknown result type (might be due to invalid IL or missing references)
			//IL_0167: Unknown result type (might be due to invalid IL or missing references)
			//IL_016c: 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_0182: Unknown result type (might be due to invalid IL or missing references)
			//IL_0187: Unknown result type (might be due to invalid IL or missing references)
			//IL_018c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0116: Unknown result type (might be due to invalid IL or missing references)
			//IL_011b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0125: Unknown result type (might be due to invalid IL or missing references)
			//IL_012a: Unknown result type (might be due to invalid IL or missing references)
			//IL_012f: Unknown result type (might be due to invalid IL or missing references)
			//IL_03cf: Unknown result type (might be due to invalid IL or missing references)
			//IL_03d4: Unknown result type (might be due to invalid IL or missing references)
			//IL_03d6: Unknown result type (might be due to invalid IL or missing references)
			//IL_03db: Unknown result type (might be due to invalid IL or missing references)
			//IL_03dc: Unknown result type (might be due to invalid IL or missing references)
			//IL_03e5: Unknown result type (might be due to invalid IL or missing references)
			//IL_03e9: 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_03f3: Unknown result type (might be due to invalid IL or missing references)
			//IL_0400: Unknown result type (might be due to invalid IL or missing references)
			//IL_0402: Unknown result type (might be due to invalid IL or missing references)
			//IL_0409: Unknown result type (might be due to invalid IL or missing references)
			//IL_040b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0139: Unknown result type (might be due to invalid IL or missing references)
			//IL_013e: 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_014d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0152: Unknown result type (might be due to invalid IL or missing references)
			//IL_019b: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a0: 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_01a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ae: Unknown result type (might be due to invalid IL or missing references)
			//IL_01be: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c0: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d0: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d5: Unknown result type (might be due to invalid IL or missing references)
			//IL_04e2: Unknown result type (might be due to invalid IL or missing references)
			//IL_04e7: Unknown result type (might be due to invalid IL or missing references)
			//IL_0269: Unknown result type (might be due to invalid IL or missing references)
			//IL_026e: Unknown result type (might be due to invalid IL or missing references)
			//IL_028b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0293: Unknown result type (might be due to invalid IL or missing references)
			//IL_029b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0632: Unknown result type (might be due to invalid IL or missing references)
			//IL_0637: Unknown result type (might be due to invalid IL or missing references)
			//IL_063c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0641: Unknown result type (might be due to invalid IL or missing references)
			//IL_0643: Unknown result type (might be due to invalid IL or missing references)
			//IL_0648: Unknown result type (might be due to invalid IL or missing references)
			//IL_064d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0652: Unknown result type (might be due to invalid IL or missing references)
			//IL_06d7: Unknown result type (might be due to invalid IL or missing references)
			//IL_06e1: Unknown result type (might be due to invalid IL or missing references)
			//IL_06fc: Unknown result type (might be due to invalid IL or missing references)
			//IL_070a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0718: Unknown result type (might be due to invalid IL or missing references)
			//IL_05b7: Unknown result type (might be due to invalid IL or missing references)
			//IL_05bf: Unknown result type (might be due to invalid IL or missing references)
			//IL_05c7: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)body == (Object)null || (Object)(object)body.sharedMesh == (Object)null || e == null)
			{
				return null;
			}
			BodyInfo info = GetInfo(body);
			if (info == null || !info.Valid)
			{
				return null;
			}
			SlotDef slotDef = Defs[slot];
			int num = info.Verts.Length;
			Vector2[] array = (Vector2[])(object)new Vector2[num];
			int[] tris = info.Tris;
			List<int> list = new List<int>(tris.Length / 4);
			float num2 = slotDef.Size * info.Height * Mathf.Clamp(e.Scale, 0.4f, 2f);
			if (!slotDef.Cylinder)
			{
				HashSet<int> hashSet = BoneSet(info, slotDef.Bones);
				Vector3 val = info.Front * slotDef.Facing;
				Vector3 val2 = ((slotDef.Facing > 0f) ? info.Left : (-info.Left));
				Vector3 up = info.Up;
				float height = info.Height;
				Vector3 val3 = (Vector3)(slot switch
				{
					TattooSlot.Face => J(info, "Head") + up * (0.06f * height), 
					TattooSlot.Chest => J(info, "Spine2") + up * (0.01f * height), 
					_ => J(info, "Spine2") - up * (0.02f * height), 
				}) + (up * (e.OffsetV * 0.15f * height) + val2 * (e.OffsetH * 0.15f * height));
				for (int i = 0; i < num; i++)
				{
					Vector3 val4 = info.Verts[i] - val3;
					array[i] = new Vector2(Vector3.Dot(val4, val2) / num2 + 0.5f, Vector3.Dot(val4, up) / num2 + 0.5f);
				}
				_diagBones = (_diagFacing = (_diagQuad = 0));
				for (int j = 0; j < tris.Length; j += 3)
				{
					int num3 = tris[j];
					int num4 = tris[j + 1];
					int num5 = tris[j + 2];
					if ((hashSet.Contains(info.Dominant[num3]) ? 1 : 0) + (hashSet.Contains(info.Dominant[num4]) ? 1 : 0) + (hashSet.Contains(info.Dominant[num5]) ? 1 : 0) < 2)
					{
						continue;
					}
					_diagBones++;
					if (!(Vector3.Dot(TriNormal(info, num3, num4, num5), val) <= 0.3f))
					{
						_diagFacing++;
						if (TouchesQuad(array[num3], array[num4], array[num5]))
						{
							_diagQuad++;
							list.Add(num3);
							list.Add(num4);
							list.Add(num5);
						}
					}
				}
			}
			else
			{
				HashSet<int> hashSet2 = BoneSet(info, slotDef.LimbBones);
				if (!info.Joints.ContainsKey(slotDef.Start) || !info.Joints.ContainsKey(slotDef.End))
				{
					return null;
				}
				Vector3 val5 = J(info, slotDef.Start);
				Vector3 val6 = J(info, slotDef.End);
				Vector3 val7 = val6 - val5;
				Vector3 normalized = ((Vector3)(ref val7)).normalized;
				val7 = val6 - val5;
				float magnitude = ((Vector3)(ref val7)).magnitude;
				val7 = info.Front - normalized * Vector3.Dot(info.Front, normalized);
				Vector3 normalized2 = ((Vector3)(ref val7)).normalized;
				Vector3 val8 = Vector3.Cross(normalized, normalized2);
				if (Vector3.Dot(val8, info.Left) < 0f)
				{
					val8 = -val8;
				}
				float[] array2 = new float[num];
				List<float> list2 = new List<float>();
				float[] array3 = new float[num];
				for (int k = 0; k < num; k++)
				{
					Vector3 val9 = info.Verts[k] - val5;
					float num6 = Vector3.Dot(val9, normalized);
					Vector3 val10 = val9 - normalized * num6;
					array3[k] = num6;
					array2[k] = Mathf.Atan2(Vector3.Dot(val10, val8), Vector3.Dot(val10, normalized2));
					if (hashSet2.Contains(info.Dominant[k]))
					{
						list2.Add(((Vector3)(ref val10)).magnitude);
					}
				}
				if (list2.Count == 0)
				{
					return null;
				}
				list2.Sort();
				float num7 = list2[list2.Count / 2];
				float num8 = e.OffsetH * (float)Math.PI;
				float num9 = magnitude * (slotDef.Center - e.OffsetV * 0.35f);
				float[] array4 = new float[num];
				for (int l = 0; l < num; l++)
				{
					array4[l] = Mathf.Repeat(array2[l] - num8 + (float)Math.PI, (float)Math.PI * 2f) - (float)Math.PI;
					array[l] = new Vector2(array4[l] * num7 / num2 + 0.5f, (num9 - array3[l]) / num2 + 0.5f);
				}
				float num10 = (float)Math.PI * 2f / 3f;
				_diagBones = (_diagFacing = (_diagQuad = 0));
				for (int m = 0; m < tris.Length; m += 3)
				{
					int num11 = tris[m];
					int num12 = tris[m + 1];
					int num13 = tris[m + 2];
					if ((hashSet2.Contains(info.Dominant[num11]) ? 1 : 0) + (hashSet2.Contains(info.Dominant[num12]) ? 1 : 0) + (hashSet2.Contains(info.Dominant[num13]) ? 1 : 0) < 2)
					{
						continue;
					}
					_diagBones++;
					if (!(Mathf.Abs(array4[num11]) > num10) && !(Mathf.Abs(array4[num12]) > num10) && !(Mathf.Abs(array4[num13]) > num10))
					{
						_diagFacing++;
						if (TouchesQuad(array[num11], array[num12], array[num13]))
						{
							_diagQuad++;
							list.Add(num11);
							list.Add(num12);
							list.Add(num13);
						}
					}
				}
			}
			Vector2 val11 = default(Vector2);
			((Vector2)(ref val11))..ctor(float.MaxValue, float.MaxValue);
			Vector2 val12 = default(Vector2);
			((Vector2)(ref val12))..ctor(float.MinValue, float.MinValue);
			for (int n = 0; n < num; n++)
			{
				val11 = Vector2.Min(val11, array[n]);
				val12 = Vector2.Max(val12, array[n]);
			}
			Plugin.Log.LogDebug((object)($"[diag] {slot}: tris total {tris.Length / 3}, nos ossos {_diagBones}, virados certo {_diagFacing}, dentro da tattoo {_diagQuad} | " + $"altura corpo {info.Height:0.#####}, tamanho {num2:0.#####}, uv min {val11} max {val12} | " + $"up {info.Up} left {info.Left} front {info.Front} | ossos no corpo {info.BoneIndex.Count}"));
			if (list.Count == 0)
			{
				return null;
			}
			return MakeMesh(info, list, array, slot);
		}

		private static Vector3 TriNormal(BodyInfo info, int a, int b, int c)
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			Vector3 val = info.Normals[a] + info.Normals[b] + info.Normals[c];
			return ((Vector3)(ref val)).normalized;
		}

		private static bool TouchesQuad(Vector2 a, Vector2 b, Vector2 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)
			//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_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0062: Unknown result type (might be due to invalid IL or missing references)
			float num = Mathf.Min(a.x, Mathf.Min(b.x, c.x));
			float num2 = Mathf.Max(a.x, Mathf.Max(b.x, c.x));
			float num3 = Mathf.Min(a.y, Mathf.Min(b.y, c.y));
			float num4 = Mathf.Max(a.y, Mathf.Max(b.y, c.y));
			if (num2 >= 0f && num <= 1f && num4 >= 0f)
			{
				return num3 <= 1f;
			}
			return false;
		}

		private static Mesh MakeMesh(BodyInfo info, List<int> tris, Vector2[] uvAll, TattooSlot slot)
		{
			//IL_00fc: Unknown result type (might be due to invalid IL or missing references)
			//IL_0101: Unknown result type (might be due to invalid IL or missing references)
			//IL_011e: 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_012c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0134: 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_014a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0156: Unknown result type (might be due to invalid IL or missing references)
			//IL_015f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0166: Expected O, but got Unknown
			//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_008c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0091: Unknown result type (might be due to invalid IL or missing references)
			//IL_0095: Unknown result type (might be due to invalid IL or missing references)
			//IL_009a: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d8: Unknown result type (might be due to invalid IL or missing references)
			Dictionary<int, int> dictionary = new Dictionary<int, int>();
			List<Vector3> list = new List<Vector3>();
			List<Vector3> list2 = new List<Vector3>();
			List<Vector2> list3 = new List<Vector2>();
			List<Vector2> list4 = new List<Vector2>();
			List<BoneWeight> list5 = new List<BoneWeight>();
			int[] array = new int[tris.Count];
			float num = 0.0015f * info.Height;
			for (int i = 0; i < tris.Count; i++)
			{
				int num2 = tris[i];
				if (!dictionary.TryGetValue(num2, out var value))
				{
					value = (dictionary[num2] = list.Count);
					Vector3 val = info.Normals[num2];
					list.Add(info.Verts[num2] + val * num);
					list2.Add(val);
					list3.Add(uvAll[num2]);
					list4.Add(info.BodyUV[num2]);
					list5.Add(info.Weights[num2]);
				}
				array[i] = value;
			}
			Mesh val2 = new Mesh
			{
				name = "ValheimTattoo_" + slot
			};
			val2.SetVertices(list);
			val2.SetNormals(list2);
			val2.SetUVs(0, list3);
			val2.SetUVs(1, list4);
			val2.boneWeights = list5.ToArray();
			val2.bindposes = info.BindPoses;
			val2.SetTriangles(array, 0);
			val2.RecalculateBounds();
			return val2;
		}

		private static HashSet<int> BoneSet(BodyInfo info, string[] names)
		{
			HashSet<int> hashSet = new HashSet<int>();
			foreach (string key in names)
			{
				if (info.BoneIndex.TryGetValue(key, out var value))
				{
					hashSet.Add(value);
				}
			}
			return hashSet;
		}

		private static Vector3 J(BodyInfo info, string bone)
		{
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			if (!info.Joints.TryGetValue(bone, out var value))
			{
				return Vector3.zero;
			}
			return value;
		}

		private static BodyInfo GetInfo(SkinnedMeshRenderer body)
		{
			//IL_01c5: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ca: Unknown result type (might be due to invalid IL or missing references)
			//IL_01cf: 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_020e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0213: 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_033c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0341: Unknown result type (might be due to invalid IL or missing references)
			//IL_0346: Unknown result type (might be due to invalid IL or missing references)
			//IL_034a: Unknown result type (might be due to invalid IL or missing references)
			//IL_034f: Unknown result type (might be due to invalid IL or missing references)
			//IL_035c: 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_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_0378: Unknown result type (might be due to invalid IL or missing references)
			//IL_037a: 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_037e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0385: Unknown result type (might be due to invalid IL or missing references)
			//IL_038a: Unknown result type (might be due to invalid IL or missing references)
			//IL_038f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0393: Unknown result type (might be due to invalid IL or missing references)
			//IL_0398: Unknown result type (might be due to invalid IL or missing references)
			//IL_03a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_03b5: Unknown result type (might be due to invalid IL or missing references)
			//IL_03ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_03bf: Unknown result type (might be due to invalid IL or missing references)
			//IL_03c1: 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_03c5: Unknown result type (might be due to invalid IL or missing references)
			//IL_03c7: Unknown result type (might be due to invalid IL or missing references)
			//IL_03ce: Unknown result type (might be due to invalid IL or missing references)
			//IL_03d3: Unknown result type (might be due to invalid IL or missing references)
			//IL_03d8: Unknown result type (might be due to invalid IL or missing references)
			//IL_03da: Unknown result type (might be due to invalid IL or missing references)
			//IL_03dc: Unknown result type (might be due to invalid IL or missing references)
			//IL_03e3: Unknown result type (might be due to invalid IL or missing references)
			//IL_03e8: Unknown result type (might be due to invalid IL or missing references)
			//IL_03ed: Unknown result type (might be due to invalid IL or missing references)
			//IL_03f1: Unknown result type (might be due to invalid IL or missing references)
			//IL_03f6: Unknown result type (might be due to invalid IL or missing references)
			//IL_03f9: Unknown result type (might be due to invalid IL or missing references)
			//IL_03fb: Unknown result type (might be due to invalid IL or missing references)
			//IL_0401: Unknown result type (might be due to invalid IL or missing references)
			//IL_0403: Unknown result type (might be due to invalid IL or missing references)
			//IL_0409: Unknown result type (might be due to invalid IL or missing references)
			//IL_040b: Unknown result type (might be due to invalid IL or missing references)
			//IL_041c: 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_0431: Unknown result type (might be due to invalid IL or missing references)
			//IL_0436: Unknown result type (might be due to invalid IL or missing references)
			Mesh sharedMesh = body.sharedMesh;
			if (Infos.TryGetValue(sharedMesh, out var value))
			{
				return value;
			}
			BodyInfo bodyInfo = new BodyInfo();
			Infos[sharedMesh] = bodyInfo;
			try
			{
				if (!sharedMesh.isReadable)
				{
					Plugin.Log.LogError((object)("Malha do corpo '" + ((Object)sharedMesh).name + "' não é legível, tatuagens desativadas pra ela."));
					return bodyInfo;
				}
				bodyInfo.Verts = sharedMesh.vertices;
				bodyInfo.BodyUV = sharedMesh.uv;
				if (bodyInfo.BodyUV == null || bodyInfo.BodyUV.Length != bodyInfo.Verts.Length)
				{
					bodyInfo.BodyUV = (Vector2[])(object)new Vector2[bodyInfo.Verts.Length];
				}
				bodyInfo.Normals = sharedMesh.normals;
				bodyInfo.Weights = sharedMesh.boneWeights;
				bodyInfo.Tris = sharedMesh.GetTriangles(0);
				bodyInfo.BindPoses = sharedMesh.bindposes;
				Transform[] bones = body.bones;
				if (bodyInfo.Weights == null || bodyInfo.Weights.Length != bodyInfo.Verts.Length || bones == null || bones.Length != bodyInfo.BindPoses.Length)
				{
					Plugin.Log.LogError((object)("Malha do corpo '" + ((Object)sharedMesh).name + "' sem pesos/ossos compatíveis."));
					return bodyInfo;
				}
				if (bodyInfo.Normals == null || bodyInfo.Normals.Length != bodyInfo.Verts.Length)
				{
					sharedMesh = Object.Instantiate<Mesh>(sharedMesh);
					sharedMesh.RecalculateNormals();
					bodyInfo.Normals = sharedMesh.normals;
					Object.Destroy((Object)(object)sharedMesh);
				}
				bodyInfo.BoneIndex = new Dictionary<string, int>();
				bodyInfo.Joints = new Dictionary<string, Vector3>();
				for (int i = 0; i < bones.Length; i++)
				{
					if (!((Object)(object)bones[i] == (Object)null))
					{
						string name = ((Object)bones[i]).name;
						if (!bodyInfo.BoneIndex.ContainsKey(name))
						{
							bodyInfo.BoneIndex[name] = i;
							Dictionary<string, Vector3> joints = bodyInfo.Joints;
							Matrix4x4 inverse = ((Matrix4x4)(ref bodyInfo.BindPoses[i])).inverse;
							joints[name] = Vector4.op_Implicit(((Matrix4x4)(ref inverse)).GetColumn(3));
						}
					}
				}
				bodyInfo.Dominant = new int[bodyInfo.Verts.Length];
				for (int j = 0; j < bodyInfo.Weights.Length; j++)
				{
					BoneWeight val = bodyInfo.Weights[j];
					int num = ((BoneWeight)(ref val)).boneIndex0;
					float num2 = ((BoneWeight)(ref val)).weight0;
					if (((BoneWeight)(ref val)).weight1 > num2)
					{
						num = ((BoneWeight)(ref val)).boneIndex1;
						num2 = ((BoneWeight)(ref val)).weight1;
					}
					if (((BoneWeight)(ref val)).weight2 > num2)
					{
						num = ((BoneWeight)(ref val)).boneIndex2;
						num2 = ((BoneWeight)(ref val)).weight2;
					}
					if (((BoneWeight)(ref val)).weight3 > num2)
					{
						num = ((BoneWeight)(ref val)).boneIndex3;
					}
					bodyInfo.Dominant[j] = num;
				}
				string[] array = new string[7] { "Head", "Hips", "LeftArm", "RightArm", "LeftFoot", "LeftToeBase", "Spine2" };
				foreach (string text in array)
				{
					if (!bodyInfo.Joints.ContainsKey(text))
					{
						Plugin.Log.LogError((object)("Osso '" + text + "' não encontrado no corpo, tatuagens desativadas."));
						return bodyInfo;
					}
				}
				Vector3 val2 = bodyInfo.Joints["Head"] - bodyInfo.Joints["Hips"];
				Vector3 normalized = ((Vector3)(ref val2)).normalized;
				Vector3 val3 = bodyInfo.Joints["LeftArm"] - bodyInfo.Joints["RightArm"];
				val2 = val3 - normalized * Vector3.Dot(val3, normalized);
				val3 = ((Vector3)(ref val2)).normalized;
				Vector3 val4 = bodyInfo.Joints["LeftToeBase"] - bodyInfo.Joints["LeftFoot"];
				val2 = val4 - normalized * Vector3.Dot(val4, normalized) - val3 * Vector3.Dot(val4, val3);
				Vector3 normalized2 = ((Vector3)(ref val2)).normalized;
				bodyInfo.Up = normalized;
				bodyInfo.Left = val3;
				bodyInfo.Front = normalized2;
				bodyInfo.Height = Vector3.Dot(bodyInfo.Joints["Head"] - bodyInfo.Joints["LeftFoot"], normalized) * 1.12f;
				bodyInfo.Valid = bodyInfo.Height > 0f && ((Vector3)(ref normalized2)).sqrMagnitude > 0.5f;
				if (!bodyInfo.Valid)
				{
					Plugin.Log.LogError((object)("Não deu pra calcular os eixos do corpo '" + ((Object)body.sharedMesh).name + "'."));
				}
			}
			catch (Exception arg)
			{
				Plugin.Log.LogError((object)$"Erro analisando o corpo: {arg}");
				bodyInfo.Valid = false;
			}
			return bodyInfo;
		}
	}
	internal static class TattooCamera
	{
		[CompilerGenerated]
		private static class <>O
		{
			public static CameraCallback <0>__OnPreCull;

			public static CameraCallback <1>__OnPostRender;
		}

		internal static float TargetZoom;

		private static float _zoom;

		private static int _lastUpdateFrame = -100;

		private static Camera _applied;

		private static Vector3 _savedPos;

		private static Quaternion _savedRot;

		private static readonly Dictionary<Transform, Dictionary<string, Transform>> BoneCache = new Dictionary<Transform, Dictionary<string, Transform>>();

		internal static void Init()
		{
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: Expected O, but got Unknown
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Expected O, but got Unknown
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_005e: Expected O, but got Unknown
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: Expected O, but got Unknown
			CameraCallback onPreCull = Camera.onPreCull;
			object obj = <>O.<0>__OnPreCull;
			if (obj == null)
			{
				CameraCallback val = OnPreCull;
				<>O.<0>__OnPreCull = val;
				obj = (object)val;
			}
			Camera.onPreCull = (CameraCallback)Delegate.Combine((Delegate?)(object)onPreCull, (Delegate?)obj);
			CameraCallback onPostRender = Camera.onPostRender;
			object obj2 = <>O.<1>__OnPostRender;
			if (obj2 == null)
			{
				CameraCallback val2 = OnPostRender;
				<>O.<1>__OnPostRender = val2;
				obj2 = (object)val2;
			}
			Camera.onPostRender = (CameraCallback)Delegate.Combine((Delegate?)(object)onPostRender, (Delegate?)obj2);
		}

		internal static void Shutdown()
		{
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: Expected O, but got Unknown
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Expected O, but got Unknown
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_005e: Expected O, but got Unknown
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: Expected O, but got Unknown
			CameraCallback onPreCull = Camera.onPreCull;
			object obj = <>O.<0>__OnPreCull;
			if (obj == null)
			{
				CameraCallback val = OnPreCull;
				<>O.<0>__OnPreCull = val;
				obj = (object)val;
			}
			Camera.onPreCull = (CameraCallback)Delegate.Remove((Delegate?)(object)onPreCull, (Delegate?)obj);
			CameraCallback onPostRender = Camera.onPostRender;
			object obj2 = <>O.<1>__OnPostRender;
			if (obj2 == null)
			{
				CameraCallback val2 = OnPostRender;
				<>O.<1>__OnPostRender = val2;
				obj2 = (object)val2;
			}
			Camera.onPostRender = (CameraCallback)Delegate.Remove((Delegate?)(object)onPostRender, (Delegate?)obj2);
		}

		internal static void Tick()
		{
			if (_lastUpdateFrame != Time.frameCount)
			{
				_lastUpdateFrame = Time.frameCount;
				float num = 0f;
				try
				{
					num = ZInput.GetMouseScrollWheel();
				}
				catch
				{
				}
				if (Mathf.Abs(num) > 0.001f)
				{
					TargetZoom = Mathf.Clamp01(TargetZoom + Mathf.Sign(num) * 0.1f);
				}
				_zoom = Mathf.MoveTowards(_zoom, TargetZoom, Time.unscaledDeltaTime * 3f);
			}
		}

		internal static void Reset()
		{
			TargetZoom = 0f;
			_zoom = 0f;
		}

		private static void OnPreCull(Camera cam)
		{
			//IL_0088: Unknown result type (might be due to invalid IL or missing references)
			//IL_008d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0093: 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_00a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00dd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f9: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)_applied != (Object)null || (Object)(object)cam == (Object)null || (Object)(object)cam != (Object)(object)Camera.main)
			{
				return;
			}
			Player editingPlayer = TattooPanel.EditingPlayer;
			if ((Object)(object)editingPlayer == (Object)null || !TattooPanel.IsEditing(editingPlayer))
			{
				if (_zoom > 0f && Time.frameCount - _lastUpdateFrame > 2)
				{
					Reset();
				}
			}
			else
			{
				if (_zoom < 0.001f)
				{
					return;
				}
				try
				{
					if (TryGetFocus(editingPlayer, TattooPanel.CurrentSlot, out var target, out var viewDir, out var dist))
					{
						Transform transform = ((Component)cam).transform;
						_savedPos = transform.position;
						_savedRot = transform.rotation;
						_applied = cam;
						Vector3 val = target + viewDir * dist;
						Quaternion val2 = Quaternion.LookRotation(target - val, Vector3.up);
						float num = Mathf.SmoothStep(0f, 1f, _zoom);
						transform.position = Vector3.Lerp(_savedPos, val, num);
						transform.rotation = Quaternion.Slerp(_savedRot, val2, num);
					}
				}
				catch (Exception ex)
				{
					Plugin.Log.LogDebug((object)("Zoom: " + ex.Message));
					Restore();
				}
			}
		}

		private static void OnPostRender(Camera cam)
		{
			if ((Object)(object)cam == (Object)(object)_applied)
			{
				Restore();
			}
		}

		private static void Restore()
		{
			//IL_0019: 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)
			if (!((Object)(object)_applied == (Object)null))
			{
				Transform transform = ((Component)_applied).transform;
				transform.position = _savedPos;
				transform.rotation = _savedRot;
				_applied = null;
			}
		}

		private static bool TryGetFocus(Player p, TattooSlot slot, out Vector3 target, out Vector3 viewDir, out float dist)
		{
			//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_000c: 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_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			//IL_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_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_008b: Unknown result type (might be due to invalid IL or missing references)
			//IL_008c: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00de: Unknown result type (might be due to invalid IL or missing references)
			//IL_00df: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ea: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ef: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f8: Unknown result type (might be due to invalid IL or missing references)
			//IL_0111: Unknown result type (might be due to invalid IL or missing references)
			//IL_0112: Unknown result type (might be due to invalid IL or missing references)
			//IL_0118: Unknown result type (might be due to invalid IL or missing references)
			//IL_011d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0122: Unknown result type (might be due to invalid IL or missing references)
			//IL_0126: Unknown result type (might be due to invalid IL or missing references)
			//IL_012b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0141: Unknown result type (might be due to invalid IL or missing references)
			//IL_0142: 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_014d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0152: Unknown result type (might be due to invalid IL or missing references)
			//IL_0156: Unknown result type (might be due to invalid IL or missing references)
			//IL_015b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0171: Unknown result type (might be due to invalid IL or missing references)
			//IL_0172: Unknown result type (might be due to invalid IL or missing references)
			//IL_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_0182: Unknown result type (might be due to invalid IL or missing references)
			//IL_0186: Unknown result type (might be due to invalid IL or missing references)
			//IL_018b: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b1: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b6: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e0: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e5: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ef: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f4: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f9: Unknown result type (might be due to invalid IL or missing references)
			//IL_01fd: Unknown result type (might be due to invalid IL or missing references)
			//IL_0202: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c0: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c5: Unknown result type (might be due to invalid IL or missing references)
			//IL_01cf: 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_01d9: Unknown result type (might be due to invalid IL or missing references)
			target = Vector3.zero;
			viewDir = Vector3.forward;
			dist = 1f;
			Vector3 forward = ((Component)p).transform.forward;
			forward.y = 0f;
			if (((Vector3)(ref forward)).sqrMagnitude < 0.01f)
			{
				forward = Vector3.forward;
			}
			((Vector3)(ref forward)).Normalize();
			Vector3 val = Vector3.Cross(Vector3.up, forward);
			string name;
			Vector3 val2;
			switch (slot)
			{
			case TattooSlot.Face:
				name = "Head";
				viewDir = forward;
				dist = 0.55f;
				break;
			case TattooSlot.Chest:
				name = "Spine2";
				viewDir = forward;
				dist = 1f;
				break;
			case TattooSlot.Back:
				name = "Spine2";
				viewDir = -forward;
				dist = 1f;
				break;
			case TattooSlot.LeftArm:
				name = "LeftForeArm";
				val2 = forward - val * 0.6f;
				viewDir = ((Vector3)(ref val2)).normalized;
				dist = 0.9f;
				break;
			case TattooSlot.RightArm:
				name = "RightForeArm";
				val2 = forward + val * 0.6f;
				viewDir = ((Vector3)(ref val2)).normalized;
				dist = 0.9f;
				break;
			case TattooSlot.LeftLeg:
				name = "LeftLeg";
				val2 = forward - val * 0.3f;
				viewDir = ((Vector3)(ref val2)).normalized;
				dist = 1f;
				break;
			case TattooSlot.RightLeg:
				name = "RightLeg";
				val2 = forward + val * 0.3f;
				viewDir = ((Vector3)(ref val2)).normalized;
				dist = 1f;
				break;
			default:
				return false;
			}
			Transform val3 = FindBone(p, name);
			if ((Object)(object)val3 == (Object)null)
			{
				return false;
			}
			target = val3.position;
			if (slot == TattooSlot.Face)
			{
				target += Vector3.up * 0.06f;
			}
			val2 = viewDir + Vector3.up * 0.12f;
			viewDir = ((Vector3)(ref val2)).normalized;
			return true;
		}

		private static Transform FindBone(Player p, string name)
		{
			VisEquipment component = ((Component)p).GetComponent<VisEquipment>();
			SkinnedMeshRenderer val = (((Object)(object)component != (Object)null) ? component.m_bodyModel : null);
			if ((Object)(object)val == (Object)null)
			{
				return null;
			}
			if (!BoneCache.TryGetValue(((Component)p).transform, out var value) || value == null)
			{
				value = new Dictionary<string, Transform>();
				Transform[] bones = val.bones;
				foreach (Transform val2 in bones)
				{
					if ((Object)(object)val2 != (Object)null && !value.ContainsKey(((Object)val2).name))
					{
						value[((Object)val2).name] = val2;
					}
				}
				BoneCache[((Component)p).transform] = value;
				if (BoneCache.Count > 16)
				{
					CleanCache();
				}
			}
			if (!value.TryGetValue(name, out var value2) || !((Object)(object)value2 != (Object)null))
			{
				return null;
			}
			return value2;
		}

		private static void CleanCache()
		{
			List<Transform> list = new List<Transform>();
			foreach (Transform key in BoneCache.Keys)
			{
				if ((Object)(object)key == (Object)null)
				{
					list.Add(key);
				}
			}
			foreach (Transform item in list)
			{
				BoneCache.Remove(item);
			}
		}
	}
	internal enum TattooSlot
	{
		Face,
		Chest,
		Back,
		LeftArm,
		RightArm,
		LeftLeg,
		RightLeg
	}
	internal class TattooEntry
	{
		public string Id;

		public Color Color = new Color(0.08f, 0.09f, 0.12f);

		public float Scale = 1f;

		public float OffsetV;

		public float OffsetH;

		public TattooEntry Clone()
		{
			return (TattooEntry)MemberwiseClone();
		}

		public bool SameShape(TattooEntry o)
		{
			if (o != null && o.Id == Id && Mathf.Approximately(o.Scale, Scale) && Mathf.Approximately(o.OffsetV, OffsetV))
			{
				return Mathf.Approximately(o.OffsetH, OffsetH);
			}
			return false;
		}
	}
	internal class TattooSet
	{
		internal const string CustomDataKey = "ValheimTattoos";

		internal const string ZdoKey = "ValheimTattoos_Data";

		private const string Version = "1";

		internal static readonly TattooSlot[] AllSlots = new TattooSlot[7]
		{
			TattooSlot.Face,
			TattooSlot.Chest,
			TattooSlot.Back,
			TattooSlot.LeftArm,
			TattooSlot.RightArm,
			TattooSlot.LeftLeg,
			TattooSlot.RightLeg
		};

		public readonly Dictionary<TattooSlot, TattooEntry> Entries = new Dictionary<TattooSlot, TattooEntry>();

		internal static bool IsLimb(TattooSlot s)
		{
			if (s != TattooSlot.LeftArm && s != TattooSlot.RightArm && s != TattooSlot.LeftLeg)
			{
				return s == TattooSlot.RightLeg;
			}
			return true;
		}

		internal static bool IsLeg(TattooSlot s)
		{
			if (s != TattooSlot.LeftLeg)
			{
				return s == TattooSlot.RightLeg;
			}
			return true;
		}

		public TattooEntry Get(TattooSlot slot)
		{
			if (!Entries.TryGetValue(slot, out var value))
			{
				return null;
			}
			return value;
		}

		public string Serialize()
		{
			//IL_008a: Unknown result type (might be due to invalid IL or missing references)
			StringBuilder stringBuilder = new StringBuilder("1");
			TattooSlot[] allSlots = AllSlots;
			foreach (TattooSlot tattooSlot in allSlots)
			{
				TattooEntry tattooEntry = Get(tattooSlot);
				if (tattooEntry != null && !string.IsNullOrEmpty(tattooEntry.Id))
				{
					stringBuilder.Append('|').Append(tattooSlot).Append(':')
						.Append(tattooEntry.Id.Replace(":", "").Replace("|", ""))
						.Append(':')
						.Append(ColorUtility.ToHtmlStringRGB(tattooEntry.Color))
						.Append(':')
						.Append(F(tattooEntry.Scale))
						.Append(':')
						.Append(F(tattooEntry.OffsetV))
						.Append(':')
						.Append(F(tattooEntry.OffsetH));
				}
			}
			if (stringBuilder.Length != "1".Length)
			{
				return stringBuilder.ToString();
			}
			return "";
		}

		public static TattooSet Parse(string data)
		{
			//IL_009d: Unknown result type (might be due to invalid IL or missing references)
			//IL_009f: Unknown result type (might be due to invalid IL or missing references)
			TattooSet tattooSet = new TattooSet();
			if (string.IsNullOrEmpty(data))
			{
				return tattooSet;
			}
			try
			{
				string[] array = data.Split(new char[1] { '|' });
				if (array.Length == 0 || array[0] != "1")
				{
					return tattooSet;
				}
				Color color = default(Color);
				for (int i = 1; i < array.Length; i++)
				{
					string[] array2 = array[i].Split(new char[1] { ':' });
					if (array2.Length >= 3 && Enum.TryParse<TattooSlot>(array2[0], out var result))
					{
						TattooEntry tattooEntry = new TattooEntry
						{
							Id = array2[1]
						};
						if (ColorUtility.TryParseHtmlString("#" + array2[2], ref color))
						{
							tattooEntry.Color = color;
						}
						if (array2.Length > 3)
						{
							tattooEntry.Scale = Mathf.Clamp(P(array2[3], 1f), 0.4f, 2f);
						}
						if (array2.Length > 4)
						{
							tattooEntry.OffsetV = Mathf.Clamp(P(array2[4], 0f), -1f, 1f);
						}
						if (array2.Length > 5)
						{
							tattooEntry.OffsetH = Mathf.Clamp(P(array2[5], 0f), -1f, 1f);
						}
						if (!string.IsNullOrEmpty(tattooEntry.Id))
						{
							tattooSet.Entries[result] = tattooEntry;
						}
					}
				}
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("Dados de tatuagem inválidos '" + data + "': " + ex.Message));
			}
			return tattooSet;
		}

		private static string F(float v)
		{
			return v.ToString("0.###", CultureInfo.InvariantCulture);
		}

		private static float P(string s, float def)
		{
			if (!float.TryParse(s, NumberStyles.Float, CultureInfo.InvariantCulture, out var result))
			{
				return def;
			}
			return result;
		}

		internal static string GetPlayerData(Player p)
		{
			if ((Object)(object)p == (Object)null || p.m_customData == null)
			{
				return "";
			}
			if (!p.m_customData.TryGetValue("ValheimTattoos", out var value))
			{
				return "";
			}
			return value ?? "";
		}

		internal static void SetPlayerData(Player p, string data)
		{
			if (!((Object)(object)p == (Object)null) && p.m_customData != null)
			{
				if (string.IsNullOrEmpty(data))
				{
					p.m_customData.Remove("ValheimTattoos");
				}
				else
				{
					p.m_customData["ValheimTattoos"] = data;
				}
			}
		}
	}
	internal static class TattooLibrary
	{
		private static readonly List<string> Ids = new List<string>();

		private const string ResPrefix = "res:";

		private const string ResNamePrefix = "Tattoos.";

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

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

		private static readonly Dictionary<string, Color32[]> Pixels = new Dictionary<string, Color32[]>(StringComparer.OrdinalIgnoreCase);

		private static readonly List<string> PixelsOrder = new List<string>();

		private const int MaxPixelCache = 8;

		private static readonly HashSet<string> Failed = new HashSet<string>(StringComparer.OrdinalIgnoreCase);

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

		private static MethodInfo _loadImage;

		private static bool _loadImageSearched;

		internal static int Count => Ids.Count;

		private static bool LoadImage(Texture2D tex, byte[] data)
		{
			if (!_loadImageSearched)
			{
				_loadImageSearched = true;
				_loadImage = Type.GetType("UnityEngine.ImageConversion, UnityEngine.ImageConversionModule")?.GetMethod("LoadImage", BindingFlags.Static | BindingFlags.Public, null, new Type[3]
				{
					typeof(Texture2D),
					typeof(byte[]),
					typeof(bool)
				}, null);
				if (_loadImage == null)
				{
					Plugin.Log.LogError((object)"ImageConversion.LoadImage não encontrado, não dá pra carregar PNGs.");
				}
			}
			if (_loadImage == null)
			{
				return false;
			}
			return (bool)_loadImage.Invoke(null, new object[3] { tex, data, false });
		}

		internal static string IdAt(int index)
		{
			if (index < 0 || index >= Ids.Count)
			{
				return null;
			}
			return Ids[index];
		}

		internal static int IndexOf(string id)
		{
			if (!string.IsNullOrEmpty(id))
			{
				return Ids.FindIndex((string x) => string.Equals(x, id, StringComparison.OrdinalIgnoreCase));
			}
			return -1;
		}

		internal static void Load(params string[] folders)
		{
			Ids.Clear();
			Files.Clear();
			foreach (string item in (from r in typeof(TattooLibrary).Assembly.GetManifestResourceNames()
				where r.StartsWith("Tattoos.") && r.EndsWith(".png", StringComparison.OrdinalIgnoreCase)
				select r).OrderBy<string, string>((string r) => r, StringComparer.OrdinalIgnoreCase))
			{
				string text = item.Substring("Tattoos.".Length, item.Length - "Tattoos.".Length - 4);
				if (!string.IsNullOrEmpty(text) && !Files.ContainsKey(text))
				{
					Files[text] = "res:" + item;
					Ids.Add(text);
				}
			}
			foreach (string text2 in folders)
			{
				if (string.IsNullOrEmpty(text2) || !Directory.Exists(text2))
				{
					continue;
				}
				foreach (string item2 in Directory.GetFiles(text2, "*.png").OrderBy<string, string>((string f) => f, StringComparer.OrdinalIgnoreCase))
				{
					string text3 = Path.GetFileNameWithoutExtension(item2).Replace(":", "").Replace("|", "");
					if (!string.IsNullOrEmpty(text3) && !Files.ContainsKey(text3))
					{
						Files[text3] = item2;
						Ids.Add(text3);
					}
				}
			}
			if (Ids.Count == 0)
			{
				Plugin.Log.LogWarning((object)"Nenhuma tatuagem encontrada.");
			}
		}

		internal static string DisplayName(string id)
		{
			if (string.IsNullOrEmpty(id))
			{
				return Loc.None;
			}
			int num = IndexOf(id);
			if (id.StartsWith("tattoo_", StringComparison.OrdinalIgnoreCase))
			{
				return Loc.Tattoo + " " + id.Substring(7);
			}
			if (id.StartsWith("paint_", StringComparison.OrdinalIgnoreCase))
			{
				return Loc.Paint + " " + id.Substring(6);
			}
			if (num < 0)
			{
				return id + " (?)";
			}
			return id;
		}

		internal static Texture2D Get(string id)
		{
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_005f: Expected O, but got Unknown
			if (string.IsNullOrEmpty(id))
			{
				return null;
			}
			if (Cache.TryGetValue(id, out var value) && (Object)(object)value != (Object)null)
			{
				return value;
			}
			if (Failed.Contains(id) || !Files.TryGetValue(id, out var value2))
			{
				return null;
			}
			Texture2D val = null;
			try
			{
				val = new Texture2D(2, 2, (TextureFormat)4, true)
				{
					name = "ValheimTattoo_" + id
				};
				if (!LoadImage(val, ReadBytes(value2)))
				{
					Plugin.Log.LogWarning((object)("PNG inválido: " + value2));
					Object.Destroy((Object)(object)val);
					Failed.Add(id);
					return null;
				}
				((Texture)val).wrapMode = (TextureWrapMode)1;
				((Texture)val).filterMode = (FilterMode)2;
				((Texture)val).anisoLevel = 4;
				Color32[] px = ClearBorder(val);
				RememberPixels(id, px);
				Colored[id] = HasColor(px);
				val.Apply(true, false);
				if (((Texture)val).width % 4 == 0 && ((Texture)val).height % 4 == 0)
				{
					try
					{
						val.Compress(true);
					}
					catch
					{
					}
				}
				val.Apply(false, true);
				Cache[id] = val;
				return val;
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("Erro carregando tatuagem '" + value2 + "': " + ex.Message));
				if ((Object)(object)val != (Object)null)
				{
					Object.Destroy((Object)(object)val);
				}
				Failed.Add(id);
				return null;
			}
		}

		private static void RememberPixels(string id, Color32[] px)
		{
			Pixels[id] = px;
			PixelsOrder.Remove(id);
			PixelsOrder.Add(id);
			while (PixelsOrder.Count > 8)
			{
				Pixels.Remove(PixelsOrder[0]);
				PixelsOrder.RemoveAt(0);
			}
		}

		private static Color32[] ReloadPixels(string id)
		{
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Expected O, but got Unknown
			if (!Files.TryGetValue(id, out var value))
			{
				return null;
			}
			Texture2D val = new Texture2D(2, 2, (TextureFormat)4, false);
			try
			{
				if (!LoadImage(val, ReadBytes(value)))
				{
					return null;
				}
				Color32[] array = ClearBorder(val);
				RememberPixels(id, array);
				return array;
			}
			catch
			{
				return null;
			}
			finally
			{
				Object.Destroy((Object)(object)val);
			}
		}

		private static byte[] ReadBytes(string path)
		{
			if (!path.StartsWith("res:"))
			{
				return File.ReadAllBytes(path);
			}
			using Stream stream = typeof(TattooLibrary).Assembly.GetManifestResourceStream(path.Substring("res:".Length));
			if (stream == null)
			{
				throw new FileNotFoundException(path);
			}
			using MemoryStream memoryStream = new MemoryStream();
			stream.CopyTo(memoryStream);
			return memoryStream.ToArray();
		}

		private static Color32[] ClearBorder(Texture2D tex)
		{
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: 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_0066: Unknown result type (might be due to invalid IL or missing references)
			//IL_0067: Unknown result type (might be due to invalid IL or missing references)
			//IL_0075: 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)
			int width = ((Texture)tex).width;
			int height = ((Texture)tex).height;
			Color32[] pixels = tex.GetPixels32();
			if (width >= 4 && height >= 4)
			{
				Color32 val = default(Color32);
				((Color32)(ref val))..ctor(byte.MaxValue, byte.MaxValue, byte.MaxValue, (byte)0);
				for (int i = 0; i < width; i++)
				{
					pixels[i] = val;
					pixels[(height - 1) * width + i] = val;
				}
				for (int j = 0; j < height; j++)
				{
					pixels[j * width] = val;
					pixels[j * width + width - 1] = val;
				}
				tex.SetPixels32(pixels);
			}
			return pixels;
		}

		private static bool HasColor(Color32[] px)
		{
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			int num = 0;
			int num2 = 0;
			for (int i = 0; i < px.Length; i += 3)
			{
				Color32 val = px[i];
				if (val.a >= 128)
				{
					num2++;
					if (Mathf.Min((int)val.r, Mathf.Min((int)val.g, (int)val.b)) < 200)
					{
						num++;
					}
				}
			}
			if (num2 > 0)
			{
				return num > num2 / 10;
			}
			return false;
		}

		internal static bool IsColored(string id)
		{
			if (string.IsNullOrEmpty(id) || (Object)(object)Get(id) == (Object)null)
			{
				return false;
			}
			bool value;
			return Colored.TryGetValue(id, out value) && value;
		}

		internal static Color32[] GetPixels(string id, out int width, out int height)
		{
			width = (height = 0);
			Texture2D val = Get(id);
			if ((Object)(object)val == (Object)null)
			{
				return null;
			}
			if (Pixels.TryGetValue(id, out var value))
			{
				PixelsOrder.Remove(id);
				PixelsOrder.Add(id);
			}
			else
			{
				value = ReloadPixels(id);
			}
			if (value == null)
			{
				return null;
			}
			width = ((Texture)val).width;
			height = ((Texture)val).height;
			if (value.Length != width * height)
			{
				return null;
			}
			return value;
		}
	}
	internal static class TattooPanel
	{
		private class UI
		{
			public RectTransform Root;

			public RectTransform Wood;

			public TMP_Text SlotLabel;

			public TMP_Text TattooLabel;

			public RawImage Preview;

			public readonly Slider[] Sliders = (Slider[])(object)new Slider[7];

			public readonly TMP_Text[] Labels = (TMP_Text[])(object)new TMP_Text[7];
		}

		private class PanelWatcher : MonoBehaviour
		{
			private void LateUpdate()
			{
				if (Time.frameCount - _lastFrame > 2)
				{
					((Component)this).gameObject.SetActive(false);
				}
			}
		}

		private const float PanelWidth = 330f;

		private const float PanelHeight = 504f;

		private const int ZoomIndex = 6;

		private const float GapPx = 10f;

		private static readonly MethodInfo GetPlayerMethod = AccessTools.Method(typeof(PlayerCustomizaton), "GetPlayer", (Type[])null, (Type[])null);

		private static readonly Color[] FillTints = (Color[])(object)new Color[7]
		{
			new Color(0.85f, 0.2f, 0.18f),
			new Color(0.25f, 0.75f, 0.3f),
			new Color(0.25f, 0.45f, 0.9f),
			new Color(0.95f, 0.85f, 0.6f),
			new Color(0.95f, 0.85f, 0.6f),
			new Color(0.95f, 0.85f, 0.6f),
			new Color(0.75f, 0.85f, 0.95f)
		};

		private static readonly Dictionary<PlayerCustomizaton, UI> Cache = new Dictionary<PlayerCustomizaton, UI>();

		private static readonly Vector3[] Corners = (Vector3[])(object)new Vector3[4];

		private static PlayerCustomizaton _pc;

		private static Player _player;

		private static int _lastFrame = -100;

		private static string _snapshot;

		private static TattooSet _set = new TattooSet();

		private static int _slotIndex = 1;

		private static bool _syncing;

		private static TattooSlot Slot => TattooSet.AllSlots[_slotIndex];

		internal static Player EditingPlayer => _player;

		internal static TattooSlot CurrentSlot => Slot;

		internal static bool IsEditing(Player p)
		{
			if ((Object)(object)p != (Object)null && (Object)(object)p == (Object)(object)_player && (Object)(object)_pc != (Object)null)
			{
				return Time.frameCount - _lastFrame <= 2;
			}
			return false;
		}

		private static bool PanelActive(PlayerCustomizaton pc)
		{
			if ((Object)(object)pc == (Object)null)
			{
				return false;
			}
			if ((Object)(object)pc.m_rootPanel != (Object)null)
			{
				return pc.m_rootPanel.activeInHierarchy;
			}
			return ((Behaviour)pc).isActiveAndEnabled;
		}

		private static Player GetPlayer(PlayerCustomizaton pc)
		{
			if ((Object)(object)pc == (Object)null || GetPlayerMethod == null)
			{
				return null;
			}
			try
			{
				object? obj = GetPlayerMethod.Invoke(pc, null);
				return (Player)((obj is Player) ? obj : null);
			}
			catch
			{
				return null;
			}
		}

		internal static void PostUpdate(PlayerCustomizaton pc)
		{
			if (!PanelActive(pc))
			{
				return;
			}
			Player player = GetPlayer(pc);
			if ((Object)(object)player == (Object)null)
			{
				return;
			}
			bool flag = (Object)(object)pc != (Object)(object)_pc || (Object)(object)player != (Object)(object)_player || Time.frameCount - _lastFrame > 2;
			if (flag)
			{
				_pc = pc;
				_player = player;
				TattooCamera.Reset();
				_snapshot = TattooSet.GetPlayerData(player);
				_set = TattooSet.Parse(_snapshot);
				if (_set.Entries.Count > 0 && _set.Get(Slot) == null)
				{
					_slotIndex = Array.IndexOf(TattooSet.AllSlots, _set.Entries.Keys.First());
				}
			}
			_lastFrame = Time.frameCount;
			UI uI = Ensure(pc);
			if (uI != null)
			{
				if (!((Component)uI.Root).gameObject.activeSelf)
				{
					((Component)uI.Root).gameObject.SetActive(true);
				}
				if (flag)
				{
					SyncUI(uI);
				}
				Position(uI);
				TattooCamera.Tick();
				Slider val = uI.Sliders[6];
				if ((Object)(object)val != (Object)null && Mathf.Abs(val.value - TattooCamera.TargetZoom) > 0.001f)
				{
					val.SetValueWithoutNotify(TattooCamera.TargetZoom);
				}
			}
		}

		internal static void OnCancel()
		{
			if ((Object)(object)_player != (Object)null && _snapshot != null)
			{
				TattooSet.SetPlayerData(_player, _snapshot);
				TattooRenderer.Of(_player)?.MarkDirty();
			}
			EndSession();
		}

		internal static void OnApply()
		{
			Player player = _player;
			EndSession();
			TattooRenderer.Of(player)?.MarkDirty();
		}

		private static void EndSession()
		{
			_pc = null;
			_player = null;
			_snapshot = null;
			_lastFrame = -100;
		}

		private static void Commit()
		{
			if (!((Object)(object)_player == (Object)null))
			{
				TattooSet.SetPlayerData(_player, _set.Serialize());
				TattooRenderer.Of(_player)?.MarkDirty();
			}
		}

		private static void ChangeSlot(int dir)
		{
			int num = TattooSet.AllSlots.Length;
			_slotIndex = ((_slotIndex + dir) % num + num) % num;
			if ((Object)(object)_pc != (Object)null && Cache.TryGetValue(_pc, out var value))
			{
				SyncUI(value);
			}
		}

		private static void ChangeTattoo(int dir)
		{
			//IL_00b8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bd: Unknown result type (might be due to invalid IL or missing references)
			//IL_0137: Unknown result type (might be due to invalid IL or missing references)
			//IL_012e: 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)
			int count = TattooLibrary.Count;
			if (count == 0)
			{
				return;
			}
			TattooEntry tattooEntry = _set.Get(Slot);
			int num = ((tattooEntry != null) ? TattooLibrary.IndexOf(tattooEntry.Id) : (-1)) + dir;
			if (num >= count)
			{
				num = -1;
			}
			if (num < -1)
			{
				num = count - 1;
			}
			if (num == -1)
			{
				_set.Entries.Remove(Slot);
			}
			else
			{
				bool flag = tattooEntry == null;
				if (flag)
				{
					tattooEntry = new TattooEntry();
					_set.Entries[Slot] = tattooEntry;
				}
				bool flag2 = !flag && TattooLibrary.IsColored(tattooEntry.Id);
				tattooEntry.Id = TattooLibrary.IdAt(num);
				if (TattooLibrary.IsColored(tattooEntry.Id))
				{
					if (flag || !flag2)
					{
						tattooEntry.Color = Color.white;
					}
				}
				else if (flag || flag2)
				{
					tattooEntry.Color = (from kv in _set.Entries
						where kv.Key != Slot && !TattooLibrary.IsColored(kv.Value.Id)
						select kv.Value).FirstOrDefault()?.Color ?? new TattooEntry().Color;
				}
			}
			Commit();
			if ((Object)(object)_pc != (Object)null && Cache.TryGetValue(_pc, out var value))
			{
				SyncUI(value);
			}
		}

		private static void OnSlider(int index, float value)
		{
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_004c: 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_005d: Unknown result type (might be due to invalid IL or missing references)
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			//IL_006e: Unknown result type (might be due to invalid IL or missing references)
			//IL_006f: Unknown result type (might be due to invalid IL or missing references)
			if (_syncing)
			{
				return;
			}
			TattooEntry tattooEntry = _set.Get(Slot);
			if (tattooEntry != null)
			{
				Color color = tattooEntry.Color;
				switch (index)
				{
				case 0:
					color.r = value;
					tattooEntry.Color = color;
					break;
				case 1:
					color.g = value;
					tattooEntry.Color = color;
					break;
				case 2:
					color.b = value;
					tattooEntry.Color = color;
					break;
				case 3:
					tattooEntry.Scale = value;
					break;
				case 4:
					tattooEntry.OffsetV = value;
					break;
				case 5:
					tattooEntry.OffsetH = value;
					break;
				}
				Commit();
				if ((Object)(object)_pc != (Object)null && Cache.TryGetValue(_pc, out var value2))
				{
					UpdatePreview(value2, tattooEntry);
				}
			}
		}

		private static void SyncUI(UI ui)
		{
			TattooEntry tattooEntry = _set.Get(Slot);
			ui.SlotLabel.text = Loc.SlotName(Slot);
			if (tattooEntry == null)
			{
				ui.TattooLabel.text = Loc.None;
			}
			else
			{
				int num = TattooLibrary.IndexOf(tattooEntry.Id);
				ui.TattooLabel.text = ((num >= 0) ? $"{TattooLibrary.DisplayName(tattooEntry.Id)}  <size=70%>({num + 1}/{TattooLibrary.Count})</size>" : TattooLibrary.DisplayName(tattooEntry.Id));
			}
			string[] array = Loc.SliderNames(Slot);
			float[] array2 = ((tattooEntry == null) ? new float[6] { 0f, 0f, 0f, 1f, 0f, 0f } : new float[6]
			{
				tattooEntry.Color.r,
				tattooEntry.Color.g,
				tattooEntry.Color.b,
				tattooEntry.Scale,
				tattooEntry.OffsetV,
				tattooEntry.OffsetH
			});
			_syncing = true;
			try
			{
				for (int i = 0; i < 6; i++)
				{
					if ((Object)(object)ui.Labels[i] != (Object)null)
					{
						ui.Labels[i].text = array[i];
					}
					if (!((Object)(object)ui.Sliders[i] == (Object)null))
					{
						ui.Sliders[i].SetValueWithoutNotify(array2[i]);
						((Selectable)ui.Sliders[i]).interactable = tattooEntry != null;
					}
				}
			}
			finally
			{
				_syncing = false;
			}
			if ((Object)(object)ui.Labels[6] != (Object)null)
			{
				ui.Labels[6].text = Loc.Zoom;
			}
			if ((Object)(object)ui.Sliders[6] != (Object)null)
			{
				((Selectable)ui.Sliders[6]).interactable = true;
				ui.Sliders[6].SetValueWithoutNotify(TattooCamera.TargetZoom);
			}
			UpdatePreview(ui, tattooEntry);
		}

		private static void UpdatePreview(UI ui, TattooEntry entry)
		{
			//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_005b: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)ui.Preview == (Object)null))
			{
				Texture2D val = ((entry != null) ? TattooLibrary.Get(entry.Id) : null);
				ui.Preview.texture = (Texture)(object)val;
				((Behaviour)ui.Preview).enabled = (Object)(object)val != (Object)null;
				if (entry != null)
				{
					Color color = entry.Color;
					color.a = 1f;
					((Graphic)ui.Preview).color = color;
				}
			}
		}

		private static UI Ensure(PlayerCustomizaton pc)
		{
			if (Cache.TryGetValue(pc, out var value))
			{
				if (value == null || !((Object)(object)value.Root != (Object)null))
				{
					return null;
				}
				return value;
			}
			foreach (PlayerCustomizaton item in Cache.Keys.Where((PlayerCustomizaton k) => (Object)(object)k == (Object)null).ToList())
			{
				Cache.Remove(item);
			}
			Cache[pc] = null;
			try
			{
				value = Build(pc);
				Cache[pc] = value;
				if (value != null)
				{
					Plugin.Log.LogInfo((object)("Painel de tatuagens criado (quadro: " + (((Object)(object)value.Wood != (Object)null) ? ((Object)value.Wood).name : "nenhum") + ")."));
				}
				return value;
			}
			catch (Exception arg)
			{
				Plugin.Log.LogError((object)$"Não deu pra criar o painel de tatuagens: {arg}");
				return null;
			}
		}

		private static UI Build(PlayerCustomizaton pc)
		{
			//IL_00f1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f8: Expected O, but got Unknown
			//IL_0124: Unknown result type (might be due to invalid IL or missing references)
			//IL_012e: Expected O, but got Unknown
			//IL_014d: 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_016c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0222: Unknown result type (might be due to invalid IL or missing references)
			//IL_024c: Unknown result type (might be due to invalid IL or missing references)
			//IL_025b: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d6: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e4: Unknown result type (might be due to invalid IL or missing references)
			//IL_035a: Unknown result type (might be due to invalid IL or missing references)
			//IL_037e: Unknown result type (might be due to invalid IL or missing references)
			//IL_038d: Unknown result type (might be due to invalid IL or missing references)
			//IL_03b7: Unknown result type (might be due to invalid IL or missing references)
			//IL_03d9: Unknown result type (might be due to invalid IL or missing references)
			//IL_03f1: Unknown result type (might be due to invalid IL or missing references)
			//IL_03fd: Unknown result type (might be due to invalid IL or missing references)
			//IL_0413: Unknown result type (might be due to invalid IL or missing references)
			//IL_0429: Unknown result type (might be due to invalid IL or missing references)
			//IL_049d: Unknown result type (might be due to invalid IL or missing references)
			//IL_04ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_0501: Unknown result type (might be due to invalid IL or