Decompiled source of VRMCompanions v0.2.0

BepInEx/plugins/VRMCompanions/VRMCompanions.dll

Decompiled 2 months ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Logging;
using HarmonyLib;
using UnityEngine;
using ValheimVRM;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: AssemblyTitle("VRMCompanions")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("VRMCompanions")]
[assembly: AssemblyCopyright("Copyright ©  2026")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("8aaf6e91-f870-4b1f-a224-b3b681dbbf1f")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyVersion("1.0.0.0")]
[BepInPlugin("com.vrm.companions", "VRM Companions", "1.1.1")]
public class VRMCompanionsPlugin : BaseUnityPlugin
{
	internal static ManualLogSource Log;

	private void Awake()
	{
		Log = ((BaseUnityPlugin)this).Logger;
		Harmony.CreateAndPatchAll(typeof(CompanionPatch), (string)null);
		Harmony.CreateAndPatchAll(typeof(HideArmorPatch), (string)null);
		Log.LogInfo((object)"VRM Companions loaded");
	}
}
[HarmonyPatch(typeof(VisEquipment), "UpdateEquipmentVisuals")]
public class HideArmorPatch
{
	private static void Postfix(VisEquipment __instance)
	{
		Humanoid component = ((Component)__instance).GetComponent<Humanoid>();
		if (!((Object)(object)component == (Object)null) && ((Object)component).name.Contains("HC_Companion"))
		{
			CompanionPatch.ApplyArmorHide(component);
		}
	}
}
[HarmonyPatch(typeof(Humanoid), "Start")]
public class CompanionPatch
{
	public class VRMMarker : MonoBehaviour
	{
	}

	private static void Postfix(Humanoid __instance)
	{
		if (!((Object)(object)__instance == (Object)null) && ((Object)__instance).name.Contains("HC_Companion"))
		{
			LogSafe("✔ Companion detected: " + ((Object)__instance).name);
			((MonoBehaviour)__instance).StartCoroutine(ReplaceVRM(__instance));
		}
	}

	private static IEnumerator ReplaceVRM(Humanoid humanoid)
	{
		yield return (object)new WaitForSeconds(0.5f);
		VisEquipment vis = ((Component)humanoid).GetComponent<VisEquipment>();
		if ((Object)(object)vis == (Object)null)
		{
			LogSafe("❌ VisEquipment missing");
			yield break;
		}
		string companionName = null;
		float timeout = 3f;
		float timer = 0f;
		while (timer < timeout)
		{
			Character c2 = (Character)(object)humanoid;
			if (c2 != null && !string.IsNullOrWhiteSpace(c2.m_name) && c2.m_name != "Companion")
			{
				companionName = c2.m_name;
				LogSafe("✔ Name resolved from Character.m_name: " + companionName);
				break;
			}
			timer += 0.2f;
			yield return (object)new WaitForSeconds(0.2f);
		}
		if (string.IsNullOrWhiteSpace(companionName))
		{
			ZNetView nview = ((Component)humanoid).GetComponent<ZNetView>();
			if ((Object)(object)nview != (Object)null && nview.IsValid())
			{
				string zdoName = nview.GetZDO().GetString("name", "");
				if (!string.IsNullOrWhiteSpace(zdoName) && zdoName != "Companion")
				{
					companionName = zdoName;
					LogSafe("✔ Name from ZDO: " + companionName);
				}
			}
		}
		if (string.IsNullOrWhiteSpace(companionName) || companionName == "Companion")
		{
			LogSafe("❌ Failed to resolve unique companion name, using fallback");
			companionName = "Companion";
		}
		Character dbgChar = (Character)(object)humanoid;
		if (dbgChar != null)
		{
			LogSafe("DEBUG final m_name: " + dbgChar.m_name);
		}
		ZNetView dbgNview = ((Component)humanoid).GetComponent<ZNetView>();
		if ((Object)(object)dbgNview != (Object)null && dbgNview.IsValid())
		{
			LogSafe("DEBUG ZDO name: " + dbgNview.GetZDO().GetString("name", "NULL"));
		}
		char[] invalidFileNameChars = Path.GetInvalidFileNameChars();
		foreach (char c in invalidFileNameChars)
		{
			companionName = companionName.Replace(c.ToString(), "");
		}
		string folder = Path.Combine(Paths.GameRootPath, "ValheimVRM");
		if (!Directory.Exists(folder))
		{
			LogSafe("❌ VRM folder missing: " + folder);
			yield break;
		}
		string path = null;
		string[] files = Directory.GetFiles(folder, "*.vrm");
		foreach (string file in files)
		{
			if (Path.GetFileNameWithoutExtension(file).Equals(companionName, StringComparison.OrdinalIgnoreCase))
			{
				path = file;
				break;
			}
		}
		if (path == null)
		{
			LogSafe("❌ No VRM found for '" + companionName + "', trying fallback");
			string fallback = Path.Combine(folder, "Companion.vrm");
			if (!File.Exists(fallback))
			{
				LogSafe("❌ Fallback VRM missing (Companion.vrm)");
				yield break;
			}
			path = fallback;
		}
		LogSafe("✔ Using VRM: " + path);
		Dictionary<string, string> cfg = LoadSettingsFile(path);
		byte[] data = File.ReadAllBytes(path);
		bool done = false;
		yield return VRM.ImportVisualAsync(data, path, 1f, (Action<GameObject>)delegate(GameObject loaded)
		{
			//IL_00e8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ee: 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_0143: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)loaded == (Object)null)
			{
				LogSafe("❌ VRM load failed");
				done = true;
			}
			else
			{
				VisEquipment component = ((Component)humanoid).GetComponent<VisEquipment>();
				if ((Object)(object)component != (Object)null && (Object)(object)component.m_bodyModel != (Object)null)
				{
					((Renderer)component.m_bodyModel).enabled = false;
					LogSafe("✔ Vanilla body hidden");
				}
				float result = 1f;
				float result2 = 1f;
				float result3 = 1f;
				if (cfg.TryGetValue("ModelScale", out var value))
				{
					float.TryParse(value, out result);
				}
				if (cfg.TryGetValue("SpringBoneStiffness", out var value2))
				{
					float.TryParse(value2, out result2);
				}
				if (cfg.TryGetValue("SpringBoneGravityPower", out var value3))
				{
					float.TryParse(value3, out result3);
				}
				loaded.transform.localScale = Vector3.one * result;
				Animator componentInChildren = ((Component)humanoid).GetComponentInChildren<Animator>();
				VrmSettingsContainer settings = Settings.GetSettings(companionName);
				loaded.transform.SetParent(((Component)componentInChildren).transform.parent, false);
				loaded.transform.localPosition = Vector3.zero;
				loaded.transform.localRotation = Quaternion.identity;
				loaded.AddComponent<VRMMarker>();
				LogSafe("✔ VRM attached to companion");
				VRMAnimationSync val = loaded.GetComponent<VRMAnimationSync>();
				if ((Object)(object)val == (Object)null)
				{
					val = loaded.AddComponent<VRMAnimationSync>();
				}
				val.Setup(componentInChildren, settings, false);
				componentInChildren.cullingMode = (AnimatorCullingMode)0;
				componentInChildren.updateMode = (AnimatorUpdateMode)0;
				ForceSpringBoneUpdate(loaded, result2, result3);
				ApplyArmorHide(humanoid);
				done = true;
			}
		});
		while (!done)
		{
			yield return null;
		}
	}

	public static void ApplyArmorHide(Humanoid humanoid)
	{
		VisEquipment component = ((Component)humanoid).GetComponent<VisEquipment>();
		if ((Object)(object)component != (Object)null)
		{
			component.SetHelmetItem("");
			component.SetShoulderItem("", 0);
			component.SetChestItem("");
			component.SetLegItem("");
			component.SetUtilityItem("");
			component.SetHairItem("");
			component.SetBeardItem("");
		}
		Renderer[] componentsInChildren = ((Component)humanoid).GetComponentsInChildren<Renderer>(true);
		foreach (Renderer val in componentsInChildren)
		{
			if (!((Object)(object)val == (Object)null) && !((Object)(object)((Component)val).GetComponentInParent<VRMMarker>() != (Object)null))
			{
				string text = ((Object)((Component)val).gameObject).name.ToLower();
				if (text.Contains("armor") || text.Contains("helmet") || text.Contains("helm") || text.Contains("chest") || text.Contains("shoulder") || text.Contains("cape") || text.Contains("leg"))
				{
					val.enabled = false;
				}
			}
		}
	}

	private static void ForceSpringBoneUpdate(GameObject vrmRoot, float springStiffness, float springGravity)
	{
		MonoBehaviour[] componentsInChildren = vrmRoot.GetComponentsInChildren<MonoBehaviour>(true);
		foreach (MonoBehaviour val in componentsInChildren)
		{
			Type type = ((object)val).GetType();
			if (type.Name.Contains("VRMSpringBone"))
			{
				FieldInfo field = type.GetField("m_stiffnessForce");
				FieldInfo field2 = type.GetField("m_gravityPower");
				if (field != null)
				{
					float num = (float)field.GetValue(val);
					field.SetValue(val, num * Mathf.Clamp(springStiffness, 0.1f, 5f));
				}
				if (field2 != null)
				{
					float num2 = (float)field2.GetValue(val);
					field2.SetValue(val, num2 * Mathf.Clamp(springGravity, 0.1f, 5f));
				}
			}
		}
	}

	private static Dictionary<string, string> LoadSettingsFile(string vrmPath)
	{
		string text = Path.Combine(Path.GetDirectoryName(vrmPath), "settings_" + Path.GetFileNameWithoutExtension(vrmPath) + ".txt");
		Dictionary<string, string> dictionary = new Dictionary<string, string>();
		if (!File.Exists(text))
		{
			return dictionary;
		}
		string[] array = File.ReadAllLines(text);
		foreach (string text2 in array)
		{
			if (!string.IsNullOrWhiteSpace(text2) && !text2.StartsWith("//") && text2.Contains("="))
			{
				string[] array2 = text2.Split(new char[1] { '=' });
				if (array2.Length == 2)
				{
					dictionary[array2[0].Trim()] = array2[1].Trim();
				}
			}
		}
		LogSafe("✔ Loaded VRM settings: " + text);
		return dictionary;
	}

	private static void LogSafe(string msg)
	{
		if (VRMCompanionsPlugin.Log != null)
		{
			VRMCompanionsPlugin.Log.LogInfo((object)msg);
		}
		else
		{
			Debug.Log((object)msg);
		}
	}
}