Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of ValheimTattoos v1.0.1
plugins/ValheimTattoos.dll
Decompiled 9 hours ago
The result has been truncated due to the large size, download it to view full contents!
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