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 VRM Fixes v1.1.5
plugins/NordicGlutePhysics.dll
Decompiled 2 months agousing System; using System.Collections.Generic; using System.Diagnostics; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using LocalValheimFixes; using Microsoft.CodeAnalysis; using UniGLTF; using UniGLTF.Utils; using UniVRM10.FastSpringBones.System; using UnityEngine; [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("NordicGlutePhysics")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("0.1.0.0")] [assembly: AssemblyInformationalVersion("0.1.0+3356aed1f6508684669959e6a326f48d5ef50221")] [assembly: AssemblyProduct("NordicGlutePhysics")] [assembly: AssemblyTitle("NordicGlutePhysics")] [assembly: AssemblyVersion("0.1.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 NordicGlutePhysics { public struct GluteFrame { public D3 P; public M3 R; public double S; public GluteFrame(D3 p, M3 r, double s) { P = p; R = r; S = s; } } public sealed class GluteParams { public double[] Freq; public double[] Zeta; public double[] LimPos; public double[] LimNeg; public double Inertia = 1.0; public double Twist = 2.0; public double HpHz = 0.8; public double Steady = 0.2; public double EnvK = 1.5; public double EnvAttack = 0.25; public double EnvRelease = 1.5; public double DriveHz = 3.0; public double Gravity = 0.1; public double Amount = 1.0; public double ThighDrive = 0.3; public double PivotUp = 0.06; public double PivotIn = 0.08; public double RotShare = 1.0; public double MaxTurnDeg = 25.0; public double FilterHz = 12.0; public double MaxAccel = 60.0; public double MaxSpeed = 1.2; public double Substep = 1.0 / 240.0; public double SoftStart = 0.7; public double SoftFreq = 5.0; public double LimZeta = 0.9; public double HardAt = 1.3; public GluteParams Clone() { GluteParams obj = (GluteParams)MemberwiseClone(); obj.Freq = (double[])Freq.Clone(); obj.Zeta = (double[])Zeta.Clone(); obj.LimPos = (double[])LimPos.Clone(); obj.LimNeg = (double[])LimNeg.Clone(); return obj; } } public static class GlutePresets { public static readonly string[] Names = new string[3] { "light", "visible", "strong" }; public static GluteParams For(string name) { string text = (name ?? "").ToLowerInvariant(); if (!(text == "light")) { if (text == "strong") { return Make(new double[3] { 2.0, 1.5, 2.1 }, new double[3] { 0.8, 0.8, 0.8 }, new double[6] { 0.026, 0.03, 0.03, 0.022, 0.036, 0.022 }, 1.1, 0.3); } return Make(new double[3] { 2.2, 1.6, 2.3 }, new double[3] { 0.85, 0.85, 0.85 }, new double[6] { 0.022, 0.026, 0.026, 0.018, 0.03, 0.018 }, 0.9, 0.3); } return Make(new double[3] { 2.4, 1.8, 2.5 }, new double[3] { 0.9, 0.9, 0.9 }, new double[6] { 0.018, 0.02, 0.02, 0.014, 0.024, 0.014 }, 0.65, 0.3); } private static GluteParams Make(double[] freq, double[] zeta, double[] lim, double inertia, double steady) { GluteParams gluteParams = new GluteParams(); gluteParams.Freq = freq; gluteParams.Zeta = zeta; gluteParams.LimPos = new double[3] { lim[0], lim[1], lim[2] }; gluteParams.LimNeg = new double[3] { lim[3], lim[4], lim[5] }; gluteParams.Inertia = inertia; gluteParams.Steady = steady; return gluteParams; } } public sealed class GluteSide { private struct Prev { public D3 A; public D3 P; public D3 V; public D3 W; public M3 M; public int N; public double Dt; } public const double Gravity = 9.81; public readonly D3 HeadHip; public readonly D3 HeadThigh; public readonly M3 Basis; public readonly double Hand; public GluteParams Params; public D3 X; public D3 V; public D3 Lp; public D3 Env; public D3 Drive; public D3 F1; public D3 F2; private Prev prev; private bool hasPrev; public GluteSide(D3 headHip, D3 headThigh, M3 hipRot, D3 headWorld, D3 midWorld, GluteParams p) { HeadHip = headHip; HeadThigh = headThigh; D3 d = MulMtV(hipRot, new D3(0.0, 1.0, 0.0)); D3 d2 = MulMtV(hipRot, new D3(0.0, 0.0, -1.0)); d2 = Norm(d2 - d * Dot(d2, d)); D3 d3 = Cross(d, d2); if (Dot(MulMV(hipRot, d3), headWorld - midWorld) < 0.0) { d3 *= -1.0; } Basis = new M3(d3, d, d2); Hand = ((!(Dot(Cross(d3, d), d2) < 0.0)) ? 1 : (-1)); Params = p; Reset(); } public void Reset() { X = default(D3); V = default(D3); Lp = default(D3); Env = default(D3); Drive = default(D3); F1 = default(D3); F2 = default(D3); hasPrev = false; } private D3 Anchor(GluteFrame hip, GluteFrame thigh) { D3 d = hip.P + MulMV(hip.R, HeadHip) * hip.S; double thighDrive = Params.ThighDrive; if (thighDrive == 0.0) { return d; } D3 d2 = thigh.P + MulMV(thigh.R, HeadThigh) * thigh.S; return d + (d2 - d) * thighDrive; } public void Step(double dt, GluteFrame hip, GluteFrame thigh) { GluteParams gluteParams = Params; double s = hip.S; D3 d = Anchor(hip, thigh); M3 m = MulMM(hip.R, Basis); if (!(dt > 0.0)) { return; } double num = 0.0; if (hasPrev) { double num2 = 0.0; for (int i = 0; i < 3; i++) { num2 += Dot(prev.M.Col(i), m.Col(i)); } num = Math.Acos(Math.Max(-1.0, Math.Min(1.0, (num2 - 1.0) / 2.0))); } if (!hasPrev || dt > 0.1 || Len(d - prev.A) > 1.0 || num > Math.PI / 2.0) { prev = new Prev { A = d, M = m, P = hip.P, V = default(D3), W = default(D3), N = 0, Dt = dt }; hasPrev = true; return; } D3 d2 = new D3(0.0, 0.0, 0.0); for (int j = 0; j < 3; j++) { d2 += Cross(prev.M.Col(j), m.Col(j)); } double num3 = Len(d2); d2 = ((num3 > 1E-12) ? (d2 * (num / (dt * num3))) : new D3(0.0, 0.0, 0.0)); D3 d3 = (d - prev.A) * (1.0 / dt); double t = ((gluteParams.FilterHz > 0.0) ? (1.0 - Math.Exp(Math.PI * -2.0 * gluteParams.FilterHz * dt)) : 1.0); bool num4 = prev.N == 0; D3 d4 = (num4 ? d3 : Lerp(prev.V, d3, t)); D3 d5 = (num4 ? d2 : Lerp(prev.W, d2, t)); double num5 = (prev.Dt + dt) / 2.0; D3 d6 = (num4 ? new D3(0.0, 0.0, 0.0) : ((d4 - prev.V) * (1.0 / num5))); D3 a = (num4 ? new D3(0.0, 0.0, 0.0) : ((d5 - prev.W) * (1.0 / num5))); d6 += Cross(a, prev.A - prev.P) * (gluteParams.Twist - 1.0); d6 *= 1.0 / Math.Sqrt(1.0 + Dot(d6, d6) / (gluteParams.MaxAccel * gluteParams.MaxAccel)); D3 d7 = MulMtV(prev.M, d6); double t2 = ((gluteParams.HpHz > 0.0) ? (1.0 - Math.Exp(Math.PI * -2.0 * gluteParams.HpHz * dt)) : 0.0); Lp = Lerp(Lp, d7, t2); D3 d8 = d7 - Lp; D3 d9 = new D3(0.0, 0.0, 0.0); double num6 = 1.0 - Math.Exp((0.0 - dt) / gluteParams.EnvAttack); double num7 = 1.0 - Math.Exp((0.0 - dt) / gluteParams.EnvRelease); for (int k = 0; k < 3; k++) { double num8 = Math.Abs(d8[k]); double num9 = gluteParams.EnvK * Env[k]; double num10 = Math.Max(0.0, num8 - num9); double num11 = Math.Min(num8, num9); d9[k] = (double)Math.Sign(d8[k]) * (num10 + gluteParams.Steady * num11); Env[k] += (num8 - Env[k]) * ((num8 > Env[k]) ? num6 : num7); } D3 f = F2; if (gluteParams.DriveHz > 0.0) { double t3 = 1.0 - Math.Exp(Math.PI * -2.0 * gluteParams.DriveHz * dt); F1 = Lerp(F1, d9, t3); F2 = Lerp(F2, F1, t3); } else { F1 = d9; F2 = d9; } D3 b = (Drive = F2); D3 a2 = MulMtV(prev.M, new D3(0.0, -9.81, 0.0)); D3 b2 = MulMtV(m, new D3(0.0, -9.81, 0.0)); int num12 = Math.Max(1, (int)Math.Ceiling(dt / gluteParams.Substep - 1E-09)); double num13 = dt / (double)num12; for (int l = 1; l <= num12; l++) { D3 d10 = Lerp(a2, b2, (double)l / (double)num12); D3 d11 = Lerp(f, b, (double)l / (double)num12); D3 a3 = new D3(0.0, 0.0, 0.0); for (int n = 0; n < 3; n++) { double num14 = (0.0 - gluteParams.Inertia) * d11[n] + gluteParams.Gravity * (d10[n] - ((n == 1) ? (-9.81) : 0.0)); double num15 = Math.PI * 2.0 * gluteParams.Freq[n]; a3[n] = num14 - num15 * num15 * X[n] - 2.0 * gluteParams.Zeta[n] * num15 * V[n]; } SoftLimit(ref a3, X, V, gluteParams, s); for (int num16 = 0; num16 < 3; num16++) { V[num16] += a3[num16] * num13; } double num17 = Len(V); double num18 = gluteParams.MaxSpeed * s; if (num17 > num18) { V *= num18 / num17; } for (int num19 = 0; num19 < 3; num19++) { X[num19] += V[num19] * num13; } HardLimit(ref X, ref V, gluteParams, s); } prev = new Prev { A = d, M = m, P = hip.P, V = d4, W = d5, N = prev.N + 1, Dt = dt }; } public void Motion(double hipScale, out Q4 Q, out D3 d) { GluteParams gluteParams = Params; M3 basis = Basis; d = MulMV(basis, X) * (gluteParams.Amount / hipScale); D3 d2 = (basis.C1 * (0.0 - gluteParams.PivotUp) + basis.C2 * gluteParams.PivotIn) * (1.0 / hipScale); double num = Len(d2); D3 d3 = d2 * (1.0 / num); D3 d4 = d * gluteParams.RotShare; d4 -= d3 * Dot(d4, d3); double num2 = num * Math.Tan(gluteParams.MaxTurnDeg * Math.PI / 180.0); double num3 = Len(d4); if (num3 > 1E-12) { d4 *= num2 * Math.Tanh(num3 / num2) / num3; } Q = QFromTo(d3, Norm(d2 + d4)); } public void Pose(GluteFrame hip, GluteFrame thigh, out D3 hipPos, out Q4 hipRot, out D3 thighPos, out Q4 thighRot) { Motion(hip.S, out var Q, out var d); hipPos = HeadHip + d; hipRot = Q; D3 d2 = thigh.P + MulMV(thigh.R, HeadThigh) * thigh.S; D3 d3 = MulMtV(hip.R, d2 - hip.P) * (1.0 / hip.S); D3 v = QRotate(Q, d3 - HeadHip) + HeadHip + d; D3 d4 = hip.P + MulMV(hip.R, v) * hip.S; thighPos = MulMtV(thigh.R, d4 - thigh.P) * (1.0 / thigh.S); Q4 q = QuatFromMat(hip.R); Q4 q2 = QuatFromMat(thigh.R); thighRot = QMul(QMul(QConj(q2), QMul(QMul(q, Q), QConj(q))), q2); } private static void Ellipsoid(D3 x, GluteParams p, double s, out double r, out D3 L) { double num = 0.0; L = new D3(0.0, 0.0, 0.0); for (int i = 0; i < 3; i++) { L[i] = ((x[i] >= 0.0) ? p.LimPos[i] : p.LimNeg[i]) * s; num += x[i] / L[i] * (x[i] / L[i]); } r = Math.Sqrt(num); } private static void SoftLimit(ref D3 a, D3 x, D3 v, GluteParams p, double s) { Ellipsoid(x, p, s, out var r, out var L); if (!(r <= p.SoftStart)) { double num = (r - p.SoftStart) / (1.0 - p.SoftStart); double num2 = Math.PI * 2.0 * p.SoftFreq; double num3 = num2 * num2 * num * num; D3 b = Norm(new D3(x.X / (L.X * L.X), x.Y / (L.Y * L.Y), x.Z / (L.Z * L.Z))); double num4 = Dot(x, b); double num5 = 2.0 * p.LimZeta * num2 * num; double num6 = Dot(v, b); for (int i = 0; i < 3; i++) { a[i] -= (num3 * num4 + num5 * num6) * b[i]; } } } private static void HardLimit(ref D3 x, ref D3 v, GluteParams p, double s) { Ellipsoid(x, p, s, out var r, out var L); if (r <= p.HardAt) { return; } for (int i = 0; i < 3; i++) { x[i] *= p.HardAt / r; } D3 b = Norm(new D3(x.X / (L.X * L.X), x.Y / (L.Y * L.Y), x.Z / (L.Z * L.Z))); double num = Dot(v, b); if (num > 0.0) { for (int j = 0; j < 3; j++) { v[j] -= num * b[j]; } } } private static double Dot(D3 a, D3 b) { return a.X * b.X + a.Y * b.Y + a.Z * b.Z; } private static D3 Cross(D3 a, D3 b) { return new D3(a.Y * b.Z - a.Z * b.Y, a.Z * b.X - a.X * b.Z, a.X * b.Y - a.Y * b.X); } private static double Len(D3 a) { return Math.Sqrt(Dot(a, a)); } private static D3 Norm(D3 a) { double num = Len(a); if (!(num > 1E-12)) { return new D3(0.0, 0.0, 0.0); } return a * (1.0 / num); } private static D3 Lerp(D3 a, D3 b, double t) { return new D3(a.X + (b.X - a.X) * t, a.Y + (b.Y - a.Y) * t, a.Z + (b.Z - a.Z) * t); } private static D3 MulMV(M3 m, D3 v) { return new D3(m.C0.X * v.X + m.C1.X * v.Y + m.C2.X * v.Z, m.C0.Y * v.X + m.C1.Y * v.Y + m.C2.Y * v.Z, m.C0.Z * v.X + m.C1.Z * v.Y + m.C2.Z * v.Z); } private static D3 MulMtV(M3 m, D3 v) { return new D3(m.C0.X * v.X + m.C0.Y * v.Y + m.C0.Z * v.Z, m.C1.X * v.X + m.C1.Y * v.Y + m.C1.Z * v.Z, m.C2.X * v.X + m.C2.Y * v.Y + m.C2.Z * v.Z); } private static M3 MulMM(M3 a, M3 b) { return new M3(MulMV(a, b.C0), MulMV(a, b.C1), MulMV(a, b.C2)); } private static Q4 QMul(Q4 q, Q4 r) { return new Q4(q.W * r.X + q.X * r.W + q.Y * r.Z - q.Z * r.Y, q.W * r.Y - q.X * r.Z + q.Y * r.W + q.Z * r.X, q.W * r.Z + q.X * r.Y - q.Y * r.X + q.Z * r.W, q.W * r.W - q.X * r.X - q.Y * r.Y - q.Z * r.Z); } private static Q4 QConj(Q4 q) { return new Q4(0.0 - q.X, 0.0 - q.Y, 0.0 - q.Z, q.W); } private static D3 QRotate(Q4 q, D3 v) { D3 a = new D3(q.X, q.Y, q.Z); D3 d = Cross(a, v) * 2.0; return v + d * q.W + Cross(a, d); } private static Q4 QFromTo(D3 a, D3 b) { D3 d = Cross(a, b); double num = 1.0 + Dot(a, b); if (num < 1E-08) { return new Q4(0.0, 0.0, 0.0, 1.0); } double num2 = Math.Sqrt(d.X * d.X + d.Y * d.Y + d.Z * d.Z + num * num); return new Q4(d.X / num2, d.Y / num2, d.Z / num2, num / num2); } public static Q4 QuatFromMat(M3 m) { double x = m.C0.X; double y = m.C0.Y; double z = m.C0.Z; double x2 = m.C1.X; double y2 = m.C1.Y; double z2 = m.C1.Z; double x3 = m.C2.X; double y3 = m.C2.Y; double z3 = m.C2.Z; double num = x + y2 + z3; Q4 q; if (num > 0.0) { double num2 = Math.Sqrt(num + 1.0) * 2.0; q = new Q4((z2 - y3) / num2, (x3 - z) / num2, (y - x2) / num2, 0.25 * num2); } else if (x > y2 && x > z3) { double num3 = Math.Sqrt(1.0 + x - y2 - z3) * 2.0; q = new Q4(0.25 * num3, (x2 + y) / num3, (x3 + z) / num3, (z2 - y3) / num3); } else if (y2 > z3) { double num4 = Math.Sqrt(1.0 + y2 - x - z3) * 2.0; q = new Q4((x2 + y) / num4, 0.25 * num4, (y3 + z2) / num4, (x3 - z) / num4); } else { double num5 = Math.Sqrt(1.0 + z3 - x - y2) * 2.0; q = new Q4((x3 + z) / num5, (y3 + z2) / num5, 0.25 * num5, (y - x2) / num5); } double num6 = Math.Sqrt(q.X * q.X + q.Y * q.Y + q.Z * q.Z + q.W * q.W); return new Q4(q.X / num6, q.Y / num6, q.Z / num6, q.W / num6); } } [BepInPlugin("local.valheim.nordicglutephysics", "Nordic Glute Physics (prototype)", "0.3.0")] [BepInDependency("com.rawrtastic.plugins.enhancedvalheimvrm", "1.0.0")] public sealed class GlutePhysicsPlugin : BaseUnityPlugin { internal static ManualLogSource Log; internal static ConfigEntry<bool> Enabled; internal static ConfigEntry<string> Preset; internal static ConfigEntry<float> Amount; private static FieldInfo cloneField; internal static readonly List<GluteJiggleDriver> Drivers = new List<GluteJiggleDriver>(); internal static int ServiceFrame = -1; private void Awake() { //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_005e: Expected O, but got Unknown //IL_0091: Unknown result type (might be due to invalid IL or missing references) //IL_009b: Expected O, but got Unknown //IL_0130: Unknown result type (might be due to invalid IL or missing references) //IL_0136: Expected O, but got Unknown //IL_0148: Unknown result type (might be due to invalid IL or missing references) //IL_0156: Expected O, but got Unknown //IL_0199: Unknown result type (might be due to invalid IL or missing references) //IL_01a6: Expected O, but got Unknown Log = ((BaseUnityPlugin)this).Logger; Enabled = ((BaseUnityPlugin)this).Config.Bind<bool>("GlutePhysics", "Enabled", true, "Glute physics for avatars that have the Glute_* bones (applies to avatars loaded after the change)."); Preset = ((BaseUnityPlugin)this).Config.Bind<string>("GlutePhysics", "Preset", "visible", new ConfigDescription("How much the glutes show their motion: light, visible or strong (all toned). Read when an avatar is loaded: restart the game (or reload the avatar) after changing it.", (AcceptableValueBase)(object)new AcceptableValueList<string>(GlutePresets.Names), Array.Empty<object>())); Amount = ((BaseUnityPlugin)this).Config.Bind<float>("GlutePhysics", "Amount", 1f, new ConfigDescription("Strength of the shown motion: 0 = still, 1 = the preset; above 1 goes past the preset's travel limits (tested with clothing at 1 only). Live.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1.5f), Array.Empty<object>())); Type type = ((object)Chainloader.PluginInfos["com.rawrtastic.plugins.enhancedvalheimvrm"].Instance).GetType().Assembly.GetType("EnhancedValheimVRM.VrmInstance", throwOnError: false); MethodInfo methodInfo = ((type == null) ? null : AccessTools.DeclaredMethod(type, "CreateVrmGo", Type.EmptyTypes, (Type[])null)); cloneField = ((type == null) ? null : AccessTools.DeclaredField(type, "_vrmGo")); if (methodInfo == null || cloneField == null) { ((BaseUnityPlugin)this).Logger.LogError((object)"Inactive: EnhancedValheimVRM clone staging hook not found."); return; } Harmony val = new Harmony("local.valheim.nordicglutephysics"); val.Patch((MethodBase)methodInfo, new HarmonyMethod(typeof(GlutePhysicsPlugin), "BeforeCreate", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); MethodInfo methodInfo2 = AccessTools.DeclaredMethod(typeof(FastSpringBoneService), "LateUpdate", Type.EmptyTypes, (Type[])null); try { if (methodInfo2 == null) { throw new MissingMethodException("FastSpringBoneService.LateUpdate"); } val.Patch((MethodBase)methodInfo2, (HarmonyMethod)null, new HarmonyMethod(typeof(GlutePhysicsPlugin), "AfterSprings", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("Glute physics: spring service hook unavailable, avatars step in their own LateUpdate: " + ex.Message)); } ((BaseUnityPlugin)this).Logger.LogInfo((object)$"Glute physics prototype enabled (preset {Preset.Value}, Amount {Amount.Value:0.##})."); } private static void AfterSprings() { ServiceFrame = Time.frameCount; for (int num = Drivers.Count - 1; num >= 0; num--) { GluteJiggleDriver gluteJiggleDriver = Drivers[num]; if ((Object)(object)gluteJiggleDriver == (Object)null) { Drivers.RemoveAt(num); } else { try { gluteJiggleDriver.Tick(); } catch (Exception ex) { Log.LogError((object)("Glute physics step failed, driver stopped: " + ex)); ((Behaviour)gluteJiggleDriver).enabled = false; } } } } private static void BeforeCreate(object __instance) { if (!Enabled.Value) { return; } try { object? value = cloneField.GetValue(__instance); GameObject val = (GameObject)((value is GameObject) ? value : null); if ((Object)(object)val == (Object)null || (Object)(object)val.GetComponent<GluteJiggleDriver>() != (Object)null) { return; } Dictionary<string, Transform> dictionary = new Dictionary<string, Transform>(); Transform[] componentsInChildren = val.GetComponentsInChildren<Transform>(true); foreach (Transform val2 in componentsInChildren) { if (((Object)val2).name.StartsWith("Glute_", StringComparison.Ordinal) || ((Object)val2).name == "CC_Base_Hip") { dictionary[((Object)val2).name] = val2; } } if (dictionary.ContainsKey("Glute_L") || dictionary.ContainsKey("Glute_R")) { GluteParams gluteParams = GlutePresets.For(Preset.Value); GluteJiggleDriver gluteJiggleDriver = val.AddComponent<GluteJiggleDriver>(); if (!gluteJiggleDriver.Setup(dictionary, gluteParams, ((Object)val).name)) { Object.Destroy((Object)(object)gluteJiggleDriver); Log.LogWarning((object)("Glute physics not attached to " + ((Object)val).name + ": glute bones not as expected.")); return; } Log.LogInfo((object)("Glute physics attached to " + ((Object)val).name + ": preset " + Preset.Value + ", freq out/up/back " + FormattableString.Invariant($"{gluteParams.Freq[0]:0.##}/{gluteParams.Freq[1]:0.##}/{gluteParams.Freq[2]:0.##} Hz, damping {gluteParams.Zeta[1]:0.##}, inertia {gluteParams.Inertia:0.##}, steady-run share {gluteParams.Steady:0.##}."))); } } catch (Exception ex) { Log.LogError((object)("Glute physics setup failed: " + ex)); } } } [DefaultExecutionOrder(11030)] public sealed class GluteJiggleDriver : MonoBehaviour { private sealed class Side { public Transform HipBone; public Transform ThighBone; public Transform Hip; public Transform Thigh; public Vector3 RestHip; public Vector3 RestThigh; public GluteSide Solver; } private readonly List<Side> sides = new List<Side>(); private GluteParams preset; private int steppedFrame = -1; private bool loggedStep; private string who; internal bool Setup(Dictionary<string, Transform> bones, GluteParams p, string label) { //IL_00e7: Unknown result type (might be due to invalid IL or missing references) //IL_00ec: 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_0138: 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) //IL_0149: Unknown result type (might be due to invalid IL or missing references) //IL_0150: Unknown result type (might be due to invalid IL or missing references) //IL_0157: Unknown result type (might be due to invalid IL or missing references) //IL_015e: 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_01a9: Unknown result type (might be due to invalid IL or missing references) //IL_01ab: 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_01b3: Unknown result type (might be due to invalid IL or missing references) who = label; preset = p; RuntimeGltfInstance component = ((Component)this).GetComponent<RuntimeGltfInstance>(); IReadOnlyDictionary<Transform, TransformState> states = ((component != null) ? component.InitialTransformStates : null); if (!bones.TryGetValue("CC_Base_Hip", out var value)) { return false; } string[] array = new string[2] { "L", "R" }; foreach (string text in array) { if (bones.TryGetValue("Glute_" + text, out var value2) && bones.TryGetValue("Glute_" + text + "_Thigh", out var value3)) { if ((Object)(object)value2.parent != (Object)(object)value || (Object)(object)value3.parent == (Object)null || ((Object)value3.parent).name != "CC_Base_" + text + "_ThighTwist01" || value2.childCount != 0 || value3.childCount != 0) { return false; } if (Quaternion.Angle(Local(value2, states, out var pos), Quaternion.identity) > 0.01f || Quaternion.Angle(Local(value3, states, out pos), Quaternion.identity) > 0.01f) { return false; } RestTRS(value, states, out var pos2, out var rot); RestTRS(value2, states, out var pos3, out var _); Local(value2, states, out var pos4); Local(value3, states, out var pos5); GluteSide solver = new GluteSide(D(pos4), D(pos5), M(rot), D(pos3), D(pos2), p.Clone()); sides.Add(new Side { HipBone = value2, ThighBone = value3, Hip = value, Thigh = value3.parent, RestHip = pos4, RestThigh = pos5, Solver = solver }); } } return sides.Count > 0; } private void OnEnable() { foreach (Side side in sides) { side.Solver.Reset(); } if (!GlutePhysicsPlugin.Drivers.Contains(this)) { GlutePhysicsPlugin.Drivers.Add(this); } } private void OnDisable() { //IL_0037: 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) //IL_0066: 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) GlutePhysicsPlugin.Drivers.Remove(this); foreach (Side side in sides) { if ((Object)(object)side.HipBone != (Object)null) { side.HipBone.localPosition = side.RestHip; side.HipBone.localRotation = Quaternion.identity; } if ((Object)(object)side.ThighBone != (Object)null) { side.ThighBone.localPosition = side.RestThigh; side.ThighBone.localRotation = Quaternion.identity; } } } private void LateUpdate() { if (GlutePhysicsPlugin.ServiceFrame >= Time.frameCount - 1) { return; } try { Tick(); } catch (Exception ex) { GlutePhysicsPlugin.Log.LogError((object)("Glute physics step failed, driver stopped: " + ex)); ((Behaviour)this).enabled = false; } } internal void Tick() { //IL_01d0: Unknown result type (might be due to invalid IL or missing references) //IL_023d: Unknown result type (might be due to invalid IL or missing references) //IL_024e: Unknown result type (might be due to invalid IL or missing references) //IL_025e: 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_01d9: Unknown result type (might be due to invalid IL or missing references) //IL_01e2: Unknown result type (might be due to invalid IL or missing references) //IL_01e9: Unknown result type (might be due to invalid IL or missing references) //IL_01f1: Unknown result type (might be due to invalid IL or missing references) //IL_01f8: Unknown result type (might be due to invalid IL or missing references) //IL_0200: Unknown result type (might be due to invalid IL or missing references) //IL_0207: Unknown result type (might be due to invalid IL or missing references) //IL_020f: Unknown result type (might be due to invalid IL or missing references) //IL_0217: Unknown result type (might be due to invalid IL or missing references) //IL_021f: Unknown result type (might be due to invalid IL or missing references) //IL_0280: Unknown result type (might be due to invalid IL or missing references) //IL_028d: Unknown result type (might be due to invalid IL or missing references) //IL_029a: 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) if (steppedFrame == Time.frameCount || !((Behaviour)this).isActiveAndEnabled) { return; } steppedFrame = Time.frameCount; float deltaTime = Time.deltaTime; if (!loggedStep) { loggedStep = true; GlutePhysicsPlugin.Log.LogInfo((object)($"Glute physics {who}: first step at frame {Time.frameCount}, dt {deltaTime:0.0000}, " + ((GlutePhysicsPlugin.ServiceFrame == Time.frameCount) ? "right after the spring service." : "in its own LateUpdate."))); } double amount = GlutePhysicsPlugin.Amount?.Value ?? 1f; Vector3 val = default(Vector3); Vector3 val2 = default(Vector3); Quaternion val3 = default(Quaternion); Quaternion val4 = default(Quaternion); foreach (Side side in sides) { if (!((Object)(object)side.HipBone == (Object)null) && !((Object)(object)side.ThighBone == (Object)null) && !((Object)(object)side.Hip == (Object)null) && !((Object)(object)side.Thigh == (Object)null)) { side.Solver.Params.Amount = amount; GluteFrame hip = Frame(side.Hip); GluteFrame thigh = Frame(side.Thigh); side.Solver.Step(deltaTime, hip, thigh); side.Solver.Pose(hip, thigh, out var hipPos, out var hipRot, out var thighPos, out var thighRot); ((Vector3)(ref val))..ctor((float)hipPos.X, (float)hipPos.Y, (float)hipPos.Z); ((Vector3)(ref val2))..ctor((float)thighPos.X, (float)thighPos.Y, (float)thighPos.Z); ((Quaternion)(ref val3))..ctor((float)hipRot.X, (float)hipRot.Y, (float)hipRot.Z, (float)hipRot.W); ((Quaternion)(ref val4))..ctor((float)thighRot.X, (float)thighRot.Y, (float)thighRot.Z, (float)thighRot.W); if (!Finite(val) || !Finite(val2) || !Finite(new Vector3(val3.x + val4.x, val3.y + val4.y, val3.z + val4.z + val3.w + val4.w))) { side.Solver.Reset(); side.HipBone.localPosition = side.RestHip; side.ThighBone.localPosition = side.RestThigh; side.HipBone.localRotation = Quaternion.identity; side.ThighBone.localRotation = Quaternion.identity; } else { side.HipBone.localPosition = val; side.ThighBone.localPosition = val2; side.HipBone.localRotation = val3; side.ThighBone.localRotation = val4; } } } } private static GluteFrame Frame(Transform t) { //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_0008: 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_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) Vector3 lossyScale = t.lossyScale; return new GluteFrame(D(t.position), M(t.rotation), (double)(Mathf.Abs(lossyScale.x) + Mathf.Abs(lossyScale.y) + Mathf.Abs(lossyScale.z)) / 3.0); } private static Quaternion Local(Transform t, IReadOnlyDictionary<Transform, TransformState> states, out Vector3 pos) { //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_002a: 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_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0016: 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) if (states != null && states.TryGetValue(t, out var value)) { pos = ((TransformState)(ref value)).LocalPosition; return ((TransformState)(ref value)).LocalRotation; } pos = t.localPosition; return t.localRotation; } private void RestTRS(Transform t, IReadOnlyDictionary<Transform, TransformState> states, out Vector3 pos, out Quaternion rot) { //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_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0012: 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) //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_006a: 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) //IL_0071: 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) //IL_007b: Unknown result type (might be due to invalid IL or missing references) //IL_0084: 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_008a: Unknown result type (might be due to invalid IL or missing references) //IL_008f: Unknown result type (might be due to invalid IL or missing references) pos = Vector3.zero; rot = Quaternion.identity; List<Transform> list = new List<Transform>(); Transform val = t; while ((Object)(object)val != (Object)null && (Object)(object)val != (Object)(object)((Component)this).transform) { list.Add(val); val = val.parent; } for (int num = list.Count - 1; num >= 0; num--) { Vector3 pos2; Quaternion val2 = Local(list[num], states, out pos2); pos += rot * pos2; rot *= val2; } } private static D3 D(Vector3 v) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Unknown result type (might be due to invalid IL or missing references) return new D3(v.x, v.y, v.z); } private static M3 M(Quaternion q) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //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_0010: 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_0016: Unknown result type (might be due to invalid IL or missing references) //IL_0020: 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_0026: Unknown result type (might be due to invalid IL or missing references) return new M3(D(q * Vector3.right), D(q * Vector3.up), D(q * Vector3.forward)); } private static bool Finite(Vector3 v) { //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_000d: Unknown result type (might be due to invalid IL or missing references) //IL_001b: 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_0028: Unknown result type (might be due to invalid IL or missing references) if (!float.IsNaN(v.x + v.y + v.z)) { return !float.IsInfinity(v.x + v.y + v.z); } return false; } } } namespace LocalValheimFixes { public struct D3 { public double X; public double Y; public double Z; public double this[int k] { get { return k switch { 1 => Y, 0 => X, _ => Z, }; } set { switch (k) { case 0: X = value; break; case 1: Y = value; break; default: Z = value; break; } } } public D3(double x, double y, double z) { X = x; Y = y; Z = z; } public static D3 operator +(D3 a, D3 b) { return new D3(a.X + b.X, a.Y + b.Y, a.Z + b.Z); } public static D3 operator -(D3 a, D3 b) { return new D3(a.X - b.X, a.Y - b.Y, a.Z - b.Z); } public static D3 operator *(D3 a, double s) { return new D3(a.X * s, a.Y * s, a.Z * s); } } public struct M3 { public D3 C0; public D3 C1; public D3 C2; public M3(D3 c0, D3 c1, D3 c2) { C0 = c0; C1 = c1; C2 = c2; } public D3 Col(int k) { return k switch { 1 => C1, 0 => C0, _ => C2, }; } } public struct Q4 { public double X; public double Y; public double Z; public double W; public Q4(double x, double y, double z, double w) { X = x; Y = y; Z = z; W = w; } } public sealed class JiggleStage { public bool On = true; public double[] Freq; public double[] Zeta; public double[] LimPos; public double[] LimNeg; public JiggleStage Clone() { return new JiggleStage { On = On, Freq = (double[])Freq.Clone(), Zeta = (double[])Zeta.Clone(), LimPos = (double[])LimPos.Clone(), LimNeg = (double[])LimNeg.Clone() }; } } public sealed class JiggleSupport { public bool On = true; public double FreqMul = 1.25; public double ZetaMul = 1.35; public double LimMul = 0.6; public double SideLimMul = 0.48; public double InertiaMul = 0.6; public double SwingMul = 0.5; public JiggleSupport Clone() { return (JiggleSupport)MemberwiseClone(); } } public sealed class JiggleSwing { public bool On = true; public double Amount = 1.0; public double RateLo = 300.0; public double RateHi = 800.0; public double Attack = 0.06; public double Release = 0.45; public double FreqMul = 0.6; public double ZetaMul = 1.2; public double LimMul = 1.7; public JiggleSwing Clone() { return (JiggleSwing)MemberwiseClone(); } } public sealed class JiggleParams { public JiggleStage Base = new JiggleStage { Freq = new double[3] { 2.5, 3.2, 5.5 }, Zeta = new double[3] { 0.24, 0.22, 0.4 }, LimPos = new double[3] { 0.025, 0.025, 0.012 }, LimNeg = new double[3] { 0.025, 0.03, 0.008 } }; public JiggleStage Tip = new JiggleStage { On = false, Freq = new double[3] { 5.0, 4.5, 6.0 }, Zeta = new double[3] { 0.2, 0.18, 0.3 }, LimPos = new double[3] { 0.008, 0.01, 0.005 }, LimNeg = new double[3] { 0.008, 0.01, 0.004 } }; public double Inertia = 0.45; public double Gravity = 0.7; public double Amount = 1.0; public double RotShare = 0.75; public double MaxTurnDeg = 32.0; public double Squash; public double Radius = 0.045; public double MarkerFrac = 0.66; public double SkinWeight = 0.85; public double FilterHz = 12.0; public double MaxAccel = 40.0; public double MaxSpeed = 1.5; public double Substep = 1.0 / 240.0; public double SoftStart = 0.8; public double SoftFreq = 8.0; public double HardAt = 1.25; public double Bounce; public JiggleSupport Support = new JiggleSupport(); public JiggleSwing Swing = new JiggleSwing(); public const double SupportBlendSeconds = 0.3; public JiggleParams Clone() { JiggleParams obj = (JiggleParams)MemberwiseClone(); obj.Base = Base.Clone(); obj.Tip = Tip.Clone(); obj.Support = Support.Clone(); obj.Swing = Swing.Clone(); return obj; } public JiggleParams Supported() { if (!Support.On) { return this; } JiggleParams jiggleParams = Clone(); jiggleParams.Inertia = Inertia * Support.InertiaMul; jiggleParams.Swing.Amount = Swing.Amount * Support.SwingMul; JiggleStage[] array = new JiggleStage[2] { jiggleParams.Base, jiggleParams.Tip }; foreach (JiggleStage jiggleStage in array) { for (int j = 0; j < 3; j++) { double num = ((j == 0) ? Support.SideLimMul : Support.LimMul); jiggleStage.Freq[j] *= Support.FreqMul; jiggleStage.Zeta[j] = Math.Min(1.0, jiggleStage.Zeta[j] * Support.ZetaMul); jiggleStage.LimPos[j] *= num; jiggleStage.LimNeg[j] *= num; } } return jiggleParams; } public static void Blend(JiggleParams bare, JiggleParams clothed, double t, JiggleParams into) { JiggleParams jiggleParams = ((t <= 0.0) ? bare : ((t >= 1.0) ? clothed : null)); into.Inertia = jiggleParams?.Inertia ?? (bare.Inertia + (clothed.Inertia - bare.Inertia) * t); into.Swing.Amount = jiggleParams?.Swing.Amount ?? (bare.Swing.Amount + (clothed.Swing.Amount - bare.Swing.Amount) * t); BlendStage(bare.Base, clothed.Base, t, jiggleParams?.Base, into.Base); BlendStage(bare.Tip, clothed.Tip, t, jiggleParams?.Tip, into.Tip); } private static void BlendStage(JiggleStage a, JiggleStage b, double t, JiggleStage src, JiggleStage into) { for (int i = 0; i < 3; i++) { into.Freq[i] = ((src != null) ? src.Freq[i] : (a.Freq[i] + (b.Freq[i] - a.Freq[i]) * t)); into.Zeta[i] = ((src != null) ? src.Zeta[i] : (a.Zeta[i] + (b.Zeta[i] - a.Zeta[i]) * t)); into.LimPos[i] = ((src != null) ? src.LimPos[i] : (a.LimPos[i] + (b.LimPos[i] - a.LimPos[i]) * t)); into.LimNeg[i] = ((src != null) ? src.LimNeg[i] : (a.LimNeg[i] + (b.LimNeg[i] - a.LimNeg[i]) * t)); } } } public sealed class JiggleRest { public D3 Head; public D3 Tail; public Q4 Q0; public M3 Basis; public double Hand = 1.0; } public struct JigglePose { public D3 Position; public D3 Scale; public Q4 Quaternion; } public sealed class BreastJiggle { public const double GRAVITY = 9.81; public readonly JiggleRest Rest; public JiggleParams Params; public D3 X; public D3 V; public D3 Xt; public D3 Vt; public D3 Accel; public double Swing; public double TwistRate; private readonly JiggleStage swingStage = new JiggleStage { Freq = new double[3], Zeta = new double[3], LimPos = new double[3], LimNeg = new double[3] }; private bool hasPrev; private D3 prevA; private D3 prevV; private D3 prevW; private M3 prevM; private int prevN; private double prevDt; public BreastJiggle(JiggleRest rest, JiggleParams p) { Rest = rest; Params = p; Reset(); } public void Reset() { X = default(D3); V = default(D3); Xt = default(D3); Vt = default(D3); hasPrev = false; Accel = default(D3); Swing = 0.0; TwistRate = 0.0; } public static JiggleRest MakeRest(D3 head, Q4 q0, D3 tailLocal, M3 parentRot, D3 headWorld, D3 midWorld) { D3 d = head + QRotate(q0, tailLocal); D3 d2 = MulMtV(parentRot, new D3(0.0, 1.0, 0.0)); D3 d3 = d - head; d3 = Norm(d3 - d2 * Dot(d3, d2)); D3 d4 = Cross(d2, d3); if (Dot(MulMV(parentRot, d4), headWorld - midWorld) < 0.0) { d4 *= -1.0; } double hand = ((!(Dot(Cross(d4, d2), d3) < 0.0)) ? 1 : (-1)); return new JiggleRest { Head = head, Q0 = q0, Tail = d, Basis = new M3(d4, d2, d3), Hand = hand }; } private D3 MarkerLocal() { return Rest.Head + (Rest.Tail - Rest.Head) * Math.Max(0.05, Params.MarkerFrac); } public void Step(double dt, D3 parentPos, M3 parentRot, double parentScale = 1.0) { JiggleParams jiggleParams = Params; JiggleRest rest = Rest; D3 d = parentPos + MulMV(parentRot, MarkerLocal()) * parentScale; M3 m = MulMM(parentRot, rest.Basis); if (!(dt > 0.0)) { return; } double num = 0.0; if (hasPrev) { double num2 = 0.0; for (int i = 0; i < 3; i++) { num2 += Dot(prevM.Col(i), m.Col(i)); } num = Math.Acos(Math.Max(-1.0, Math.Min(1.0, (num2 - 1.0) / 2.0))); } if (!hasPrev || dt > 0.1 || Len(d - prevA) > 1.0 || num > Math.PI / 2.0) { hasPrev = true; prevA = d; prevM = m; prevV = default(D3); prevW = default(D3); prevN = 0; prevDt = dt; return; } D3 d2 = default(D3); for (int j = 0; j < 3; j++) { d2 += Cross(prevM.Col(j), m.Col(j)); } double num3 = Len(d2); d2 = ((num3 > 1E-12) ? (d2 * (num / (dt * num3))) : default(D3)); D3 d3 = (d - prevA) * (1.0 / dt); double t = ((jiggleParams.FilterHz > 0.0) ? (1.0 - Math.Exp(Math.PI * -2.0 * jiggleParams.FilterHz * dt)) : 1.0); bool num4 = prevN == 0; D3 d4 = (num4 ? d3 : Lerp(prevV, d3, t)); D3 d5 = (num4 ? d2 : Lerp(prevW, d2, t)); double num5 = (prevDt + dt) / 2.0; D3 d6 = (num4 ? default(D3) : ((d4 - prevV) * (1.0 / num5))); D3 v = (num4 ? default(D3) : ((d5 - prevW) * (1.0 / num5))); d6 *= 1.0 / Math.Sqrt(1.0 + Dot(d6, d6) / (jiggleParams.MaxAccel * jiggleParams.MaxAccel)); D3 accel = MulMtV(prevM, d6); D3 a = MulMtV(m, d5) * rest.Hand; D3 a2 = MulMtV(m, v) * rest.Hand; D3 a3 = MulMtV(prevM, new D3(0.0, -9.81, 0.0)); D3 b = MulMtV(m, new D3(0.0, -9.81, 0.0)); Accel = accel; JiggleStage jiggleStage = jiggleParams.Base; JiggleSwing swing = jiggleParams.Swing; TwistRate = a.Y; if (swing != null && swing.On) { double num6 = Math.Min(1.0, Math.Max(0.0, (Math.Abs(a.Y) * 180.0 / Math.PI - swing.RateLo) / (swing.RateHi - swing.RateLo))); double num7 = num6 * num6 * (3.0 - 2.0 * num6); Swing += (num7 - Swing) * (1.0 - Math.Exp((0.0 - dt) / ((num7 > Swing) ? swing.Attack : swing.Release))); double num8 = Swing * swing.Amount; if (num8 > 0.0) { JiggleStage jiggleStage2 = jiggleParams.Base; jiggleStage = swingStage; double num9 = 1.0 + (swing.FreqMul - 1.0) * num8; double num10 = 1.0 + (swing.ZetaMul - 1.0) * num8; double num11 = 1.0 + (swing.LimMul - 1.0) * num8; for (int k = 0; k < 3; k++) { jiggleStage.Freq[k] = jiggleStage2.Freq[k]; jiggleStage.Zeta[k] = jiggleStage2.Zeta[k]; jiggleStage.LimPos[k] = jiggleStage2.LimPos[k]; jiggleStage.LimNeg[k] = jiggleStage2.LimNeg[k]; } jiggleStage.Freq[0] = jiggleStage2.Freq[0] * num9; jiggleStage.Zeta[0] = Math.Min(1.0, jiggleStage2.Zeta[0] * num10); jiggleStage.LimPos[0] = jiggleStage2.LimPos[0] * num11; jiggleStage.LimNeg[0] = jiggleStage2.LimNeg[0] * num11; } } int num12 = Math.Max(1, (int)Math.Ceiling(dt / jiggleParams.Substep - 1E-09)); double num13 = dt / (double)num12; for (int l = 1; l <= num12; l++) { D3 d7 = Lerp(a3, b, (double)l / (double)num12); D3 d8 = Cross(a, V); D3 d9 = Cross(a2, X); D3 d10 = Cross(a, Cross(a, X)); D3 d11 = default(D3); D3 a4 = default(D3); for (int n = 0; n < 3; n++) { d11[n] = jiggleParams.Inertia * (0.0 - accel[n] - 2.0 * d8[n] - d9[n] - d10[n]) + jiggleParams.Gravity * (d7[n] - ((n == 1) ? (-9.81) : 0.0)); double num14 = Math.PI * 2.0 * jiggleStage.Freq[n]; a4[n] = d11[n] - num14 * num14 * X[n] - 2.0 * jiggleStage.Zeta[n] * num14 * V[n]; } SoftLimit(ref a4, X, jiggleStage, parentScale, jiggleParams); for (int num15 = 0; num15 < 3; num15++) { V[num15] += a4[num15] * num13; } double num16 = Len(V); double num17 = jiggleParams.MaxSpeed * parentScale; if (num16 > num17) { V *= num17 / num16; } for (int num18 = 0; num18 < 3; num18++) { X[num18] += V[num18] * num13; } HardLimit(ref X, ref V, jiggleStage, parentScale, jiggleParams.HardAt, jiggleParams.Bounce); if (jiggleParams.Tip.On) { for (int num19 = 0; num19 < 3; num19++) { double num20 = Math.PI * 2.0 * jiggleParams.Tip.Freq[num19]; double num21 = 0.0 - (a4[num19] - d11[num19]) - num20 * num20 * Xt[num19] - 2.0 * jiggleParams.Tip.Zeta[num19] * num20 * Vt[num19]; Vt[num19] += num21 * num13; Xt[num19] += Vt[num19] * num13; } HardLimit(ref Xt, ref Vt, jiggleParams.Tip, parentScale, jiggleParams.HardAt, jiggleParams.Bounce); } } if (!jiggleParams.Tip.On) { Xt = default(D3); Vt = default(D3); } prevA = d; prevM = m; prevV = d4; prevW = d5; prevN++; prevDt = dt; } public D3 Displacement() { return X + Xt; } public JigglePose Pose(double parentScale = 1.0) { JiggleParams jiggleParams = Params; JiggleRest rest = Rest; M3 basis = rest.Basis; double num = jiggleParams.Amount / (parentScale * jiggleParams.SkinWeight); D3 d = MulMV(basis, X) * num; D3 d2 = MulMV(basis, Xt) * num; D3 d3 = d + d2; D3 d4 = MarkerLocal() - rest.Head; double num2 = Len(d4); D3 d5 = d4 * (1.0 / num2); D3 d6 = d * jiggleParams.RotShare + d2; d6 -= d5 * Dot(d6, d5); double num3 = num2 * Math.Tan(jiggleParams.MaxTurnDeg * Math.PI / 180.0); double num4 = Len(d6); if (num4 > num3) { d6 *= num3 / num4; } Q4 q = QFromTo(d5, Norm(d4 + d6)); double num5 = Math.Max(-0.1, Math.Min(0.1, jiggleParams.Amount * jiggleParams.Squash * X.Y / (jiggleParams.Radius * parentScale))); double num6 = 1.0 - num5; double num7 = 1.0 / Math.Sqrt(num6); D3 d7 = QRotate(QConj(rest.Q0), basis.C1); D3 scale = ((Math.Abs(d7.X) >= Math.Abs(d7.Z)) ? new D3(num6, num7, num7) : new D3(num7, num7, num6)); D3 d8 = QRotate(QConj(rest.Q0), d4); D3 d9 = QRotate(QMul(q, rest.Q0), new D3(d8.X * scale.X, d8.Y * scale.Y, d8.Z * scale.Z)); D3 position = rest.Head + (d3 - (d9 - d4)); return new JigglePose { Position = position, Quaternion = QMul(q, rest.Q0), Scale = scale }; } private static double Ellipsoid(D3 x, JiggleStage lim, double s, out D3 L) { double num = 0.0; L = default(D3); for (int i = 0; i < 3; i++) { L[i] = ((x[i] >= 0.0) ? lim.LimPos[i] : lim.LimNeg[i]) * s; num += x[i] / L[i] * (x[i] / L[i]); } return Math.Sqrt(num); } private static void SoftLimit(ref D3 a, D3 x, JiggleStage lim, double s, JiggleParams p) { D3 L; double num = Ellipsoid(x, lim, s, out L); if (!(num <= p.SoftStart)) { double num2 = (num - p.SoftStart) / (1.0 - p.SoftStart); double num3 = Math.PI * 2.0 * p.SoftFreq; double num4 = num3 * num3 * num2 * num2; D3 b = Norm(new D3(x.X / (L.X * L.X), x.Y / (L.Y * L.Y), x.Z / (L.Z * L.Z))); double num5 = Dot(x, b); for (int i = 0; i < 3; i++) { a[i] -= num4 * num5 * b[i]; } } } private static bool HardLimit(ref D3 x, ref D3 v, JiggleStage lim, double s, double hardAt, double bounce) { D3 L; double num = Ellipsoid(x, lim, s, out L); if (num <= hardAt) { return false; } for (int i = 0; i < 3; i++) { x[i] *= hardAt / num; } D3 b = Norm(new D3(x.X / (L.X * L.X), x.Y / (L.Y * L.Y), x.Z / (L.Z * L.Z))); double num2 = Dot(v, b); if (num2 > 0.0) { for (int j = 0; j < 3; j++) { v[j] -= (1.0 + bounce) * num2 * b[j]; } } return true; } private static double Dot(D3 a, D3 b) { return a.X * b.X + a.Y * b.Y + a.Z * b.Z; } private static D3 Cross(D3 a, D3 b) { return new D3(a.Y * b.Z - a.Z * b.Y, a.Z * b.X - a.X * b.Z, a.X * b.Y - a.Y * b.X); } private static double Len(D3 a) { return Math.Sqrt(Dot(a, a)); } private static D3 Lerp(D3 a, D3 b, double t) { return new D3(a.X + (b.X - a.X) * t, a.Y + (b.Y - a.Y) * t, a.Z + (b.Z - a.Z) * t); } private static D3 Norm(D3 a) { double num = Len(a); if (!(num > 1E-12)) { return default(D3); } return a * (1.0 / num); } private static D3 MulMV(M3 m, D3 v) { return new D3(m.C0.X * v.X + m.C1.X * v.Y + m.C2.X * v.Z, m.C0.Y * v.X + m.C1.Y * v.Y + m.C2.Y * v.Z, m.C0.Z * v.X + m.C1.Z * v.Y + m.C2.Z * v.Z); } private static D3 MulMtV(M3 m, D3 v) { return new D3(Dot(m.C0, v), Dot(m.C1, v), Dot(m.C2, v)); } private static M3 MulMM(M3 a, M3 b) { return new M3(MulMV(a, b.C0), MulMV(a, b.C1), MulMV(a, b.C2)); } private static Q4 QMul(Q4 q, Q4 r) { return new Q4(q.W * r.X + q.X * r.W + q.Y * r.Z - q.Z * r.Y, q.W * r.Y - q.X * r.Z + q.Y * r.W + q.Z * r.X, q.W * r.Z + q.X * r.Y - q.Y * r.X + q.Z * r.W, q.W * r.W - q.X * r.X - q.Y * r.Y - q.Z * r.Z); } private static Q4 QConj(Q4 q) { return new Q4(0.0 - q.X, 0.0 - q.Y, 0.0 - q.Z, q.W); } private static Q4 QFromTo(D3 a, D3 b) { D3 d = Cross(a, b); double num = 1.0 + Dot(a, b); if (num < 1E-08) { return new Q4(0.0, 0.0, 0.0, 1.0); } double num2 = Math.Sqrt(d.X * d.X + d.Y * d.Y + d.Z * d.Z + num * num); return new Q4(d.X / num2, d.Y / num2, d.Z / num2, num / num2); } public static D3 QRotate(Q4 q, D3 v) { D3 a = new D3(q.X, q.Y, q.Z); D3 d = Cross(a, v) * 2.0; return v + d * q.W + Cross(a, d); } } }
plugins/VRMFixes.dll
Decompiled 2 months 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.Collections.ObjectModel; using System.Diagnostics; using System.Globalization; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Serialization; using System.Runtime.Serialization.Json; using System.Runtime.Versioning; using System.Text; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using LocalValheimFixes.Packages; using Microsoft.CodeAnalysis; using UniGLTF; using UniGLTF.Utils; using UniVRM10; using UniVRM10.FastSpringBones.System; using UnityEngine; [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("VRMFixes")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.1.5.0")] [assembly: AssemblyInformationalVersion("1.1.5+3356aed1f6508684669959e6a326f48d5ef50221")] [assembly: AssemblyProduct("VRMFixes")] [assembly: AssemblyTitle("VRMFixes")] [assembly: AssemblyVersion("1.1.5.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 LocalValheimFixes { [BepInPlugin("local.valheim.vrm1springclonefix", "VRM Fixes", "1.1.5")] [BepInDependency("com.rawrtastic.plugins.enhancedvalheimvrm", "1.0.0")] public sealed class SpringCloneFixPlugin : BaseUnityPlugin { internal static ManualLogSource Log; internal static FieldInfo CloneField; private void Awake() { //IL_0128: 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_014c: Expected O, but got Unknown Log = ((BaseUnityPlugin)this).Logger; PluginInfo val = Chainloader.PluginInfos["com.rawrtastic.plugins.enhancedvalheimvrm"]; Version version = val.Metadata.Version; if (!EnhancedVrmCompatibility.AllowsVersion(version)) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("Inactive: EnhancedValheimVRM " + version?.ToString() + " is outside the supported 1.x version family.")); return; } Type type = ((object)val.Instance).GetType().Assembly.GetType("EnhancedValheimVRM.VrmInstance", throwOnError: false); MethodInfo methodInfo = ((type == null) ? null : AccessTools.DeclaredMethod(type, "CreateVrmGo", Type.EmptyTypes, (Type[])null)); CloneField = ((type == null) ? null : AccessTools.DeclaredField(type, "_vrmGo")); if (methodInfo == null || methodInfo.IsStatic || methodInfo.ContainsGenericParameters || methodInfo.ReturnType != typeof(void) || CloneField == null || CloneField.IsStatic || CloneField.FieldType != typeof(GameObject)) { ((BaseUnityPlugin)this).Logger.LogError((object)"Inactive: the expected clone staging hook was not found."); return; } try { new Harmony("local.valheim.vrm1springclonefix").Patch((MethodBase)methodInfo, new HarmonyMethod(typeof(SpringCloneFixPlugin), "BeforeCreate", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogError((object)("Inactive: clone staging hook could not be installed: " + ex)); return; } ((BaseUnityPlugin)this).Logger.LogInfo((object)("VRM1 spring clone repair enabled for EnhancedValheimVRM " + version?.ToString() + ".")); try { PackagedAvatarHooks.Install(type.Assembly, ((BaseUnityPlugin)this).Logger); } catch (Exception ex2) { ((BaseUnityPlugin)this).Logger.LogError((object)("Packaged avatar support inactive: " + ex2)); } try { BearPawsFix.Install(type.Assembly, version, ((BaseUnityPlugin)this).Logger); } catch (Exception ex3) { ((BaseUnityPlugin)this).Logger.LogError((object)("Nordic bear paws support inactive: " + ex3)); } try { Wardrobe.Install(type.Assembly, ((BaseUnityPlugin)this).Config, ((BaseUnityPlugin)this).Logger); } catch (Exception ex4) { ((BaseUnityPlugin)this).Logger.LogError((object)("Avatar wardrobe inactive: " + ex4)); } try { BreastPhysics.Install(type.Assembly, ((BaseUnityPlugin)this).Config, ((BaseUnityPlugin)this).Logger); } catch (Exception ex5) { ((BaseUnityPlugin)this).Logger.LogError((object)("Breast physics inactive: " + ex5)); } } private static void BeforeCreate(object __instance) { try { object? value = CloneField.GetValue(__instance); GameObject val = (GameObject)((value is GameObject) ? value : null); int num = SpringGraphRepair.RepairFromCachedTemplate(val); if (num > 0) { Log.LogInfo((object)("Restored VRM1 spring graph: " + num + " joint references on " + ((Object)val).name + ".")); } } catch (Exception ex) { Log.LogError((object)("VRM1 spring clone repair failed: " + ex)); } try { object? value2 = CloneField.GetValue(__instance); BreastPhysics.Attach(__instance, (GameObject)((value2 is GameObject) ? value2 : null)); } catch (Exception ex2) { Log.LogError((object)("Breast physics setup failed, VRM springs kept: " + ex2)); } } } public static class SpringGraphRepair { private static readonly FieldInfo RuntimeField = AccessTools.DeclaredField(typeof(Vrm10Instance), "m_runtime"); public static int RepairFromCachedTemplate(GameObject cloneRoot) { if ((Object)(object)cloneRoot == (Object)null) { return 0; } Vrm10Instance clone = cloneRoot.GetComponent<Vrm10Instance>(); if (!NeedsRepair(clone)) { return 0; } Vrm10Instance[] array = (from v in Resources.FindObjectsOfTypeAll<Vrm10Instance>() where (Object)(object)v != (Object)(object)clone && (Object)(object)v.Vrm == (Object)(object)clone.Vrm && (Object)(object)((Component)v).transform.parent == (Object)null && !((Component)v).gameObject.activeInHierarchy && v.SpringBone != null && v.SpringBone.Springs.Count > 0 select v).ToArray(); if (array.Length != 1) { throw new InvalidOperationException("Expected one inactive imported template, found " + array.Length); } return Repair(((Component)array[0]).gameObject, cloneRoot); } private static bool NeedsRepair(Vrm10Instance clone) { if ((Object)(object)clone != (Object)null && (clone.SpringBone == null || clone.SpringBone.Springs.Count == 0)) { return ((Component)clone).GetComponentsInChildren<VRM10SpringBoneJoint>(true).Length > 1; } return false; } public static int Repair(GameObject sourceRoot, GameObject cloneRoot) { //IL_0080: Unknown result type (might be due to invalid IL or missing references) //IL_0086: Expected O, but got Unknown //IL_014b: Unknown result type (might be due to invalid IL or missing references) //IL_0152: Expected O, but got Unknown //IL_02d7: 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_037c: Unknown result type (might be due to invalid IL or missing references) //IL_03cd: Unknown result type (might be due to invalid IL or missing references) //IL_03d2: 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_03df: Unknown result type (might be due to invalid IL or missing references) //IL_03e7: Unknown result type (might be due to invalid IL or missing references) //IL_03ec: Unknown result type (might be due to invalid IL or missing references) //IL_042a: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)sourceRoot == (Object)null || (Object)(object)cloneRoot == (Object)null || (Object)(object)sourceRoot == (Object)(object)cloneRoot) { return 0; } Vrm10Instance component = sourceRoot.GetComponent<Vrm10Instance>(); Vrm10Instance component2 = cloneRoot.GetComponent<Vrm10Instance>(); if (!NeedsRepair(component2) || (Object)(object)component == (Object)null || component.SpringBone == null || component.SpringBone.Springs.Count == 0) { return 0; } Dictionary<Transform, Transform> transforms = new Dictionary<Transform, Transform>(); MapHierarchy(sourceRoot.transform, cloneRoot.transform, transforms); Vrm10InstanceSpringBone val = new Vrm10InstanceSpringBone(); Dictionary<VRM10SpringBoneColliderGroup, List<VRM10SpringBoneCollider>> dictionary = new Dictionary<VRM10SpringBoneColliderGroup, List<VRM10SpringBoneCollider>>(); VRM10SpringBoneColliderGroup[] componentsInChildren = ((Component)component).GetComponentsInChildren<VRM10SpringBoneColliderGroup>(true); foreach (VRM10SpringBoneColliderGroup val2 in componentsInChildren) { VRM10SpringBoneColliderGroup key = MapComponent<VRM10SpringBoneColliderGroup>(val2, transforms); dictionary[key] = val2.Colliders.Select((VRM10SpringBoneCollider c) => MapComponent<VRM10SpringBoneCollider>(c, transforms)).ToList(); } val.ColliderGroups = component.SpringBone.ColliderGroups.Select((VRM10SpringBoneColliderGroup g) => MapComponent<VRM10SpringBoneColliderGroup>(g, transforms)).ToList(); int num = 0; foreach (Spring spring in component.SpringBone.Springs) { Spring val3 = new Spring(spring.Name); val3.Center = (((Object)(object)spring.Center == (Object)null) ? null : transforms[spring.Center]); val3.Joints = spring.Joints.Select((VRM10SpringBoneJoint j) => MapComponent<VRM10SpringBoneJoint>(j, transforms)).ToList(); val3.ColliderGroups = spring.ColliderGroups.Select((VRM10SpringBoneColliderGroup g) => MapComponent<VRM10SpringBoneColliderGroup>(g, transforms)).ToList(); if (val3.Joints.Count((VRM10SpringBoneJoint j) => (Object)(object)j != (Object)null) < 2) { throw new InvalidOperationException("Imported spring has fewer than two usable joints."); } num += val3.Joints.Count; val.Springs.Add(val3); } RuntimeGltfInstance component3 = sourceRoot.GetComponent<RuntimeGltfInstance>(); RuntimeGltfInstance component4 = cloneRoot.GetComponent<RuntimeGltfInstance>(); IDictionary<Transform, TransformState> dictionary2 = (((Object)(object)component4 == (Object)null) ? null : (component4.InitialTransformStates as IDictionary<Transform, TransformState>)); if ((Object)(object)component3 == (Object)null || dictionary2 == null) { throw new InvalidOperationException("Initial transform state storage unavailable."); } Dictionary<Transform, TransformState> dictionary3 = new Dictionary<Transform, TransformState>(); foreach (KeyValuePair<Transform, TransformState> initialTransformState in component3.InitialTransformStates) { dictionary3.Add(transforms[initialTransformState.Key], initialTransformState.Value); } foreach (KeyValuePair<VRM10SpringBoneColliderGroup, List<VRM10SpringBoneCollider>> item in dictionary) { item.Key.Colliders = item.Value; } VRM10SpringBoneJoint[] componentsInChildren2 = ((Component)component).GetComponentsInChildren<VRM10SpringBoneJoint>(true); foreach (VRM10SpringBoneJoint val4 in componentsInChildren2) { VRM10SpringBoneJoint obj = MapComponent<VRM10SpringBoneJoint>(val4, transforms); obj.m_stiffnessForce = val4.m_stiffnessForce; obj.m_gravityPower = val4.m_gravityPower; obj.m_gravityDir = val4.m_gravityDir; obj.m_dragForce = val4.m_dragForce; obj.m_jointRadius = val4.m_jointRadius; } VRM10SpringBoneCollider[] componentsInChildren3 = ((Component)component).GetComponentsInChildren<VRM10SpringBoneCollider>(true); foreach (VRM10SpringBoneCollider val5 in componentsInChildren3) { VRM10SpringBoneCollider obj2 = MapComponent<VRM10SpringBoneCollider>(val5, transforms); obj2.ColliderType = val5.ColliderType; obj2.Offset = val5.Offset; obj2.Tail = val5.Tail; obj2.Radius = val5.Radius; } foreach (KeyValuePair<Transform, TransformState> item2 in dictionary3) { dictionary2[item2.Key] = item2.Value; } component2.SpringBone = val; if (RuntimeField != null && RuntimeField.GetValue(component2) != null) { component2.Runtime.ReconstructSpringBone(); } return num; } private static void MapHierarchy(Transform source, Transform clone, Dictionary<Transform, Transform> map) { if (source.childCount > clone.childCount) { throw new InvalidOperationException("Clone hierarchy is shorter than template."); } map.Add(source, clone); for (int i = 0; i < source.childCount; i++) { Transform child = source.GetChild(i); Transform child2 = clone.GetChild(i); if (((Object)child).name != ((Object)child2).name) { throw new InvalidOperationException("Clone hierarchy differs at child " + i + " of " + ((Object)source).name); } MapHierarchy(child, child2, map); } } private static T MapComponent<T>(T source, Dictionary<Transform, Transform> map) where T : Component { if ((Object)(object)source == (Object)null) { return default(T); } int num = Array.IndexOf(((Component)source).GetComponents<T>(), source); T[] components = ((Component)map[((Component)source).transform]).GetComponents<T>(); if (num < 0 || num >= components.Length) { throw new InvalidOperationException("Missing cloned component " + typeof(T).Name); } return components[num]; } } internal static class EnhancedVrmCompatibility { internal static bool AllowsVersion(Version version) { if (version != null) { return version.Major == 1; } return false; } } internal static class PackagedAvatarHooks { private static AvatarPackIndex _index; private static FieldInfo _outfitPath; internal static void Install(Assembly enhancedAssembly, ManualLogSource log) { //IL_0147: Unknown result type (might be due to invalid IL or missing references) //IL_014d: Expected O, but got Unknown //IL_015f: Unknown result type (might be due to invalid IL or missing references) //IL_016d: Expected O, but got Unknown //IL_0181: Unknown result type (might be due to invalid IL or missing references) //IL_018e: Expected O, but got Unknown MethodInfo methodInfo = AccessTools.DeclaredMethod(enhancedAssembly.GetType("EnhancedValheimVRM.Constants+Vrm", throwOnError: true), "Find", new Type[1] { typeof(string) }, (Type[])null); Type? type = enhancedAssembly.GetType("EnhancedValheimVRM.OutfitController", throwOnError: true); MethodInfo methodInfo2 = AccessTools.DeclaredMethod(type, "Generate", Type.EmptyTypes, (Type[])null); _outfitPath = AccessTools.DeclaredField(type, "_path"); if (methodInfo == null || !methodInfo.IsStatic || methodInfo.ContainsGenericParameters || methodInfo.ReturnType != typeof(string) || methodInfo2 == null || methodInfo2.IsStatic || methodInfo2.ContainsGenericParameters || methodInfo2.ReturnType != typeof(string) || _outfitPath == null || _outfitPath.IsStatic || _outfitPath.FieldType != typeof(string)) { throw new MissingMethodException("Expected EnhancedValheimVRM avatar lookup/write hooks were not found."); } _index = AvatarPackIndex.Build(Paths.PluginPath); foreach (string diagnostic in _index.Diagnostics) { log.LogWarning((object)diagnostic); } Harmony val = new Harmony("local.valheim.vrm1springclonefix.packages"); try { val.Patch((MethodBase)methodInfo2, new HarmonyMethod(typeof(PackagedAvatarHooks), "BeforeGenerate", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); val.Patch((MethodBase)methodInfo, (HarmonyMethod)null, new HarmonyMethod(typeof(PackagedAvatarHooks), "AfterFind", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } catch { val.UnpatchSelf(); throw; } log.LogInfo((object)("Packaged avatars indexed: " + _index.Bindings.Count + ". Explicit package characters override manual files; restart after package changes.")); foreach (AvatarBinding binding in _index.Bindings) { log.LogInfo((object)("Packaged avatar " + binding.Character + " from " + binding.PackId + ": " + binding.ModelPath)); } } private static void AfterFind(string fileName, ref string __result) { if (_index.TryResolve(fileName, out var path)) { __result = path; } } private static bool BeforeGenerate(object __instance, ref string __result) { string path = _outfitPath.GetValue(__instance) as string; if (!_index.IsProtectedPath(path)) { return true; } __result = "Packaged avatars use read-only outfit defaults; outfit generation is disabled for this avatar."; return false; } } public static class BearPawsFix { public sealed class AttachmentPlan { private readonly SkinnedMeshRenderer renderer; private readonly Transform avatarRoot; private readonly Transform[] palette; private readonly List<Attachment> attachments; internal AttachmentPlan(SkinnedMeshRenderer renderer, Transform avatarRoot, Transform[] palette, List<Attachment> attachments) { this.renderer = renderer; this.avatarRoot = avatarRoot; this.palette = palette; this.attachments = attachments; } public void Apply(List<Transform> moved) { //IL_002e: 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_0050: Unknown result type (might be due to invalid IL or missing references) foreach (Attachment attachment in attachments) { attachment.Source.SetParent(attachment.Target, false); attachment.Source.localPosition = Vector3.zero; attachment.Source.localRotation = attachment.Rotation; attachment.Source.localScale = attachment.Scale; if (!moved.Contains(attachment.Source)) { moved.Add(attachment.Source); } } renderer.bones = palette; renderer.rootBone = avatarRoot; renderer.updateWhenOffscreen = true; } } internal sealed class Attachment { internal Transform Source; internal Transform Target; internal Quaternion Rotation; internal Vector3 Scale; } private static FieldInfo AvatarRoot; private static FieldInfo AvatarAnimator; private static FieldInfo Settings; private static FieldInfo ItemName; private static FieldInfo Equipment; private static FieldInfo Moved; private static FieldInfo PiecesAvatar; private static Type PiecesType; private static ManualLogSource Log; private static readonly string[] Names = new string[6] { "LeftArm", "LeftForeArm", "LeftHand", "RightArm", "RightForeArm", "RightHand" }; private static readonly HumanBodyBones[] Targets; internal static void Install(Assembly assembly, Version version, ManualLogSource log) { //IL_027d: 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_02a1: Expected O, but got Unknown Log = log; if (version.Major != 1 || version.Minor != 3 || version.Build != 1) { log.LogInfo((object)"Nordic bear paws fix applies to EnhancedValheimVRM 1.3.1 only; spring and package support retain the 1.x policy."); return; } Type type = assembly.GetType("EnhancedValheimVRM.VrmAnimator", throwOnError: false); MethodInfo methodInfo = ((type == null) ? null : AccessTools.DeclaredMethod(type, "MoveRigOntoAvatar", new Type[1] { typeof(GameObject) }, (Type[])null)); PiecesType = assembly.GetType("EnhancedValheimVRM.RiggedItemPieces", throwOnError: false); AvatarRoot = ((type == null) ? null : AccessTools.DeclaredField(type, "_vrmGo")); AvatarAnimator = ((type == null) ? null : AccessTools.DeclaredField(type, "_vrmGoAnimator")); Settings = ((type == null) ? null : AccessTools.DeclaredField(type, "_vrmSettings")); ItemName = ((type == null) ? null : AccessTools.DeclaredField(type, "_rigItemName")); Equipment = ((type == null) ? null : AccessTools.DeclaredField(type, "_visEquipment")); Moved = ((PiecesType == null) ? null : AccessTools.DeclaredField(PiecesType, "Moved")); PiecesAvatar = ((PiecesType == null) ? null : AccessTools.DeclaredField(PiecesType, "Avatar")); if (methodInfo == null || methodInfo.IsStatic || methodInfo.ReturnType != typeof(void) || AvatarRoot?.FieldType != typeof(GameObject) || AvatarAnimator?.FieldType != typeof(Animator) || ItemName?.FieldType != typeof(string) || Settings == null || Equipment == null || PiecesType == null || !typeof(Component).IsAssignableFrom(PiecesType) || Moved?.FieldType != typeof(List<Transform>) || PiecesAvatar?.FieldType != typeof(Animator)) { log.LogWarning((object)"Nordic bear paws fix inactive: expected equipment hooks unavailable. Other fixes remain active."); return; } new Harmony("local.valheim.vrm1springclonefix.darkurgepaws").Patch((MethodBase)methodInfo, new HarmonyMethod(typeof(BearPawsFix), "BeforeMove", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); log.LogInfo((object)"Paws of the Bear attachment fix enabled for Dark Urge and Sigrid."); } private static bool BeforeMove(object __instance, GameObject rig) { //IL_0078: Unknown result type (might be due to invalid IL or missing references) //IL_0083: Expected O, but got Unknown if (!string.Equals(ItemName.GetValue(__instance) as string, "FistBjornClaw", StringComparison.Ordinal)) { return true; } object? value = AvatarRoot.GetValue(__instance); GameObject val = (GameObject)((value is GameObject) ? value : null); if (!IsSupportedAvatar(val)) { return true; } try { object? value2 = AvatarAnimator.GetValue(__instance); Animator val2 = (Animator)((value2 is Animator) ? value2 : null); Component val3 = (((Object)(object)rig == (Object)null) ? null : rig.GetComponent(PiecesType)); if ((Object)(object)val3 != (Object)null && (Object)(Animator)PiecesAvatar.GetValue(val3) == (Object)(object)val2) { return false; } object value3 = Settings.GetValue(__instance); object value4 = Equipment.GetValue(__instance); if (value3 == null || value4 == null) { return true; } FieldInfo fieldInfo = AccessTools.Field(value3.GetType(), "PlayerVrmScale"); PropertyInfo propertyInfo = AccessTools.Property(value3.GetType(), "PlayerVrmScale"); object obj = ((fieldInfo != null) ? fieldInfo.GetValue(value3) : propertyInfo?.GetValue(value3, null)); object? obj2 = AccessTools.Field(value4.GetType(), "m_bodyModel")?.GetValue(value4); SkinnedMeshRenderer val4 = (SkinnedMeshRenderer)((obj2 is SkinnedMeshRenderer) ? obj2 : null); if (!(obj is float scale) || (Object)(object)val4 == (Object)null) { return true; } AttachmentPlan attachmentPlan = Prepare(val, val2, rig, val4.bones, scale); if (attachmentPlan == null) { Log.LogWarning((object)"Nordic bear paws fix skipped: item or avatar rig differs from the validated layout."); return true; } val3 = val3 ?? rig.AddComponent(PiecesType); if (!(Moved.GetValue(val3) is List<Transform> moved)) { return true; } attachmentPlan.Apply(moved); PiecesAvatar.SetValue(val3, val2); Log.LogInfo((object)("Attached bear paws for " + val.GetComponent<Vrm10Instance>().Vrm.Meta.Name + " with calibrated wrist axes and both upper-arm weights.")); return false; } catch (Exception ex) { Log.LogError((object)("Nordic bear paws attachment could not be prepared: " + ex)); return true; } } public static bool IsSupportedAvatar(GameObject root) { Vrm10Instance val = (((Object)(object)root == (Object)null) ? null : root.GetComponent<Vrm10Instance>()); if ((Object)(object)val != (Object)null && (Object)(object)val.Vrm != (Object)null && val.Vrm.Meta != null) { if (!string.Equals(val.Vrm.Meta.Name, "Dark Urge", StringComparison.Ordinal)) { return string.Equals(val.Vrm.Meta.Name, "Sigrid", StringComparison.Ordinal); } return true; } return false; } public static AttachmentPlan Prepare(GameObject root, Animator animator, GameObject rig, Transform[] bodyBones, float scale) { //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_0206: Unknown result type (might be due to invalid IL or missing references) //IL_020b: 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_022f: Unknown result type (might be due to invalid IL or missing references) //IL_0231: Unknown result type (might be due to invalid IL or missing references) //IL_0238: Unknown result type (might be due to invalid IL or missing references) //IL_023d: Unknown result type (might be due to invalid IL or missing references) //IL_0242: Unknown result type (might be due to invalid IL or missing references) //IL_0244: Unknown result type (might be due to invalid IL or missing references) //IL_0252: Unknown result type (might be due to invalid IL or missing references) //IL_0260: 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_0299: 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_02a1: Unknown result type (might be due to invalid IL or missing references) //IL_02ab: Unknown result type (might be due to invalid IL or missing references) //IL_02b3: Unknown result type (might be due to invalid IL or missing references) //IL_02bd: Unknown result type (might be due to invalid IL or missing references) //IL_02c5: Unknown result type (might be due to invalid IL or missing references) //IL_02cf: Unknown result type (might be due to invalid IL or missing references) //IL_02d7: Unknown result type (might be due to invalid IL or missing references) //IL_02dc: Unknown result type (might be due to invalid IL or missing references) if (!IsSupportedAvatar(root) || (Object)(object)animator == (Object)null || (Object)(object)animator.avatar == (Object)null || !animator.isHuman || (Object)(object)rig == (Object)null || bodyBones == null || !Finite(scale) || scale <= 0f) { return null; } RuntimeGltfInstance component = root.GetComponent<RuntimeGltfInstance>(); if ((Object)(object)component == (Object)null) { return null; } Dictionary<string, Transform> dictionary = new Dictionary<string, Transform>(StringComparer.Ordinal); Transform[] componentsInChildren = rig.GetComponentsInChildren<Transform>(true); foreach (Transform val in componentsInChildren) { if (dictionary.ContainsKey(((Object)val).name)) { return null; } dictionary.Add(((Object)val).name, val); } SkinnedMeshRenderer[] componentsInChildren2 = rig.GetComponentsInChildren<SkinnedMeshRenderer>(true); if (componentsInChildren2.Length != 1) { return null; } SkinnedMeshRenderer val2 = componentsInChildren2[0]; Mesh sharedMesh = val2.sharedMesh; if ((Object)(object)sharedMesh == (Object)null || ((Object)sharedMesh).name != "BerserkerClaws" || sharedMesh.vertexCount != 631 || bodyBones.Length != 53 || val2.bones.Length != bodyBones.Length || sharedMesh.bindposes.Length != bodyBones.Length) { return null; } Transform[] array = (Transform[])(object)new Transform[bodyBones.Length]; for (int j = 0; j < array.Length; j++) { if ((Object)(object)bodyBones[j] == (Object)null || !dictionary.TryGetValue(((Object)bodyBones[j]).name, out array[j])) { return null; } } Transform[] array2 = (Transform[])(object)new Transform[Names.Length]; for (int k = 0; k < array2.Length; k++) { if (!dictionary.TryGetValue(Names[k], out array2[k])) { return null; } } List<Attachment> list = new List<Attachment>(); for (int l = 0; l < array2.Length; l++) { int num = Array.IndexOf(array, array2[l]); Transform boneTransform = animator.GetBoneTransform(Targets[l]); if (num < 0 || (Object)(object)boneTransform == (Object)null || !RestRotation(component, root.transform, boneTransform, out var rotation)) { return null; } Matrix4x4 val3 = rig.transform.worldToLocalMatrix * array2[l].localToWorldMatrix; Vector3 lossyScale = ((Matrix4x4)(ref val3)).lossyScale; Vector3 lossyScale2 = boneTransform.lossyScale; if (!PositiveUniform(lossyScale) || !PositiveUniform(lossyScale2) || ((Matrix4x4)(ref val3)).determinant <= 0f) { return null; } Quaternion val4 = Quaternion.Inverse(rotation) * ((Matrix4x4)(ref val3)).rotation; if (!Finite(val4.x) || !Finite(val4.y) || !Finite(val4.z) || !Finite(val4.w)) { return null; } list.Add(new Attachment { Source = array2[l], Target = boneTransform, Rotation = val4, Scale = new Vector3(lossyScale.x * scale / lossyScale2.x, lossyScale.y * scale / lossyScale2.y, lossyScale.z * scale / lossyScale2.z) }); } return new AttachmentPlan(val2, root.transform, array, list); } private static bool RestRotation(RuntimeGltfInstance gltf, Transform root, Transform bone, out Quaternion rotation) { //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_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0038: 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) rotation = Quaternion.identity; Transform val = bone; while ((Object)(object)val != (Object)(object)root) { if ((Object)(object)val == (Object)null || !gltf.InitialTransformStates.TryGetValue(val, out var value)) { return false; } rotation = ((TransformState)(ref value)).LocalRotation * rotation; val = val.parent; } return true; } private static bool Finite(float value) { if (!float.IsNaN(value)) { return !float.IsInfinity(value); } return false; } private static bool PositiveUniform(Vector3 scale) { //IL_0000: 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_001d: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_0037: 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_0051: 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_0070: 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) if (Finite(scale.x) && Finite(scale.y) && Finite(scale.z) && scale.x > 1E-06f && scale.y > 1E-06f && scale.z > 1E-06f && Mathf.Abs(scale.x / scale.y - 1f) < 0.002f) { return Mathf.Abs(scale.x / scale.z - 1f) < 0.002f; } return false; } static BearPawsFix() { HumanBodyBones[] array = new HumanBodyBones[6]; RuntimeHelpers.InitializeArray(array, (RuntimeFieldHandle)/*OpCode not supported: LdMemberToken*/); Targets = (HumanBodyBones[])(object)array; } } internal static class Wardrobe { internal sealed class Garment { internal GameObject Node; internal Renderer Renderer; internal string Item; internal int Level; internal readonly List<KeyValuePair<SkinnedMeshRenderer, int>> Masks = new List<KeyValuePair<SkinnedMeshRenderer, int>>(); } private sealed class State { internal Player Player; internal bool NoPlayer; internal WardrobeView View; internal int NextLookup; internal int Helmet; internal int Chest; internal int Leg; internal int HelmetQuality; internal int ChestQuality; internal int LegQuality; internal bool Applied; } private const string Prefix = "Garment_"; private const string MaskPrefix = "Mask_"; private const string UnderwearPrefix = "Underwear_"; private const string GivenKey = "NordicTestGarmentsGivenItems"; private const int RetryFrames = 30; private static readonly int HelmetQualityKey = StringExtensionMethods.GetStableHashCode("NordicHelmetQuality"); private static readonly int ChestQualityKey = StringExtensionMethods.GetStableHashCode("NordicChestQuality"); private static readonly int LegQualityKey = StringExtensionMethods.GetStableHashCode("NordicLegQuality"); private static readonly ConditionalWeakTable<VisEquipment, State> States = new ConditionalWeakTable<VisEquipment, State>(); private static ManualLogSource Log; private static ConfigEntry<bool> GiveTestGarments; private static MethodInfo FindInstance; private static MethodInfo GetGameObject; private static FieldInfo Clone; private static FieldInfo Owner; private static FieldRef<VisEquipment, int> HelmetHash; private static FieldRef<VisEquipment, int> ChestHash; private static FieldRef<VisEquipment, int> LegHash; private static FieldRef<VisEquipment, ZNetView> NView; private static FieldRef<Humanoid, ItemData> HelmetItem; private static FieldRef<Humanoid, ItemData> ChestItem; private static FieldRef<Humanoid, ItemData> LegItem; private static bool Failed; private static bool WarnedFull; internal static void Install(Assembly enhancedAssembly, ConfigFile config, ManualLogSource log) { //IL_03b9: Unknown result type (might be due to invalid IL or missing references) //IL_03c0: Expected O, but got Unknown //IL_03d3: Unknown result type (might be due to invalid IL or missing references) //IL_03e1: Expected O, but got Unknown //IL_03f6: Unknown result type (might be due to invalid IL or missing references) //IL_0403: Expected O, but got Unknown Log = log; GiveTestGarments = config.Bind<bool>("Wardrobe", "GiveTestGarments", false, "Test only: put one item for every garment level found in the avatar into the inventory, once per level and character, as free slots allow."); Type type = enhancedAssembly.GetType("EnhancedValheimVRM.VrmController", throwOnError: false); Type type2 = enhancedAssembly.GetType("EnhancedValheimVRM.VrmInstance", throwOnError: false); FindInstance = ((type == null) ? null : AccessTools.DeclaredMethod(type, "FindInstance", new Type[1] { typeof(Player) }, (Type[])null)); GetGameObject = ((type2 == null) ? null : AccessTools.DeclaredMethod(type2, "GetGameObject", Type.EmptyTypes, (Type[])null)); MethodInfo methodInfo = ((type2 == null) ? null : AccessTools.DeclaredMethod(type2, "CreateVrmGo", Type.EmptyTypes, (Type[])null)); Clone = ((type2 == null) ? null : AccessTools.DeclaredField(type2, "_vrmGo")); Owner = ((type2 == null) ? null : AccessTools.DeclaredField(type2, "_player")); MethodInfo methodInfo2 = AccessTools.DeclaredMethod(typeof(VisEquipment), "UpdateEquipmentVisuals", Type.EmptyTypes, (Type[])null); FieldInfo fieldInfo = AccessTools.DeclaredField(typeof(VisEquipment), "m_currentHelmetItemHash"); FieldInfo fieldInfo2 = AccessTools.DeclaredField(typeof(VisEquipment), "m_currentChestItemHash"); FieldInfo fieldInfo3 = AccessTools.DeclaredField(typeof(VisEquipment), "m_currentLegItemHash"); FieldInfo fieldInfo4 = AccessTools.DeclaredField(typeof(VisEquipment), "m_nview"); FieldInfo fieldInfo5 = AccessTools.DeclaredField(typeof(Humanoid), "m_helmetItem"); FieldInfo fieldInfo6 = AccessTools.DeclaredField(typeof(Humanoid), "m_chestItem"); FieldInfo fieldInfo7 = AccessTools.DeclaredField(typeof(Humanoid), "m_legItem"); if (FindInstance == null || !FindInstance.IsStatic || FindInstance.ReturnType != type2 || GetGameObject == null || GetGameObject.IsStatic || GetGameObject.ReturnType != typeof(GameObject) || Owner?.FieldType != typeof(Player) || methodInfo == null || methodInfo.IsStatic || Clone?.FieldType != typeof(GameObject) || methodInfo2 == null || fieldInfo?.FieldType != typeof(int) || fieldInfo2?.FieldType != typeof(int) || fieldInfo3?.FieldType != typeof(int) || fieldInfo4?.FieldType != typeof(ZNetView) || fieldInfo5?.FieldType != typeof(ItemData) || fieldInfo6?.FieldType != typeof(ItemData) || fieldInfo7?.FieldType != typeof(ItemData)) { throw new MissingMethodException("Expected avatar or equipment hooks were not found."); } HelmetHash = AccessTools.FieldRefAccess<VisEquipment, int>(fieldInfo); ChestHash = AccessTools.FieldRefAccess<VisEquipment, int>(fieldInfo2); LegHash = AccessTools.FieldRefAccess<VisEquipment, int>(fieldInfo3); NView = AccessTools.FieldRefAccess<VisEquipment, ZNetView>(fieldInfo4); HelmetItem = AccessTools.FieldRefAccess<Humanoid, ItemData>(fieldInfo5); ChestItem = AccessTools.FieldRefAccess<Humanoid, ItemData>(fieldInfo6); LegItem = AccessTools.FieldRefAccess<Humanoid, ItemData>(fieldInfo7); Harmony val = new Harmony("local.valheim.vrm1springclonefix.wardrobe"); try { val.Patch((MethodBase)methodInfo, new HarmonyMethod(typeof(Wardrobe), "BeforeCreate", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); val.Patch((MethodBase)methodInfo2, (HarmonyMethod)null, new HarmonyMethod(typeof(Wardrobe), "AfterEquipment", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } catch { val.UnpatchSelf(); throw; } log.LogInfo((object)("Avatar wardrobe enabled: Garment_<item>_Q<level> meshes follow worn helmet, chest and legs items; Underwear_* meshes hide under a legs garment." + (GiveTestGarments.Value ? " Test garments will be given once per level and character." : ""))); } private static void BeforeCreate(object __instance) { try { object? value = Clone.GetValue(__instance); GameObject val = (GameObject)((value is GameObject) ? value : null); if ((Object)(object)val == (Object)null || (Object)(object)val.GetComponent<WardrobeView>() != (Object)null) { return; } List<Garment> underwear; List<Garment> list = Collect(val, out underwear); if (list.Count == 0 && underwear.Count == 0) { return; } WardrobeView wardrobeView = val.AddComponent<WardrobeView>(); wardrobeView.Garments = list; wardrobeView.Underwear = underwear; object? value2 = Owner.GetValue(__instance); Player val2 = (Player)((value2 is Player) ? value2 : null); VisEquipment val3 = (((Object)(object)val2 == (Object)null) ? null : ((Component)val2).GetComponent<VisEquipment>()); if ((Object)(object)val3 != (Object)null) { State value3 = States.GetValue(val3, (VisEquipment _) => new State()); value3.View = wardrobeView; value3.Applied = false; value3.NextLookup = Time.frameCount + 30; } Log.LogInfo((object)("Avatar garments" + (((Object)(object)val2 == (Object)null) ? "" : (" on " + val2.GetPlayerName())) + ": " + string.Join(", ", from g in list.Concat(underwear) select ((Object)g.Node).name + ((g.Masks.Count > 0) ? (" (skin masks: " + g.Masks.Count + ")") : "")))); } catch (Exception ex) { Log.LogError((object)("Wardrobe could not prepare a new avatar: " + ex)); } } private static void AfterEquipment(VisEquipment __instance) { if (Failed || !__instance.m_isPlayer) { return; } try { State value = States.GetValue(__instance, (VisEquipment _) => new State()); if (value.NoPlayer) { return; } if ((Object)(object)value.Player == (Object)null) { value.Player = ((Component)__instance).GetComponent<Player>(); if ((Object)(object)value.Player == (Object)null) { value.NoPlayer = true; return; } } if (Time.frameCount >= value.NextLookup) { value.NextLookup = Time.frameCount + 30; GameObject val = AvatarRoot(value.Player); WardrobeView wardrobeView = (((Object)(object)val == (Object)null) ? null : val.GetComponent<WardrobeView>()); if ((Object)(object)wardrobeView != (Object)(object)value.View) { value.View = wardrobeView; value.Applied = false; } if ((Object)(object)wardrobeView != (Object)null) { GiveOnce(value.Player, wardrobeView.Garments); } } if ((Object)(object)value.View == (Object)null || value.View.Frozen || value.View.Garments.Count == 0) { return; } ZNetView obj = NView.Invoke(__instance); ZDO zdo = ((obj != null) ? obj.GetZDO() : null); int num = HelmetHash.Invoke(__instance); int num2 = ChestHash.Invoke(__instance); int num3 = LegHash.Invoke(__instance); int num4 = Quality(value.Player, zdo, HelmetItem, HelmetQualityKey); int num5 = Quality(value.Player, zdo, ChestItem, ChestQualityKey); int num6 = Quality(value.Player, zdo, LegItem, LegQualityKey); if (!value.Applied || num != value.Helmet || num2 != value.Chest || num3 != value.Leg || num4 != value.HelmetQuality || num5 != value.ChestQuality || num6 != value.LegQuality) { if (((Character)value.Player).IsDead()) { value.View.Frozen = true; return; } value.Helmet = num; value.Chest = num2; value.Leg = num3; value.HelmetQuality = num4; value.ChestQuality = num5; value.LegQuality = num6; value.View.Helmet = Pick(value.View.Garments, ItemName(num), num4); value.View.Chest = Pick(value.View.Garments, ItemName(num2), num5); value.View.Leg = Pick(value.View.Garments, ItemName(num3), num6); value.Applied = true; } } catch (Exception ex) { Failed = true; Log.LogError((object)("Wardrobe disabled after an error: " + ex)); } } private static GameObject AvatarRoot(Player player) { object obj = FindInstance.Invoke(null, new object[1] { player }); if (obj != null) { object? obj2 = GetGameObject.Invoke(obj, null); return (GameObject)((obj2 is GameObject) ? obj2 : null); } return null; } private static int Quality(Player player, ZDO zdo, FieldRef<Humanoid, ItemData> itemField, int key) { if (zdo == null || zdo.IsOwner()) { int num = itemField.Invoke((Humanoid)(object)player)?.m_quality ?? 0; if (zdo != null && zdo.GetInt(key, 0) != num) { zdo.Set(key, num, false); } return num; } return zdo.GetInt(key, 0); } private static string ItemName(int hash) { if (hash == 0 || (Object)(object)ObjectDB.instance == (Object)null) { return null; } GameObject itemPrefab = ObjectDB.instance.GetItemPrefab(hash); if (!((Object)(object)itemPrefab == (Object)null)) { return ((Object)itemPrefab).name; } return null; } private static Garment Pick(List<Garment> garments, string item, int quality) { if (item == null) { return null; } List<Garment> source = (from g in garments where g.Item == item orderby g.Level select g).ToList(); return source.LastOrDefault((Garment g) => g.Level <= Math.Max(1, quality)) ?? source.FirstOrDefault(); } private static List<Garment> Collect(GameObject root, out List<Garment> underwear) { List<Garment> list = new List<Garment>(); underwear = new List<Garment>(); Transform[] componentsInChildren = root.GetComponentsInChildren<Transform>(true); foreach (Transform val in componentsInChildren) { string name = ((Object)val).name; if (name.StartsWith("Underwear_", StringComparison.Ordinal)) { Renderer component = ((Component)val).GetComponent<Renderer>(); if ((Object)(object)component != (Object)null) { underwear.Add(new Garment { Node = ((Component)val).gameObject, Renderer = component }); } continue; } int num = name.LastIndexOf("_Q", StringComparison.Ordinal); if (!name.StartsWith("Garment_", StringComparison.Ordinal) || num <= "Garment_".Length) { continue; } int j; for (j = num + 2; j < name.Length && char.IsDigit(name[j]); j++) { } if (j != num + 2 && int.TryParse(name.Substring(num + 2, j - num - 2), out var result)) { Renderer component2 = ((Component)val).GetComponent<Renderer>(); if (!((Object)(object)component2 == (Object)null)) { list.Add(new Garment { Node = ((Component)val).gameObject, Renderer = component2, Item = name.Substring("Garment_".Length, num - "Garment_".Length), Level = result }); } } } if (list.Count == 0 && underwear.Count == 0) { return list; } SkinnedMeshRenderer[] componentsInChildren2 = root.GetComponentsInChildren<SkinnedMeshRenderer>(true); foreach (SkinnedMeshRenderer val2 in componentsInChildren2) { Mesh sharedMesh = val2.sharedMesh; if ((Object)(object)sharedMesh == (Object)null) { continue; } for (int k = 0; k < sharedMesh.blendShapeCount; k++) { string blendShapeName = sharedMesh.GetBlendShapeName(k); if (blendShapeName.StartsWith("Mask_", StringComparison.Ordinal)) { string target = blendShapeName.Substring("Mask_".Length); (list.Find((Garment g) => ((Object)g.Node).name == target) ?? underwear.Find((Garment g) => ((Object)g.Node).name == target))?.Masks.Add(new KeyValuePair<SkinnedMeshRenderer, int>(val2, k)); } } } return list; } private static void GiveOnce(Player player, List<Garment> garments) { if (!GiveTestGarments.Value || garments.Count == 0 || (Object)(object)player != (Object)(object)Player.m_localPlayer) { return; } Inventory inventory = ((Humanoid)player).GetInventory(); if (inventory == null || (Object)(object)ObjectDB.instance == (Object)null) { return; } player.m_customData.TryGetValue("NordicTestGarmentsGivenItems", out var value); HashSet<string> hashSet = new HashSet<string>((value ?? "").Split(new char[1] { ';' }, StringSplitOptions.RemoveEmptyEntries)); List<ItemData> allItems = inventory.GetAllItems(); List<string> list = new List<string>(); List<string> list2 = new List<string>(); int num = 0; foreach (Garment garment in from g in garments orderby g.Item, g.Level select g) { string item = garment.Item + ":" + garment.Level; if (hashSet.Contains(item)) { continue; } if (allItems.Any((ItemData i) => (Object)(object)i.m_dropPrefab != (Object)null && ((Object)i.m_dropPrefab).name == garment.Item && i.m_quality == garment.Level)) { hashSet.Add(item); continue; } if (inventory.GetEmptySlots() == 0) { num++; continue; } hashSet.Add(item); if (inventory.AddItem(garment.Item, 1, garment.Level, 0, player.GetPlayerID(), player.GetPlayerName(), false, false) != null) { list.Add(garment.Item + " quality " + garment.Level); } else { list2.Add(item); } } string text = string.Join(";", hashSet.OrderBy<string, string>((string s) => s, StringComparer.Ordinal).ToArray()); if (text != (value ?? "")) { player.m_customData["NordicTestGarmentsGivenItems"] = text; } if (list.Count > 0) { Log.LogInfo((object)("Test garments given to " + player.GetPlayerName() + ": " + string.Join(", ", list.ToArray()))); } if (list2.Count > 0) { Log.LogWarning((object)("Test garments could not be created: " + string.Join(", ", list2.ToArray()))); } if (num > 0 && (!WarnedFull || list.Count > 0)) { Log.LogWarning((object)(num + " test garments wait for free inventory slots.")); } WarnedFull = num > 0; } } internal sealed class WardrobeView : MonoBehaviour { internal List<Wardrobe.Garment> Garments = new List<Wardrobe.Garment>(); internal List<Wardrobe.Garment> Underwear = new List<Wardrobe.Garment>(); internal Wardrobe.Garment Helmet; internal Wardrobe.Garment Chest; internal Wardrobe.Garment Leg; internal bool Frozen; private void LateUpdate() { foreach (Wardrobe.Garment garment in Garments) { Show(garment, garment == Helmet || garment == Chest || garment == Leg); } bool show = Leg == null || (Object)(object)Leg.Renderer == (Object)null || Leg.Renderer.forceRenderingOff; foreach (Wardrobe.Garment item in Underwear) { Show(item, show); } } private static void Show(Wardrobe.Garment garment, bool show) { if ((Object)(object)garment.Renderer != (Object)null && garment.Renderer.enabled != show) { garment.Renderer.enabled = show; } float num = ((show && (Object)(object)garment.Renderer != (Object)null && !garment.Renderer.forceRenderingOff) ? 100f : 0f); foreach (KeyValuePair<SkinnedMeshRenderer, int> mask in garment.Masks) { if ((Object)(object)mask.Key != (Object)null && mask.Key.GetBlendShapeWeight(mask.Value) != num) { mask.Key.SetBlendShapeWeight(mask.Value, num); } } } } public struct D3 { public double X; public double Y; public double Z; public double this[int k] { get { return k switch { 1 => Y, 0 => X, _ => Z, }; } set { switch (k) { case 0: X = value; break; case 1: Y = value; break; default: Z = value; break; } } } public D3(double x, double y, double z) { X = x; Y = y; Z = z; } public static D3 operator +(D3 a, D3 b) { return new D3(a.X + b.X, a.Y + b.Y, a.Z + b.Z); } public static D3 operator -(D3 a, D3 b) { return new D3(a.X - b.X, a.Y - b.Y, a.Z - b.Z); } public static D3 operator *(D3 a, double s) { return new D3(a.X * s, a.Y * s, a.Z * s); } } public struct M3 { public D3 C0; public D3 C1; public D3 C2; public M3(D3 c0, D3 c1, D3 c2) { C0 = c0; C1 = c1; C2 = c2; } public D3 Col(int k) { return k switch { 1 => C1, 0 => C0, _ => C2, }; } } public struct Q4 { public double X; public double Y; public double Z; public double W; public Q4(double x, double y, double z, double w) { X = x; Y = y; Z = z; W = w; } } public sealed class JiggleStage { public bool On = true; public double[] Freq; public double[] Zeta; public double[] LimPos; public double[] LimNeg; public JiggleStage Clone() { return new JiggleStage { On = On, Freq = (double[])Freq.Clone(), Zeta = (double[])Zeta.Clone(), LimPos = (double[])LimPos.Clone(), LimNeg = (double[])LimNeg.Clone() }; } } public sealed class JiggleSupport { public bool On = true; public double FreqMul = 1.25; public double ZetaMul = 1.35; public double LimMul = 0.6; public double SideLimMul = 0.48; public double InertiaMul = 0.6; public double SwingMul = 0.5; public JiggleSupport Clone() { return (JiggleSupport)MemberwiseClone(); } } public sealed class JiggleSwing { public bool On = true; public double Amount = 1.0; public double RateLo = 300.0; public double RateHi = 800.0; public double Attack = 0.06; public double Release = 0.45; public double FreqMul = 0.6; public double ZetaMul = 1.2; public double LimMul = 1.7; public JiggleSwing Clone() { return (JiggleSwing)MemberwiseClone(); } } public sealed class JiggleParams { public JiggleStage Base = new JiggleStage { Freq = new double[3] { 2.5, 3.2, 5.5 }, Zeta = new double[3] { 0.24, 0.22, 0.4 }, LimPos = new double[3] { 0.025, 0.025, 0.012 }, LimNeg = new double[3] { 0.025, 0.03, 0.008 } }; public JiggleStage Tip = new JiggleStage { On = false, Freq = new double[3] { 5.0, 4.5, 6.0 }, Zeta = new double[3] { 0.2, 0.18, 0.3 }, LimPos = new double[3] { 0.008, 0.01, 0.005 }, LimNeg = new double[3] { 0.008, 0.01, 0.004 } }; public double Inertia = 0.45; public double Gravity = 0.7; public double Amount = 1.0; public double RotShare = 0.75; public double MaxTurnDeg = 32.0; public double Squash; public double Radius = 0.045; public double MarkerFrac = 0.66; public double SkinWeight = 0.85; public double FilterHz = 12.0; public double MaxAccel = 40.0; public double MaxSpeed = 1.5; public double Substep = 1.0 / 240.0; public double SoftStart = 0.8; public double SoftFreq = 8.0; public double HardAt = 1.25; public double Bounce; public JiggleSupport Support = new JiggleSupport(); public JiggleSwing Swing = new JiggleSwing(); public const double SupportBlendSeconds = 0.3; public JiggleParams Clone() { JiggleParams obj = (JiggleParams)MemberwiseClone(); obj.Base = Base.Clone(); obj.Tip = Tip.Clone(); obj.Support = Support.Clone(); obj.Swing = Swing.Clone(); return obj; } public JiggleParams Supported() { if (!Support.On) { return this; } JiggleParams jiggleParams = Clone(); jiggleParams.Inertia = Inertia * Support.InertiaMul; jiggleParams.Swing.Amount = Swing.Amount * Support.SwingMul; JiggleStage[] array = new JiggleStage[2] { jiggleParams.Base, jiggleParams.Tip }; foreach (JiggleStage jiggleStage in array) { for (int j = 0; j < 3; j++) { double num = ((j == 0) ? Support.SideLimMul : Support.LimMul); jiggleStage.Freq[j] *= Support.FreqMul; jiggleStage.Zeta[j] = Math.Min(1.0, jiggleStage.Zeta[j] * Support.ZetaMul); jiggleStage.LimPos[j] *= num; jiggleStage.LimNeg[j] *= num; } } return jiggleParams; } public static void Blend(JiggleParams bare, JiggleParams clothed, double t, JiggleParams into) { JiggleParams jiggleParams = ((t <= 0.0) ? bare : ((t >= 1.0) ? clothed : null)); into.Inertia = jiggleParams?.Inertia ?? (bare.Inertia + (clothed.Inertia - bare.Inertia) * t); into.Swing.Amount = jiggleParams?.Swing.Amount ?? (bare.Swing.Amount + (clothed.Swing.Amount - bare.Swing.Amount) * t); BlendStage(bare.Base, clothed.Base, t, jiggleParams?.Base, into.Base); BlendStage(bare.Tip, clothed.Tip, t, jiggleParams?.Tip, into.Tip); } private static void BlendStage(JiggleStage a, JiggleStage b, double t, JiggleStage src, JiggleStage into) { for (int i = 0; i < 3; i++) { into.Freq[i] = ((src != null) ? src.Freq[i] : (a.Freq[i] + (b.Freq[i] - a.Freq[i]) * t)); into.Zeta[i] = ((src != null) ? src.Zeta[i] : (a.Zeta[i] + (b.Zeta[i] - a.Zeta[i]) * t)); into.LimPos[i] = ((src != null) ? src.LimPos[i] : (a.LimPos[i] + (b.LimPos[i] - a.LimPos[i]) * t)); into.LimNeg[i] = ((src != null) ? src.LimNeg[i] : (a.LimNeg[i] + (b.LimNeg[i] - a.LimNeg[i]) * t)); } } } public sealed class JiggleRest { public D3 Head; public D3 Tail; public Q4 Q0; public M3 Basis; public double Hand = 1.0; } public struct JigglePose { public D3 Position; public D3 Scale; public Q4 Quaternion; } public sealed class BreastJiggle { public const double GRAVITY = 9.81; public readonly JiggleRest Rest; public JiggleParams Params; public D3 X; public D3 V; public D3 Xt; public D3 Vt; public D3 Accel; public double Swing; public double TwistRate; private readonly JiggleStage swingStage = new JiggleStage { Freq = new double[3], Zeta = new double[3], LimPos = new double[3], LimNeg = new double[3] }; private bool hasPrev; private D3 prevA; private D3 prevV; private D3 prevW; private M3 prevM; private int prevN; private double prevDt; public BreastJiggle(JiggleRest rest, JiggleParams p) { Rest = rest; Params = p; Reset(); } public void Reset() { X = default(D3); V = default(D3); Xt = default(D3); Vt = default(D3); hasPrev = false; Accel = default(D3); Swing = 0.0; TwistRate = 0.0; } public static JiggleRest MakeRest(D3 head, Q4 q0, D3 tailLocal, M3 parentRot, D3 headWorld, D3 midWorld) { D3 d = head + QRotate(q0, tailLocal); D3 d2 = MulMtV(parentRot, new D3(0.0, 1.0, 0.0)); D3 d3 = d - head; d3 = Norm(d3 - d2 * Dot(d3, d2)); D3 d4 = Cross(d2, d3); if (Dot(MulMV(parentRot, d4), headWorld - midWorld) < 0.0) { d4 *= -1.0; } double hand = ((!(Dot(Cross(d4, d2), d3) < 0.0)) ? 1 : (-1)); return new JiggleRest { Head = head, Q0 = q0, Tail = d, Basis = new M3(d4, d2, d3), Hand = hand }; } private D3 MarkerLocal() { return Rest.Head + (Rest.Tail - Rest.Head) * Math.Max(0.05, Params.MarkerFrac); } public void Step(double dt, D3 parentPos, M3 parentRot, double parentScale = 1.0) { JiggleParams jiggleParams = Params; JiggleRest rest = Rest; D3 d = parentPos + MulMV(parentRot, MarkerLocal()) * parentScale; M3 m = MulMM(parentRot, rest.Basis); if (!(dt > 0.0)) { return; } double num = 0.0; if (hasPrev) { double num2 = 0.0; for (int i = 0; i < 3; i++) { num2 += Dot(prevM.Col(i), m.Col(i)); } num = Math.Acos(Math.Max(-1.0, Math.Min(1.0, (num2 - 1.0) / 2.0))); } if (!hasPrev || dt > 0.1 || Len(d - prevA) > 1.0 || num > Math.PI / 2.0) { hasPrev = true; prevA = d; prevM = m; prevV = default(D3); prevW = default(D3); prevN = 0; prevDt = dt; return; } D3 d2 = default(D3); for (int j = 0; j < 3; j++) { d2 += Cross(prevM.Col(j), m.Col(j)); } double num3 = Len(d2); d2 = ((num3 > 1E-12) ? (d2 * (num / (dt * num3))) : default(D3)); D3 d3 = (d - prevA) * (1.0 / dt); double t = ((jiggleParams.FilterHz > 0.0) ? (1.0 - Math.Exp(Math.PI * -2.0 * jiggleParams.FilterHz * dt)) : 1.0); bool num4 = prevN == 0; D3 d4 = (num4 ? d3 : Lerp(prevV, d3, t)); D3 d5 = (num4 ? d2 : Lerp(prevW, d2, t)); double num5 = (prevDt + dt) / 2.0; D3 d6 = (num4 ? default(D3) : ((d4 - prevV) * (1.0 / num5))); D3 v = (num4 ? default(D3) : ((d5 - prevW) * (1.0 / num5))); d6 *= 1.0 / Math.Sqrt(1.0 + Dot(d6, d6) / (jiggleParams.MaxAccel * jiggleParams.MaxAccel)); D3 accel = MulMtV(prevM, d6); D3 a = MulMtV(m, d5) * rest.Hand; D3 a2 = MulMtV(m, v) * rest.Hand; D3 a3 = MulMtV(prevM, new D3(0.0, -9.81, 0.0)); D3 b = MulMtV(m, new D3(0.0, -9.81, 0.0)); Accel = accel; JiggleStage jiggleStage = jiggleParams.Base; JiggleSwing swing = jiggleParams.Swing; TwistRate = a.Y; if (swing != null && swing.On) { double num6 = Math.Min(1.0, Math.Max(0.0, (Math.Abs(a.Y) * 180.0 / Math.PI - swing.RateLo) / (swing.RateHi - swing.RateLo))); double num7 = num6 * num6 * (3.0 - 2.0 * num6); Swing += (num7 - Swing) * (1.0 - Math.Exp((0.0 - dt) / ((num7 > Swing) ? swing.Attack : swing.Release))); double num8 = Swing * swing.Amount; if (num8 > 0.0) { JiggleStage jiggleStage2 = jiggleParams.Base; jiggleStage = swingStage; double num9 = 1.0 + (swing.FreqMul - 1.0) * num8; double num10 = 1.0 + (swing.ZetaMul - 1.0) * num8; double num11 = 1.0 + (swing.LimMul - 1.0) * num8; for (int k = 0; k < 3; k++) { jiggleStage.Freq[k] = jiggleStage2.Freq[k]; jiggleStage.Zeta[k] = jiggleStage2.Zeta[k]; jiggleStage.LimPos[k] = jiggleStage2.LimPos[k]; jiggleStage.LimNeg[k] = jiggleStage2.LimNeg[k]; } jiggleStage.Freq[0] = jiggleStage2.Freq[0] * num9; jiggleStage.Zeta[0] = Math.Min(1.0, jiggleStage2.Zeta[0] * num10); jiggleStage.LimPos[0] = jiggleStage2.LimPos[0] * num11; jiggleStage.LimNeg[0] = jiggleStage2.LimNeg[0] * num11; } } int num12 = Math.Max(1, (int)Math.Ceiling(dt / jiggleParams.Substep - 1E-09)); double num13 = dt / (double)num12; for (int l = 1; l <= num12; l++) { D3 d7 = Lerp(a3, b, (double)l / (double)num12); D3 d8 = Cross(a, V); D3 d9 = Cross(a2, X); D3 d10 = Cross(a, Cross(a, X)); D3 d11 = default(D3); D3 a4 = default(D3); for (int n = 0; n < 3; n++) { d11[n] = jiggleParams.Inertia * (0.0 - accel[n] - 2.0 * d8[n] - d9[n] - d10[n]) + jiggleParams.Gravity * (d7[n] - ((n == 1) ? (-9.81) : 0.0)); double num14 = Math.PI * 2.0 * jiggleStage.Freq[n]; a4[n] = d11[n] - num14 * num14 * X[n] - 2.0 * jiggleStage.Zeta[n] * num14 * V[n]; } SoftLimit(ref a4, X, jiggleStage, parentScale, jiggleParams); for (int num15 = 0; num15 < 3; num15++) { V[num15] += a4[num15] * num13; } double num16 = Len(V); double num17 = jiggleParams.MaxSpeed * parentScale; if (num16 > num17) { V *= num17 / num16; } for (int num18 = 0; num18 < 3; num18++) { X[num18] += V[num18] * num13; } HardLimit(ref X, ref V, jiggleStage, parentScale, jiggleParams.HardAt, jiggleParams.Bounce); if (jiggleParams.Tip.On) { for (int num19 = 0; num19 < 3; num19++) { double num20 = Math.PI * 2.0 * jiggleParams.Tip.Freq[num19]; double num21 = 0.0 - (a4[num19] - d11[num19]) - num20 * num20 * Xt[num19] - 2.0 * jiggleParams.Tip.Zeta[num19] * num20 * Vt[num19]; Vt[num19] += num21 * num13; Xt[num19] += Vt[num19] * num13; } HardLimit(ref Xt, ref Vt, jiggleParams.Tip, parentScale, jiggleParams.HardAt, jiggleParams.Bounce); } } if (!jiggleParams.Tip.On) { Xt = default(D3); Vt = default(D3); } prevA = d; prevM = m; prevV = d4; prevW = d5; prevN++; prevDt = dt; } public D3 Displacement() { return X + Xt; } public JigglePose Pose(double parentScale = 1.0) { JiggleParams jiggleParams = Params; JiggleRest rest = Rest; M3 basis = rest.Basis; double num = jiggleParams.Amount / (parentScale * jiggleParams.SkinWeight); D3 d = MulMV(basis, X) * num; D3 d2 = MulMV(basis, Xt) * num; D3 d3 = d + d2; D3 d4 = MarkerLocal() - rest.Head; double num2 = Len(d4); D3 d5 = d4 * (1.0 / num2); D3 d6 = d * jiggleParams.RotShare + d2; d6 -= d5 * Dot(d6, d5); double num3 = num2 * Math.Tan(jiggleParams.MaxTurnDeg * Math.PI / 180.0); double num4 = Len(d6); if (num4 > num3) { d6 *= num3 / num4; } Q4 q = QFromTo(d5, Norm(d4 + d6)); double num5 = Math.Max(-0.1, Math.Min(0.1, jiggleParams.Amount * jiggleParams.Squash * X.Y / (jiggleParams.Radius * parentScale))); double num6 = 1.0 - num5; double num7 = 1.0 / Math.Sqrt(num6); D3 d7 = QRotate(QConj(rest.Q0), basis.C1); D3 scale = ((Math.Abs(d7.X) >= Math.Abs(d7.Z)) ? new D3(num6, num7, num7) : new D3(num7, num7, num6)); D3 d8 = QRotate(QConj(rest.Q0), d4); D3 d9 = QRotate(QMul(q, rest.Q0), new D3(d8.X * scale.X, d8.Y * scale.Y, d8.Z * scale.Z)); D3 position = rest.Head + (d3 - (d9 - d4)); return new JigglePose { Position = position, Quaternion = QMul(q, rest.Q0), Scale = scale }; } private static double Ellipsoid(D3 x, JiggleStage lim, double s, out D3 L) { double num = 0.0; L = default(D3); for (int i = 0; i < 3; i++) { L[i] = ((x[i] >= 0.0) ? lim.LimPos[i] : lim.LimNeg[i]) * s; num += x[i] / L[i] * (x[i] / L[i]); } return Math.Sqrt(num); } private static void SoftLimit(ref D3 a, D3 x, JiggleStage lim, double s, JiggleParams p) { D3 L; double num = Ellipsoid(x, lim, s, out L); if (!(num <= p.SoftStart)) { double num2 = (num - p.SoftStart) / (1.0 - p.SoftStart); double num3 = Math.PI * 2.0 * p.SoftFreq; double num4 = num3 * num3 * num2 * num2; D3 b = Norm(new D3(x.X / (L.X * L.X), x.Y / (L.Y * L.Y), x.Z / (L.Z * L.Z))); double num5 = Dot(x, b); for (int i = 0; i < 3; i++) { a[i] -= num4 * num5 * b[i]; } } } private static bool HardLimit(ref D3 x, ref D3 v, JiggleStage lim, double s, double hardAt, double bounce) { D3 L; double num = Ellipsoid(x, lim, s, out L); if (num <= hardAt) { return false; } for (int i = 0; i < 3; i++) { x[i] *= hardAt / num; } D3 b = Norm(new D3(x.X / (L.X * L.X), x.Y / (L.Y * L.Y), x.Z / (L.Z * L.Z))); double num2 = Dot(v, b); if (num2 > 0.0) { for (int j = 0; j < 3; j++) { v[j] -= (1.0 + bounce) * num2 * b[j]; } } return true; } private static double Dot(D3 a, D3 b) { return a.X * b.X + a.Y * b.Y + a.Z * b.Z; } private static D3 Cross(D3 a, D3 b) { return new D3(a.Y * b.Z - a.Z * b.Y, a.Z * b.X - a.X * b.Z, a.X * b.Y - a.Y * b.X); } private static double Len(D3 a) { return Math.Sqrt(Dot(a, a)); } private static D3 Lerp(D3 a, D3 b, double t) { return new D3(a.X + (b.X - a.X) * t, a.Y + (b.Y - a.Y) * t, a.Z + (b.Z - a.Z) * t); } private static D3 Norm(D3 a) { double num = Len(a); if (!(num > 1E-12)) { return default(D3); } return a * (1.0 / num); } private static D3 MulMV(M3 m, D3 v) { return new D3(m.C0.X * v.X + m.C1.X * v.Y + m.C2.X * v.Z, m.C0.Y * v.X + m.C1.Y * v.Y + m.C2.Y * v.Z, m.C0.Z * v.X + m.C1.Z * v.Y + m.C2.Z * v.Z); } private static D3 MulMtV(M3 m, D3 v) { return new D3(Dot(m.C0, v), Dot(m.C1, v), Dot(m.C2, v)); } private static M3 MulMM(M3 a, M3 b) { return new M3(MulMV(a, b.C0), MulMV(a, b.C1), MulMV(a, b.C2)); } private static Q4 QMul(Q4 q, Q4 r) { return new Q4(q.W * r.X + q.X * r.W + q.Y * r.Z - q.Z * r.Y, q.W * r.Y - q.X * r.Z + q.Y * r.W + q.Z * r.X, q.W * r.Z + q.X * r.Y - q.Y * r.X + q.Z * r.W, q.W * r.W - q.X * r.X - q.Y * r.Y - q.Z * r.Z); } private static Q4 QConj(Q4 q) { return new Q4(0.0 - q.X, 0.0 - q.Y, 0.0 - q.Z, q.W); } private static Q4 QFromTo(D3 a, D3 b) { D3 d = Cross(a, b); double num = 1.0 + Dot(a, b); if (num < 1E-08) { return new Q4(0.0, 0.0, 0.0, 1.0); } double num2 = Math.Sqrt(d.X * d.X + d.Y * d.Y + d.Z * d.Z + num * num); return new Q4(d.X / num2, d.Y / num2, d.Z / num2, num / num2); } public static D3 QRotate(Q4 q, D3 v) { D3 a = new D3(q.X, q.Y, q.Z); D3 d = Cross(a, v) * 2.0; return v + d * q.W + Cross(a, d); } } internal static class BreastPhysics { internal static ManualLogSource Log; internal static ConfigEntry<bool> Enabled; internal static ConfigEntry<float> Amount; internal static ConfigEntry<float> RecordSeconds; private static FieldInfo Owner; private static readonly FieldInfo RuntimeField = AccessTools.DeclaredField(typeof(Vrm10Instance), "m_runtime"); internal static readonly List<BreastJiggleDriver> Drivers = new List<BreastJiggleDriver>(); internal static int ServiceFrame = -1; internal static void Install(Assembly enhancedAssembly, ConfigFile config, ManualLogSource log) { //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Expected O, but got Unknown //IL_00e7: Unknown result type (might be due to invalid IL or missing references) //IL_00fe: Unknown result type (might be due to invalid IL or missing references) //IL_010b: Expected O, but got Unknown Log = log; Enabled = config.Bind<bool>("BreastPhysics", "Enabled", true, "Soft breast physics for the Nordic avatars (Sigrid, Dark Urge) instead of their VRM breast springs. Applies to avatars loaded after the change: restart the game after switching it."); Amount = config.Bind<float>("BreastPhysics", "Amount", 1f, new ConfigDescription("Strength of the breast motion: 0 = still, 1 = default. Read every frame (the config file itself is read at game start).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1.2f), Array.Empty<object>())); RecordSeconds = config.Bind<float>("BreastPhysics", "RecordSeconds", 0f, "Tuning only: above 0, each avatar writes this many seconds of chest motion and breast offsets to BepInEx/breast-physics-records/*.csv, starting when the avatar appears and again whenever the setting changes in game."); Type type = enhancedAssembly.GetType("EnhancedValheimVRM.VrmInstance", throwOnError: false); Owner = (((object)type != null) ? AccessTools.DeclaredField(type, "_player") : null); if (!Enabled.Value) { log.LogInfo((object)"Breast physics disabled in the config ([BreastPhysics] Enabled = false)."); return; } MethodInfo methodInfo = AccessTools.DeclaredMethod(typeof(FastSpringBoneService), "LateUpdate", Type.EmptyTypes, (Type[])null); try { if (methodInfo == null) { throw new MissingMethodException("FastSpringBoneService.LateUpdate"); } new Harmony("local.valheim.vrm1springclonefix.breastphysics").Patch((MethodBase)methodInfo, (HarmonyMethod)null, new HarmonyMethod(typeof(BreastPhysics), "AfterSprings", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } catch (Exception ex) { log.LogWarning((object)("Breast physics: spring service hook unavailable, avatars step in their own LateUpdate: " + ex.Message)); } log.LogInfo((object)$"Breast physics enabled for Nordic avatars (Amount {Amount.Value:0.##})."); } private static void AfterSprings() { ServiceFrame = Time.frameCount; for (int num = Drivers.Count - 1; num >= 0; num--) { BreastJiggleDriver breastJiggleDriver = Drivers[num]; if ((Object)(object)breastJiggleDriver == (Object)null) { Drivers.RemoveAt(num); } else { try { breastJiggleDriver.Tick(); } catch (Exception ex) { Log.LogError((object)("Breast physics step failed, driver stopped: " + ex)); ((Behaviour)breastJiggleDriver).enabled = false; } } } } internal static void Attach(object vrmInstance, GameObject root) { if (Enabled == null || !Enabled.Value || (Object)(object)root == (Object)null) { return; } Vrm10Instance component = root.GetComponent<Vrm10Instance>(); if ((Object)(object)component == (Object)null || component.SpringBone == null || (Object)(object)root.GetComponent<BreastJiggleDriver>() != (Object)null) { return; } string text = JigglePresets.AvatarOf(root); if (text == null) { return; } object? obj = Owner?.GetValue(vrmInstance); Player val = (Player)((obj is Player) ? obj : null); string text2 = text + (((Object)(object)val == (Object)null) ? "" : (" (" + val.GetPlayerName() + ")")); List<Spring> springs = component.SpringBone.Springs; List<Spring> list = springs.Where((Spring sp) => sp.Joints.Count > 0 && (Object)(object)sp.Joints[0] != (Object)null && JigglePresets.IsBreastBone(((Object)((Component)sp.Joints[0]).transform).name)).ToList(); List<(Transform, Transform)> list2 = list.Select((Spring sp) => (bone: ((Component)sp.Joints[0]).transform, end: (sp.Joints.Count > 1 && (Object)(object)sp.Joints[1] != (Object)null) ? ((Component)sp.Joints[1]).transform : null)).ToList(); List<Transform> list3 = list2.Select<(Transform, Transform), Transform>(((Transform bone, Transform end) x) => x.bone).Distinct().ToList(); if (list.Count != 2 || list3.Count != 2 || list2.Any<(Transform, Transform)>(((Transform bone, Transform end) x) => (Object)(object)x.end == (Object)null || (Object)(object)x.end.parent != (Object)(object)x.bone || (Object)(object)x.bone.parent == (Object)null || !x.bone.IsChildOf(root.transform))) { Log.LogWarning((object)$"Breast physics not attached to {text2}: expected two breast springs with end bones, found {list.Count}; VRM springs kept."); return; } JiggleParams jiggleParams = JigglePresets.For(text); BreastJiggleDriver breastJiggleDriver = root.AddComponent<BreastJiggleDriver>(); if (!breastJiggleDriver.Setup(list2, jiggleParams, text2)) { Object.Destroy((Object)(object)breastJiggleDriver); Log.LogWarning((object)("Breast physics not attached to " + text2 + ": rest pose unusable; VRM springs kept.")); return; } List<Spring> collection = springs.ToList(); try { foreach (Spring item in list) { springs.Remove(item); } if (RuntimeField != null && RuntimeField.GetValue(component) != null) { component.Runtime.ReconstructSpringBone(); } } catch { springs.Clear(); springs.AddRange(collection); Object.Destroy((Object)(object)breastJiggleDriver); if (RuntimeField != null && RuntimeField.GetValue(component) != null) { component.Runtime.ReconstructSpringBone(); } throw; } Log.LogInfo((object)("Breast physics attached to " + text2 + ", clone " + ((Object)root).name + ": bones " + string.Join(", ", list3.Select((Transform b) => ((Object)b).name)) + "; " + $"bare {Describe(jiggleParams)}; clothed {Describe(jiggleParams.Supported())}; Amount {Amount.Value:0.##}. " + "Starts bare; a change to clothed or bare is logged when the worn chest garment changes.")); } internal static string Describe(JiggleParams p) { return FormattableString.Invariant($"freq out/up/fwd {p.Base.Freq[0]:0.##}/{p.Base.Freq[1]:0.##}/{p.Base.Freq[2]:0.##} Hz, damping {p.Base.Zeta[0]:0.###}/{p.Base.Zeta[1]:0.###}/{p.Base.Zeta[2]:0.###}, ") + FormattableString.Invariant($"travel out/up/down/fwd {p.Base.LimPos[0] * 1000.0:0.#}/{p.Base.LimPos[1] * 1000.0:0.#}/{p.Base.LimNeg[1] * 1000.0:0.#}/{p.Base.LimPos[2] * 1000.0:0.#} mm, inertia {p.Inertia:0.###}") + FormattableString.Invariant($", swing-out {(p.Swing.On ? p.Swing.Amount : 0.0):0.##}"); } } internal static class JigglePresets { internal static bool IsBreastBone(string name) { if (!(name == "CC_Base_L_Breast")) { return name == "CC_Base_R_Breast"; } return true; } internal static string AvatarOf(GameObject root) { Transform[] componentsInChildren = root.GetComponentsInChildren<Transform>(true); foreach (Transform val in componentsInChildren) { if (((Object)val).name == "Sigrid_Breast_L_End") { return "Sigrid"; } if (((Object)val).name == "DarkUrge_Breast_L_End") { return "DarkUrge"; } } return null; } internal static JiggleParams For(string avatar) { return new JiggleParams(); } } [DefaultExecutionOrder(11020)] internal sealed class BreastJiggleDriver : MonoBehaviour { private sealed class Breast { public Transform Bone; public Transform Parent; public Vector3 RestPos; public Vector3 RestScale; public Quaternion RestRot; public BreastJiggle Solver; } private readonly List<Breast> breasts = new List<Breast>(); private JiggleParams bare; private JiggleParams clothed; private JiggleParams blended; private double support; private bool wearing; private bool known; private bool loggedStep; private WardrobeView wardrobe; private int steppedFrame = -1; private string who; private StringBuilder record; private float recordLeft; private float recordAsked; internal bool Setup(List<(Transform bone, Transform end)> pairs, JiggleParams p, string label) { //IL_00ee: 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_00fa: Unknown result type (might be due to invalid IL or missing references) //IL_0100: 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_010e: Unknown result type (might be due to invalid IL or missing references) //IL_0115: Unknown result type (might be due to invalid IL or missing references) //IL_011c: Unknown result type (might be due to invalid IL or missing references) //IL_0123: Unknown result type (might be due to invalid IL or missing references) //IL_0150: 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_0158: Unknown result type (might be due to invalid IL or missing references) //IL_015a: Unknown result type (might be due to invalid IL or missing references) //IL_0161: Unknown result type (might be due to invalid IL or missing references) //IL_0166: Unknown result type (might be due to invalid IL or missing references) who = label; bare = p; clothed = p.Supported(); blended = p.Clone(); breasts.Clear(); RuntimeGltfInstance component = ((Component)this).GetComponent<RuntimeGltfInstance>(); IReadOnlyDictionary<Transform, TransformState> states = ((component != null) ? component.InitialTransformStates : null); Transform val = FindDeep(((Component)this).transform, "CC_Base_Hip"); foreach (var (val2, val3) in pairs.OrderBy(((Transform bone, Transform end) x) => (!((Object)x.bone).name.Contains("_L_")) ? 1 : 0)) { if (!((Object)(object)val3 == (Object)null) && !((Object)(object)val2.parent == (Object)null)) { RestTRS(val2.parent, states, out var _, out var rot); RestTRS(val2, states, out var pos2, out var rot2); RestTRS(val ?? ((Component)this).transform, states, out var pos3, out rot2); Vector3 pos4; Quaternion val4 = Local(val2, states, out pos4); Local(val3, states, out var pos5); JiggleRest rest = BreastJiggle.MakeRest(D(pos4), Q(val4), D(pos5), M(rot), D(pos2), D(pos3)); breasts.Add(new Breast { Bone = val2, Parent = val2.parent, RestPos = pos4, RestRot = val4, RestScale = val2.localScale, Solver = new BreastJiggle(rest, bare) }); } } foreach (Breast breast in breasts) { JiggleRest rest2 = breast.Solver.Rest; CultureInfo inv = CultureInfo.InvariantCulture; BreastPhysics.Log.LogDebug((object)(string.Format("Breast physics {0}/{1}: hand {2}, out {3}, up {4}, ", who, ((Object)breast.Bone).name, rest2.Hand, V(rest2.Basis.C0, "0.###"), V(rest2.Basis.C1, "0.###")) + "fwd " + V(rest2.Basis.C2, "0.###") + ", head " + V(rest2.Head, "0.####") + ", tail " + V(rest2.Tail, "0.####"))); string V(D3 v, string f) { return v.X.ToString(f, inv) + "," + v.Y.ToString(f, inv) + "," + v.Z.ToString(f, inv); } } if (breasts.Count == 2) { return breasts.All((Breast b) => Finite(b.Solver.Rest.Basis.C0) && Finite(b.Solver.Rest.Basis.C1) && Finite(b.Solver.Rest.Basis.C2)); } return false; } private void OnEnable() { foreach (Breast breast in breasts) { breast.Solver.Reset(); } if (!BreastPhysics.Drivers.Contains(this)) { BreastPhysics.Drivers.Add(this); } } private void OnDisable() { //IL_0037: 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_0059: Unknown result type (might be due to invalid IL or missing references) BreastPhysics.Drivers.Remove(this); foreach (Breast breast in breasts) { if ((Object)(object)breast.Bone != (Object)null) { breast.Bone.localPosition = breast.RestPos; breast.Bone.localRotation = breast.RestRot; breast.Bone.localScale = breast.RestScale; } } } private void LateUpdate() { if (BreastPhysics.ServiceFrame >= Time.frameCount - 1) { return; } try { Tick(); } catch (Exception ex) { BreastPhysics.Log.LogError((object)("Breast physics step failed, driver stopped: " + ex)); ((Behaviour)this).enabled = false; } } internal void Tick() { //IL_0162: 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_0169: Unknown result type (might be due to invalid IL or missing references) //IL_0175: 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_01ac: Unknown result type (might be due to invalid IL or missing references) //IL_01bd: Unknown result type (might be due to invalid IL or missing references) //IL_0275: Unknown result type (might be due to invalid IL or missing references) //IL_027e: Unknown result type (might be due to invalid IL or missing references) //IL_0287: Unknown result type (might be due to invalid IL or missing references) //IL_02a5: Unknown result type (might be due to invalid IL or missing references) //IL_02b3: Unknown result type (might be due to invalid IL or missing references) //IL_02c1: Unknown result type (might be due to invalid IL or missing references) //IL_02c5: Unknown result type (might be due to invalid IL or missing references) //IL_02ca: Unknown result type (might be due to invalid IL or missing references) if (steppedFrame == Time.frameCount || !((Behaviour)this).isActiveAndEnabled || bare == null || breasts.Count != 2) { return; } steppedFrame = Time.frameCount; float deltaTime = Time.deltaTime; if (!loggedStep) { loggedStep = true; BreastPhysics.Log.LogInfo((object)($"Breast physics {who}: first step at frame {Time.frameCount}, dt {deltaTime:0.0000}, " + ((BreastPhysics.ServiceFrame == Time.frameCount) ? "right after the spring service." : "in its own LateUpdate (spring service not ticking)."))); } UpdateSupport(deltaTime); double amount = BreastPhysics.Amount?.Value ?? 1f; bare.Amount = amount; clothed.Amount = amount; blended.Amount = amount; float num = BreastPhysics.RecordSeconds?.Value ?? 0f; if (num != recordAsked) { recordAsked = num; recordLeft = num; record = ((num > 0f) ? new StringBuilder("frame,time,dt,support,L_px,L_py,L_pz,L_qx,L_qy,L_qz,L_qw,L_x,L_y,L_z,R_px,R_py,R_pz,R_qx,R_qy,R_qz,R_qw,R_x,R_y,R_z\n") : null); } Vector3 val = default(Vector3); Quaternion val2 = default(Quaternion); Vector3 val3 = default(Vector3); foreach (Breast breast in breasts) { if (!((Object)(object)breast.Bone == (Object)null) && !((Object)(object)breast.Parent == (Object)null)) { Vector3 lossyScale = breast.Parent.lossyScale; double parentScale = (double)(Mathf.Abs(lossyScale.x) + Mathf.Abs(lossyScale.y) + Mathf.Abs(lossyScale.z)) / 3.0; breast.Solver.Step(deltaTime, D(breast.Parent.position), M(breast.Parent.rotation), parentScale); JigglePose jigglePose = breast.Solver.Pose(parentScale); ((Vector3)(ref val))..ctor((float)jigglePose.Position.X, (float)jigglePose.Position.Y, (float)jigglePose.Position.Z); ((Quaternion)(ref val2))..ctor((float)jigglePose.Quaternion.X, (float)jigglePose.Quaternion.Y, (float)jigglePose.Quaternion.Z, (float)jigglePose.Quaternion.W); ((Vector3)(ref val3))..ctor((float)jigglePose.Scale.X, (float)jigglePose.Scale.Y, (float)jigglePose.Scale.Z); if (!Finite(val) || !Finite(val3) || !Finite(val2)) { breast.Solver.Reset(); continue; } breast.Bone.localPosition = val; breast.Bone.localRotation = val2; breast.Bone.localScale = Vector3.Scale(val3, breast.RestScale); } } if (record != null) { Record(deltaTime); } } private void UpdateSupport(float dt) { if ((Object)(object)wardrobe == (Object)null && (!known || Time.frameCount % 30 == 0)) { wardrobe = ((Component)this).GetComponent<WardrobeView>(); } Wardrobe.Garment garment = (((Object)(object)wardrobe == (Object)null) ? null : wardrobe.Chest); bool flag = garment != null && (Object)(object)garment.Renderer != (Object)null && !garment.Renderer.forceRenderingOff; if (flag != wearing || !known) { if (known || flag) { BreastPhysics.Log.LogInfo((object)("Breast physics " + who + ": " + (flag ? ("clothed (" + ((Object)garment.Node).name + "), supported parameters") : "bare parameters") + ".")); } wearing = flag; known = true; } double num = (wearing ? 1 : 0); if (support != num && dt > 0f) { double num2 = (double)dt / 0.3; support = ((num > support) ? Math.Min(num, support + num2) : Math.Max(num, support - num2)); } JiggleParams jiggleParams; if (support <= 0.0) { jiggleParams = bare; } else if (support >= 1.0) { jiggleParams = clothed; } else { JiggleParams.Blend(bare, clothed, support, blended); jiggleParams = blended; } foreach (Breast breast in breasts) { breast.Solver.Params = jiggleParams; } } private void Record(float dt) { //IL_00a5: Unknown result type (might be due to invalid IL or missing references) //IL_00aa: 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_00b7: Unknown result type (might be due to invalid IL or missing references) //IL_00cf: 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_00e5: 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_00fb: Unknown result type (might be due to invalid IL or missing references) //IL_0106: 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) CultureInfo invariantCulture = CultureInfo.InvariantCulture; record.Append(Time.frameCount).Append(',').Append(Time.unscaledTime.ToString("R", invariantCulture)) .Append(',') .Append(dt.ToString("R", invariantCulture)) .Append(',') .Append(support.ToString("R", invariantCulture)); foreach (Breast breast in breasts) { if ((Object)(object)breast.Parent == (Object)null) { record = null; return; } Vector3 position = breast.Parent.position; Quaternion rotation = breast.Parent.rotation; D3 x = breast.Solver.X; double[] array = new double[10] { position.x, position.y, position.z, rotation.x, rotation.y, rotation.z, rotation.w, x.X, x.Y, x.Z }; foreach (double num in array) { record.Append(',').Append(num.ToString("R", invariantCulture)); } } record.Append('\n'); recordLeft -= dt; if (recordLeft > 0f) { return; } try { string text = Path.Combine(Paths.BepInExRootPath, "breast-physics-records"); Directory.CreateDirectory(text); string arg = new string(who.Select((char c) => (!char.IsLetterOrDigit(c)) ? '_' : c).ToArray()); string text2 = Path.Combine(text, $"{arg}-{((Object)this).GetInstanceID()}-{DateTime.Now:yyyyMMdd-HHmmss}.csv"); File.WriteAllText(text2, record.ToString()); BreastPhysics.Log.LogInfo((object)("Breast physics record written: " + text2)); } catch (Exception ex) { BreastPhysics.Log.LogError((object)("Breast physics record failed: " + ex.Message)); } record = null; } private static Quaternion Local(Transform t, IReadOnlyDictionary<Transform, TransformState> states, out Vector3 pos) { //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_002a: 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_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0016: 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) if (states != null && states.TryGetValue(t, out var value)) { pos = ((TransformState)(ref value)).LocalPosition; return ((TransformState)(ref value)).LocalRotation; } pos = t.localPosition; return t.localRotation; } private void RestTRS(Transform t, IReadOnlyDictionary<Transform, TransformState> states, out Vector3 pos, out Quaternion rot) { //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_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0012: 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) //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_006a: 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) //IL_0071: 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) //IL_007b: Unknown result type (might be due to invalid IL or missing references) //IL_0084: 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_008a: Unknown result type (might be due to invalid IL or missing references) //IL_008f: Unknown result type (might be due to invalid IL or missing references) pos = Vector3.zero; rot = Quaternion.identity; List<Transform> list = new List<Transform>(); Transform val = t; while ((Object)(object)val != (Object)null && (Object)(object)val != (Object)(object)((Component)this).transform) { list.Add(val); val = val.parent; } for (int num = list.Count - 1; num >= 0; num--) { Vector3 pos2; Quaternion val2 = Local(list[num], states, out pos2); pos += rot * pos2; rot *= val2; } } private static Transform FindDeep(Transform t, string name) { if (((Object)t).name == name) { return t; } for (int i = 0; i < t.childCount; i++) { Transform val = FindDeep(t.GetChild(i), name); if ((Object)(object)val != (Object)null) { return val; } } return null; } private static D3 D(Vector3 v) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Unknown result type (might be due to invalid IL or missing references) return new D3(v.x, v.y, v.z); } private static Q4 Q(Quaternion q) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) return new Q4(q.x, q.y, q.z, q.w); } private static M3 M(Quaternion q) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //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_0010: 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_0016: Unknown result type (might be due to invalid IL or missing references) //IL_0020: 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_0026: Unknown result type (might be due to invalid IL or missing references) return new M3(D(q * Vector3.right), D(q * Vector3.up), D(q * Vector3.forward)); } private static bool Finite(float v) { if (!float.IsNaN(v)) { return !float.IsInfinity(v); } return false; } private static bool Finite(Vector3 v) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_000d: 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) if (Finite(v.x) && Finite(v.y)) { return Finite(v.z); } return false; } private static bool Finite(Quaternion q) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_000d: 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_0027: Unknown result type (might be due to invalid IL or missing references) if (Finite(q.x) && Finite(q.y) && Finite(q.z)) { return Finite(q.w); } return false; } private static bool Finite(D3 v) { if (!double.IsNaN(v.X + v.Y + v.Z)) { return !double.IsInfinity(v.X + v.Y + v.Z); } return false; } } } namespace LocalValheimFixes.Packages { [DataContract] internal sealed class AvatarPackManifest { [DataMember(Name = "schemaVersion", IsRequired = true)] public int SchemaVersion { get; set; } [DataMember(Name = "packId", IsRequired = true)] public string PackId { get; set; } [DataMember(Name = "avatars", IsRequired = true)] public AvatarPackEntry[] Avatars { get; set; } } [DataContract] internal sealed class AvatarPackEntry { [DataMember(Name = "character", IsRequired = true)] public string Character { get; set; } [DataMember(Name = "model", IsRequired = true)] public string Model { get; set; } [DataMember(Name = "settings", IsRequired = true)] public string Settings { get; set; } [DataMember(Name = "outfits", EmitDefaultValue = false)] public string Outfits { get; set; } } public sealed class AvatarBinding { public string Character { get; } public string PackId { get; } public string PackDirectory { get; } public string ModelPath { get; } public string SettingsPath { get; } public string OutfitPath { get; } internal AvatarBinding(string character, string packId, string directory, string model, string settings, string outfits) { Character = character; PackId = packId; PackDirectory = directory; ModelPath = model; SettingsPath = settings; OutfitPath = outfits; } } public sealed class AvatarPackIndex { private const string Marker = "avatar-pack.json"; private const string AbsentOutfitDirectory = "__vrm_fixes_no_outfits__"; private readonly IReadOnlyDictionary<string, string> _files; private readonly string[] _protectedDirectories; public IReadOnlyList<AvatarBinding> Bindings { get; } public IReadOnlyList<string> Diagnostics { get; } private AvatarPackIndex(IEnumerable<AvatarBinding> bindings, IEnumerable<string> diagnostics) { AvatarBinding[] array = bindings.OrderBy<AvatarBinding, string>((AvatarBinding x) => x.Character, StringComparer.OrdinalIgnoreCase).ToArray(); Dictionary<string, string> dictionary = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase); AvatarBinding[] array2 = array; foreach (AvatarBinding avatarBinding in array2) { dictionary.Add(avatarBinding.Character + ".vrm", avatarBinding.ModelPath); dictionary.Add("settings_" + avatarBinding.Character + ".txt", avatarBinding.SettingsPath); dictionary.Add("outfits_" + avatarBinding.Character + ".txt", avatarBinding.OutfitPath); } _files = new ReadOnlyDictionary<string, string>(dictionary); _protectedDirectories = array.Select((AvatarBinding x) => WithSeparator(x.PackDirectory)).Distinct<string>(StringComparer.OrdinalIgnoreCase).ToArray(); Bindings = Array.AsReadOnly(array); Diagnostics = Array.AsReadOnly(diagnostics.ToArray()); } public static AvatarPackIndex Build(string pluginDirectory) { List<string> list = new List<string>(); List<AvatarBinding[]> list2 = new List<AvatarBinding[]>(); if (!Directory.Exists(pluginDirectory)) { return new AvatarPackIndex(Array.Empty<AvatarBinding>(), list); } foreach (string item in FindMarkers(Path.GetFullPath(pluginDirectory), list)) { try { list2.Add(ReadPack(item)); } catch (Exception ex) when (ex is InvalidDataException || ex is IOException || ex is UnauthorizedAccessException || ex is SerializationException || ex is ArgumentException) { list.Add("Ignored avatar pack " + item + ": " + ex.Message); } } HashSet<string> duplicateIds = new HashSet<string>(from x in list2.GroupBy<AvatarBinding[], string>((AvatarBinding[] x) => x[0].PackId, StringComparer.OrdinalIgnoreCase) where x.Count() > 1 select x.Key, StringComparer.OrdinalIgnoreCase); foreach (string item2 in duplicateIds.OrderBy<string, string>((string x) => x, StringComparer.OrdinalIgnoreCase)) { list.Add("Duplicate avatar pack ID " + item2 + "; all copies ignored."); } AvatarBinding[] source = list2.Where((AvatarBinding[] x) => !duplicateIds.Contains(x[0].PackId)).SelectMany((AvatarBinding[] x) => x).ToArray(); List<AvatarBinding> list3 = new List<AvatarBinding>(); foreach (IGrouping<string, AvatarBinding> item3 in source.GroupBy<AvatarBinding, string>((AvatarBinding x) => x.Character, StringComparer.OrdinalIgnoreCase).OrderBy<IGrouping<string, AvatarBinding>, string>((IGrouping<string, AvatarBinding> x) => x.Key, StringComparer.OrdinalIgnoreCase)) { if (item3.Count() == 1) { list3.Add(item3.Single()); continue; } list.Add("Conflicting packaged character " + item3.Key + " in " + string.Join(", ", item3.Select((AvatarBinding x) => x.PackId).OrderBy<string, string>((string x) => x, StringComparer.OrdinalIgnoreCase)) + "; all package mappings for this character ignored. Manual lookup remains available."); } return new AvatarPackIndex(list3, list); } public bool TryResolve(string fileName, out string path) { path = null; if (!string.IsNullOrEmpty(fileName)) { return _files.TryGetValue(fileName, out path); } return false; } public bool IsProtectedPath(string path) { if (string.IsNullOrEmpty(path)) { return false; } try { string fullPath = Path.GetFullPath(path); return _protectedDirectories.Any((string root) => fullPath.StartsWith(root, StringComparison.OrdinalIgnoreCase)); } catch (Exception ex) when (ex is ArgumentException || ex is IOException || ex is NotSupportedException) { return false; } } private static IEnumerable<string> FindMarkers(string directory, List<string> diagnostics) { string[] source; string[] array; try { if (DisabledName(Path.GetFileName(directory)) || IsReparsePoint(directory)) { yield break; } source = Directory.GetFiles(directory).OrderBy<string, string>((string x) => x, StringComparer.OrdinalIgnoreCase).ThenBy<string, string>((string x) => x, StringComparer.Ordinal) .ToArray(); array = Directory.GetDirectories(directory).OrderBy<string, string>((string x) => x, StringComparer.OrdinalIgnoreCase).ThenBy<string, string>((string x) => x, StringComparer.Ordinal) .ToArray(); } catch (Exception ex) when (ex is IOException || ex is UnauthorizedAccessException) { diagnostics.Add("Cannot inspect avatar package directory " + directory + ": " + ex.Message); yield break; } string[] array2 = source.Where((string x) => string.Equals(Path.GetFileName(x), "avatar-pack.json", StringComparison.OrdinalIgnoreCase)).ToArray(); if (array2.Length != 0) { bool flag = false; try { flag = array2.Length == 1 && !IsReparsePoint(array2[0]); } catch (Exception ex2) when (ex2 is IOException || ex2 is UnauthorizedAccessException) { diagnostics.Add("Cannot inspect avatar-pack.json in " + directory + ": " + ex2.Message); } if (flag) { yield return array2[0]; } else { diagnostics.Add("Ambiguous, inaccessible or linked avatar-pack.json in " + directory + "; directory ignored."); } yield break; } string[] array3 = array; foreach (string directory2 in array3) { foreach (string item in FindMarkers(directory2, diagnostics)) { yield return item; } } } private static AvatarBinding[] ReadPack(string marker) { if (new FileInfo(marker).Length > 65536) { throw new InvalidDataException("Manifest exceeds 64 KiB."); } AvatarPackManifest avatarPackManifest; using (FileStream stream = File.OpenRead(marker)) { avatarPackManifest = (AvatarPackManifest)new DataContractJsonSerializer(typeof(AvatarPackManifest)).ReadObject(stream); } if (avatarPackManifest == null || avatarPackManifest.SchemaVersion != 1) { throw new InvalidDataException("Only schemaVersion 1 is supported."); } if (string.IsNullOrEmpty(avatarPackManifest.PackId) || avatarPackManifest.PackId.Length > 128 || avatarPackManifest.PackId.Any((char c) => !char.IsLetterOrDigit(c) && c != '-' && c != '_' && c != '.')) { throw new InvalidDataException("packId must contain letters, digits, dots, hyphens or underscor