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);
}
}
}