Decompiled source of Windstrides Cinematic Female Hair v1.0.5

BepInEx/plugins/CinematicFemaleHair/CinematicFemaleHair.dll

Decompiled a week ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using HarmonyLib;
using Jotunn.Entities;
using Jotunn.Managers;
using Jotunn.Utils;
using MagicaCloth2;
using Newtonsoft.Json;
using UnityEngine;
using UnityEngine.Rendering;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("Windstride")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyDescription("A custom ponytail hairstyle for Valheim with native hair physics.")]
[assembly: AssemblyFileVersion("1.0.5.0")]
[assembly: AssemblyInformationalVersion("1.0.5")]
[assembly: AssemblyProduct("Windstride's Cinematic Female Hair")]
[assembly: AssemblyTitle("CinematicFemaleHair")]
[assembly: AssemblyVersion("1.0.5.0")]
namespace CinematicFemaleHair;

public sealed class HairSimulation : MonoBehaviour
{
	public void Configure(VisEquipment equipment)
	{
		//IL_006e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0073: Unknown result type (might be due to invalid IL or missing references)
		//IL_0085: 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_00ba: 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_01c7: Expected O, but got Unknown
		//IL_01fd: Unknown result type (might be due to invalid IL or missing references)
		//IL_021c: Unknown result type (might be due to invalid IL or missing references)
		//IL_02a7: Unknown result type (might be due to invalid IL or missing references)
		//IL_02b1: Expected O, but got Unknown
		//IL_02b7: Unknown result type (might be due to invalid IL or missing references)
		//IL_02c1: Expected O, but got Unknown
		//IL_0303: Unknown result type (might be due to invalid IL or missing references)
		//IL_030d: Expected O, but got Unknown
		//IL_034f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0359: Expected O, but got Unknown
		//IL_03b3: Unknown result type (might be due to invalid IL or missing references)
		//IL_03bd: Expected O, but got Unknown
		//IL_03c4: Unknown result type (might be due to invalid IL or missing references)
		List<ColliderComponent> list = new List<ColliderComponent>();
		if (equipment.m_clothColliders != null)
		{
			list.AddRange(equipment.m_clothColliders.Where((ColliderComponent c) => Object.op_Implicit((Object)(object)c)));
		}
		HairCollider[] colliders = Plugin.Asset.Colliders;
		foreach (HairCollider hairCollider in colliders)
		{
			GameObject val = new GameObject("CH_Collision_" + hairCollider.Name);
			val.transform.SetParent(((Component)this).transform, false);
			val.transform.localPosition = new Vector3(hairCollider.Center[0], hairCollider.Center[1], hairCollider.Center[2]);
			MagicaCapsuleCollider val2 = val.AddComponent<MagicaCapsuleCollider>();
			val2.direction = (Direction)1;
			val2.alignedOnCenter = true;
			val2.SetSize(hairCollider.Radius, hairCollider.Radius, hairCollider.Height);
			list.Add((ColliderComponent)(object)val2);
		}
		Player component = ((Component)equipment).GetComponent<Player>();
		int completed = 0;
		int expected = Plugin.Asset.Parts.Count((HairPart p) => p.Simulated && Plugin.PhysicsEnabled.Value);
		HairPart[] parts = Plugin.Asset.Parts;
		foreach (HairPart part in parts)
		{
			MeshRenderer component2 = ((Component)((Component)this).transform.Find(part.Name)).GetComponent<MeshRenderer>();
			if (!part.Simulated || !Plugin.PhysicsEnabled.Value)
			{
				continue;
			}
			GameObject val3 = new GameObject(part.Name + "_Cloth");
			val3.transform.SetParent(((Component)this).transform, false);
			MagicaCloth val4 = val3.AddComponent<MagicaCloth>();
			ClothSerializeData serializeData = val4.SerializeData;
			serializeData.Import(Plugin.Donor.SerializeData, false);
			serializeData.clothType = (ClothType)0;
			serializeData.sourceRenderers.Clear();
			serializeData.sourceRenderers.Add((Renderer)(object)component2);
			serializeData.paintMode = (PaintMode)0;
			serializeData.paintMaps.Clear();
			VertexAttribute[] item = part.Attributes.Select((int a) => (a != 0) ? VertexAttribute.Move : VertexAttribute.Fixed).ToArray();
			ClothSerializeData2 serializeData2 = val4.GetSerializeData2();
			serializeData2.vertexAttributeList.Clear();
			serializeData2.vertexAttributeList.Add(item);
			serializeData.reductionSetting.simpleDistance = 0f;
			serializeData.reductionSetting.shapeDistance = 0f;
			serializeData.radius = new CurveSerializeData(0.0035f);
			serializeData.damping = new CurveSerializeData(0.14f);
			serializeData.angleRestorationConstraint.useAngleRestoration = true;
			serializeData.angleRestorationConstraint.stiffness = new CurveSerializeData(1f, MakeCurve(part, (float s) => Mathf.Lerp(0.38f, 0.045f, s)));
			serializeData.angleLimitConstraint.useAngleLimit = true;
			serializeData.angleLimitConstraint.limitAngle = new CurveSerializeData(1f, MakeCurve(part, (float s) => Mathf.Lerp(2f, 48f, s)));
			serializeData.motionConstraint.useMaxDistance = true;
			serializeData.motionConstraint.maxDistance = new CurveSerializeData(part.Length * 0.55f * Plugin.MotionScale.Value, MakeCurve(part, (float s) => s));
			serializeData.colliderCollisionConstraint.mode = (Mode)1;
			serializeData.colliderCollisionConstraint.colliderList = list;
			val4.Initialize();
			val4.DisableAutoBuild();
			if (Object.op_Implicit((Object)(object)component))
			{
				val3.AddComponent<PlayerClothWindShelter>().SetPlayer(component);
			}
			val4.OnBuildComplete = (Action<MagicaCloth, bool>)Delegate.Combine(val4.OnBuildComplete, (Action<MagicaCloth, bool>)delegate(MagicaCloth val5, bool success)
			{
				//IL_0006: Unknown result type (might be due to invalid IL or missing references)
				//IL_000b: Unknown result type (might be due to invalid IL or missing references)
				//IL_0040: 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)
				ResultCode result = val5.Process.Result;
				if (!success && ((ResultCode)(ref result)).IsError())
				{
					Plugin.Error("Hair physics build failed for " + part.Name + ": " + ((object)((ResultCode)(ref result)).Result/*cast due to .constrained prefix*/).ToString() + " " + ((ResultCode)(ref result)).GetResultInformation());
				}
				if (success)
				{
					int num2 = completed + 1;
					completed = num2;
					if (num2 == expected)
					{
						Plugin.LogInfo("Hair physics ready: " + completed + " regions.");
					}
				}
			});
			if (!val4.BuildAndRun())
			{
				throw new InvalidOperationException("Build did not start: " + part.Name);
			}
		}
		Plugin.Debug("Attached Windstride's Cinematic Female Hair.");
	}

	private static AnimationCurve MakeCurve(HairPart part, Func<float, float> convert)
	{
		//IL_002b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0030: Unknown result type (might be due to invalid IL or missing references)
		//IL_0040: Unknown result type (might be due to invalid IL or missing references)
		//IL_0046: Expected O, but got Unknown
		Keyframe[] array = (Keyframe[])(object)new Keyframe[part.FlexCurve.Length];
		for (int i = 0; i < array.Length; i++)
		{
			array[i] = new Keyframe((float)i / (float)(array.Length - 1), convert(part.FlexCurve[i]));
		}
		return new AnimationCurve(array);
	}
}
[BepInPlugin("brunoluccas.valheim.cinematichair", "Windstride's Cinematic Female Hair", "1.0.5")]
[BepInDependency("com.jotunn.jotunn", "2.30.1")]
[NetworkCompatibility(/*Could not decode attribute arguments.*/)]
public sealed class Plugin : BaseUnityPlugin
{
	[HarmonyPatch(typeof(VisEquipment), "SetHairEquipped")]
	private static class AttachPhysics
	{
		private static void Postfix(VisEquipment __instance, int __0, bool __result, GameObject ___m_hairItemInstance)
		{
			if (!__result || __0 != StringExtensionMethods.GetStableHashCode("HairCinematicPonytail") || !Object.op_Implicit((Object)(object)___m_hairItemInstance) || Object.op_Implicit((Object)(object)___m_hairItemInstance.GetComponent<HairSimulation>()))
			{
				return;
			}
			try
			{
				___m_hairItemInstance.AddComponent<HairSimulation>().Configure(__instance);
			}
			catch (Exception ex)
			{
				((BaseUnityPlugin)Instance).Logger.LogError((object)("Custom hair simulation failed: " + ex));
			}
		}
	}

	public const string Guid = "brunoluccas.valheim.cinematichair";

	public const string HairName = "HairCinematicPonytail";

	internal static Plugin Instance;

	internal static HairAsset Asset;

	internal static MagicaCloth Donor;

	internal static ConfigEntry<bool> PhysicsEnabled;

	internal static ConfigEntry<float> MotionScale;

	internal static string ModDirectory;

	private Harmony harmony;

	private bool registered;

	private void Awake()
	{
		//IL_0006: Unknown result type (might be due to invalid IL or missing references)
		//IL_000c: Invalid comparison between Unknown and I4
		//IL_0082: Unknown result type (might be due to invalid IL or missing references)
		//IL_008c: Expected O, but got Unknown
		//IL_00f8: Unknown result type (might be due to invalid IL or missing references)
		//IL_0102: Expected O, but got Unknown
		Instance = this;
		if ((int)SystemInfo.graphicsDeviceType == 4)
		{
			((BaseUnityPlugin)this).Logger.LogInfo((object)"Windstride's Cinematic Female Hair is a client visual mod; skipping the dedicated server.");
			return;
		}
		ModDirectory = Path.GetDirectoryName(((BaseUnityPlugin)this).Info.Location);
		PhysicsEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("Simulation", "Enabled", true, "Use the game's MagicaCloth hair simulation.");
		MotionScale = ((BaseUnityPlugin)this).Config.Bind<float>("Simulation", "MotionScale", 1f, new ConfigDescription("Overall motion multiplier; 1 follows the authored reference.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.1f, 2f), Array.Empty<object>()));
		Asset = JsonConvert.DeserializeObject<HairAsset>(File.ReadAllText(Path.Combine(ModDirectory, "assets", "Hair_Mesh.json")));
		if (Asset == null || Asset.Parts == null || Asset.Parts.Length != 8)
		{
			throw new InvalidDataException("Hair_Mesh.json is missing or invalid.");
		}
		PrefabManager.OnVanillaPrefabsAvailable += Register;
		harmony = new Harmony("brunoluccas.valheim.cinematichair");
		harmony.PatchAll(typeof(Plugin).Assembly);
		((BaseUnityPlugin)this).Logger.LogInfo((object)"Windstride's Cinematic Female Hair ready.");
	}

	private void Register()
	{
		//IL_009d: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a2: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ae: Expected O, but got Unknown
		//IL_00de: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e4: Expected O, but got Unknown
		//IL_0127: Unknown result type (might be due to invalid IL or missing references)
		//IL_012e: Expected O, but got Unknown
		//IL_0160: Unknown result type (might be due to invalid IL or missing references)
		//IL_0165: Unknown result type (might be due to invalid IL or missing references)
		//IL_0178: Unknown result type (might be due to invalid IL or missing references)
		//IL_01f3: Unknown result type (might be due to invalid IL or missing references)
		//IL_01fa: Expected O, but got Unknown
		if (registered)
		{
			return;
		}
		try
		{
			GameObject prefab = PrefabManager.Instance.GetPrefab("Hair2");
			if (!Object.op_Implicit((Object)(object)prefab))
			{
				throw new InvalidOperationException("Native Hair2 is not loaded.");
			}
			Donor = prefab.GetComponentInChildren<MagicaCloth>(true);
			if (!Object.op_Implicit((Object)(object)Donor))
			{
				throw new InvalidOperationException("Native MeshCloth donor missing.");
			}
			Material val = new Material((from r in prefab.GetComponentsInChildren<Renderer>(true)
				select r.sharedMaterial).First((Material m) => Object.op_Implicit((Object)(object)m) && m.HasProperty("_SkinBumpMap")))
			{
				name = "CinematicFemaleHair_PlayerHair"
			};
			val.SetTexture("_MainTex", (Texture)(object)LoadTexture("Hair_Color.png", linear: false));
			val.SetTexture("_SkinBumpMap", (Texture)(object)LoadPackedNormal());
			CustomItem val2 = new CustomItem("HairCinematicPonytail", "Hair2");
			GameObject itemPrefab = val2.ItemPrefab;
			if (!Object.op_Implicit((Object)(object)itemPrefab))
			{
				throw new InvalidOperationException("Could not clone customization item.");
			}
			Transform val3 = itemPrefab.transform.Find("attach");
			if (Object.op_Implicit((Object)(object)val3))
			{
				Object.DestroyImmediate((Object)(object)((Component)val3).gameObject);
			}
			GameObject val4 = new GameObject("attach");
			val4.transform.SetParent(itemPrefab.transform, false);
			HairPart[] parts = Asset.Parts;
			foreach (HairPart hairPart in parts)
			{
				GameObject val5 = new GameObject(hairPart.Name);
				val5.transform.SetParent(val4.transform, false);
				val5.AddComponent<MeshFilter>().sharedMesh = CreateMesh(hairPart);
				MeshRenderer obj = val5.AddComponent<MeshRenderer>();
				((Renderer)obj).sharedMaterial = val;
				((Renderer)obj).shadowCastingMode = (ShadowCastingMode)1;
				((Renderer)obj).receiveShadows = true;
			}
			SharedData shared = val2.ItemDrop.m_itemData.m_shared;
			shared.m_name = "$hair_cinematic_ponytail";
			shared.m_description = "$hair_cinematic_ponytail_description";
			shared.m_toolTier = 0;
			shared.m_helmetHairSettings = new List<HelmetHairSettings>();
			CustomLocalization val6 = new CustomLocalization(((BaseUnityPlugin)this).Info.Metadata);
			string text = "English";
			val6.AddTranslation(ref text, new Dictionary<string, string>
			{
				{ "hair_cinematic_ponytail", "Half up Ponytail" },
				{ "hair_cinematic_ponytail_description", "Swept hair with loose strands and a short ponytail." }
			});
			LocalizationManager.Instance.AddLocalization(val6);
			if (!ItemManager.Instance.AddItem(val2))
			{
				throw new InvalidOperationException("Jotunn rejected the hairstyle.");
			}
			registered = true;
			((BaseUnityPlugin)this).Logger.LogInfo((object)"Registered Windstride's Cinematic Female Hair.");
			PrefabManager.OnVanillaPrefabsAvailable -= Register;
		}
		catch (Exception ex)
		{
			((BaseUnityPlugin)this).Logger.LogError((object)("Hair registration failed: " + ex));
		}
	}

	private static Mesh CreateMesh(HairPart part)
	{
		//IL_0000: Unknown result type (might be due to invalid IL or missing references)
		//IL_0005: Unknown result type (might be due to invalid IL or missing references)
		//IL_0012: Expected O, but got Unknown
		//IL_0060: Unknown result type (might be due to invalid IL or missing references)
		//IL_0065: Unknown result type (might be due to invalid IL or missing references)
		Mesh val = new Mesh
		{
			name = part.Name
		};
		val.vertices = Vectors(part.Vertices);
		val.normals = Vectors(part.Normals);
		Vector2[] array = (Vector2[])(object)new Vector2[part.Uv.Length / 2];
		for (int i = 0; i < array.Length; i++)
		{
			array[i] = new Vector2(part.Uv[i * 2], part.Uv[i * 2 + 1]);
		}
		val.uv = array;
		val.triangles = part.Triangles;
		val.RecalculateTangents();
		val.RecalculateBounds();
		return val;
	}

	private static Vector3[] Vectors(float[] values)
	{
		//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)
		Vector3[] array = (Vector3[])(object)new Vector3[values.Length / 3];
		for (int i = 0; i < array.Length; i++)
		{
			array[i] = new Vector3(values[i * 3], values[i * 3 + 1], values[i * 3 + 2]);
		}
		return array;
	}

	private static Texture2D LoadTexture(string filename, bool linear)
	{
		//IL_0005: Unknown result type (might be due to invalid IL or missing references)
		//IL_000a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0011: 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_001f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0027: Expected O, but got Unknown
		Texture2D val = new Texture2D(2, 2, (TextureFormat)4, true, linear)
		{
			name = filename,
			wrapMode = (TextureWrapMode)0,
			filterMode = (FilterMode)0,
			anisoLevel = 1
		};
		if (!ImageConversion.LoadImage(val, File.ReadAllBytes(Path.Combine(ModDirectory, "assets", filename)), false))
		{
			throw new InvalidDataException(filename);
		}
		if (((Texture)val).width != ((Texture)val).height || (((Texture)val).width != 64 && ((Texture)val).width != 128))
		{
			throw new InvalidDataException(filename + " must be 64x64 or 128x128.");
		}
		val.Apply(true, false);
		return val;
	}

	private static Texture2D LoadPackedNormal()
	{
		//IL_0042: Unknown result type (might be due to invalid IL or missing references)
		//IL_0047: Unknown result type (might be due to invalid IL or missing references)
		Texture2D val = LoadTexture("Hair_Normal.png", linear: true);
		Color32[] pixels = val.GetPixels32();
		for (int i = 0; i < pixels.Length; i++)
		{
			pixels[i] = new Color32(byte.MaxValue, pixels[i].g, pixels[i].b, pixels[i].r);
		}
		val.SetPixels32(pixels);
		val.Apply(true, false);
		((Object)val).name = "CinematicHair_Normal_AG";
		return val;
	}

	internal static void Debug(string message)
	{
		((BaseUnityPlugin)Instance).Logger.LogDebug((object)message);
	}

	internal static void LogInfo(string message)
	{
		((BaseUnityPlugin)Instance).Logger.LogInfo((object)message);
	}

	internal static void Error(string message)
	{
		((BaseUnityPlugin)Instance).Logger.LogError((object)message);
	}

	private void OnDestroy()
	{
		PrefabManager.OnVanillaPrefabsAvailable -= Register;
		Harmony obj = harmony;
		if (obj != null)
		{
			obj.UnpatchSelf();
		}
	}
}
[Serializable]
public sealed class HairAsset
{
	public string Name;

	public HairPart[] Parts;

	public HairCollider[] Colliders;
}
[Serializable]
public sealed class HairPart
{
	public string Name;

	public float[] Vertices;

	public float[] Normals;

	public float[] Uv;

	public float[] FlexCurve;

	public int[] Triangles;

	public int[] Attributes;

	public float Length;

	public bool Simulated;
}
[Serializable]
public sealed class HairCollider
{
	public string Name;

	public float[] Center;

	public float Radius;

	public float Height;
}