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 Soft Capes v1.0.0
BepInEx/plugins/SoftCapes/SoftCapes.dll
Decompiled 3 weeks agousing System; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.Reflection; using System.Runtime.CompilerServices; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using MagicaCloth2; using Microsoft.CodeAnalysis; using SoftCapesRelease; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: AssemblyVersion("0.0.0.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace SoftCapes { internal sealed class AshCollarPatch { private readonly Mesh mesh; private readonly int[] original; private readonly int[] filtered; internal AshCollarPatch(Mesh mesh, int[] original, int[] filtered) { this.mesh = mesh; this.original = original; this.filtered = filtered; } internal void Apply() { mesh.SetTriangles(filtered, 0, false); } internal void Restore() { if ((Object)(object)mesh != (Object)null && mesh.isReadable && mesh.subMeshCount == 1 && AshCollarTopology.Same(mesh.GetTriangles(0), filtered)) { mesh.SetTriangles(original, 0, false); } } } internal static class AshCollarTopology { internal static readonly int[] Expected = new int[1416] { 0, 1, 2, 0, 3, 1, 3, 4, 1, 5, 3, 0, 5, 0, 6, 5, 6, 7, 5, 7, 8, 9, 5, 8, 9, 3, 5, 9, 8, 10, 3, 11, 4, 9, 11, 3, 11, 12, 4, 11, 13, 12, 14, 9, 10, 14, 11, 9, 14, 13, 11, 14, 10, 15, 16, 14, 15, 16, 13, 14, 16, 15, 17, 16, 17, 18, 16, 18, 19, 19, 18, 20, 16, 21, 13, 21, 16, 19, 19, 20, 22, 22, 20, 23, 13, 21, 24, 13, 24, 25, 21, 19, 26, 24, 21, 26, 26, 19, 22, 24, 26, 27, 27, 26, 28, 26, 22, 28, 27, 28, 29, 22, 23, 30, 28, 22, 30, 30, 23, 31, 29, 28, 32, 28, 30, 32, 29, 32, 33, 30, 31, 34, 32, 30, 34, 34, 31, 35, 33, 32, 36, 32, 34, 36, 33, 36, 37, 34, 35, 38, 36, 34, 38, 38, 35, 39, 37, 36, 40, 36, 38, 40, 37, 40, 41, 38, 39, 42, 40, 38, 42, 42, 39, 43, 42, 43, 44, 41, 40, 45, 40, 42, 45, 45, 42, 44, 41, 45, 46, 45, 44, 47, 46, 45, 47, 46, 47, 48, 49, 50, 51, 49, 52, 50, 53, 52, 49, 52, 54, 50, 53, 55, 52, 52, 56, 54, 55, 56, 52, 55, 57, 56, 58, 59, 60, 58, 61, 59, 62, 61, 58, 61, 63, 59, 62, 64, 61, 61, 65, 63, 64, 65, 61, 64, 66, 65, 67, 68, 69, 67, 70, 68, 71, 70, 67, 70, 72, 68, 71, 73, 70, 70, 74, 72, 73, 74, 70, 73, 75, 74, 76, 77, 78, 76, 79, 77, 80, 79, 76, 79, 81, 77, 80, 82, 79, 79, 83, 81, 82, 83, 79, 82, 84, 83, 85, 86, 87, 85, 88, 86, 85, 89, 88, 85, 90, 89, 90, 91, 89, 90, 92, 91, 92, 93, 91, 92, 94, 93, 94, 95, 93, 94, 96, 95, 96, 97, 95, 96, 98, 97, 99, 100, 101, 99, 102, 100, 102, 103, 100, 102, 104, 103, 102, 105, 104, 99, 106, 102, 102, 106, 105, 99, 107, 106, 106, 108, 105, 107, 109, 106, 106, 109, 108, 107, 110, 109, 109, 111, 108, 110, 112, 109, 109, 112, 111, 110, 113, 112, 112, 114, 111, 113, 115, 112, 112, 115, 114, 115, 116, 114, 113, 117, 115, 115, 118, 116, 117, 118, 115, 117, 119, 118, 120, 121, 122, 120, 123, 121, 123, 124, 121, 123, 125, 124, 125, 126, 124, 125, 127, 126, 127, 128, 126, 127, 129, 128, 129, 130, 128, 129, 131, 130, 131, 132, 130, 131, 133, 132, 134, 135, 136, 134, 136, 137, 137, 136, 138, 139, 134, 137, 139, 140, 134, 139, 141, 140, 139, 142, 141, 143, 142, 139, 143, 139, 137, 143, 144, 142, 137, 138, 145, 143, 137, 145, 145, 138, 146, 145, 146, 147, 148, 144, 143, 148, 143, 145, 148, 145, 147, 148, 149, 144, 150, 149, 148, 150, 148, 147, 150, 151, 149, 150, 152, 151, 150, 153, 152, 153, 154, 152, 150, 147, 155, 155, 153, 150, 153, 156, 154, 156, 157, 154, 147, 158, 155, 147, 159, 158, 155, 160, 153, 158, 160, 155, 160, 156, 153, 158, 161, 160, 161, 162, 160, 160, 162, 156, 161, 163, 162, 156, 164, 157, 162, 164, 156, 164, 165, 157, 163, 166, 162, 162, 166, 164, 163, 167, 166, 164, 168, 165, 166, 168, 164, 168, 169, 165, 167, 170, 166, 166, 170, 168, 167, 171, 170, 168, 172, 169, 170, 172, 168, 172, 173, 169, 171, 174, 170, 170, 174, 172, 171, 175, 174, 172, 176, 173, 174, 176, 172, 176, 177, 173, 176, 178, 177, 175, 179, 174, 174, 179, 176, 179, 178, 176, 175, 180, 179, 179, 181, 178, 180, 181, 179, 180, 182, 181, 183, 184, 185, 183, 185, 186, 187, 183, 186, 186, 185, 188, 187, 186, 189, 186, 188, 190, 189, 186, 190, 189, 190, 191, 192, 193, 194, 192, 194, 195, 196, 192, 195, 195, 194, 197, 196, 195, 198, 195, 197, 199, 198, 195, 199, 198, 199, 200, 201, 202, 203, 201, 203, 204, 205, 201, 204, 204, 203, 206, 205, 204, 207, 204, 206, 208, 207, 204, 208, 207, 208, 209, 210, 211, 212, 210, 212, 213, 214, 210, 213, 213, 212, 215, 214, 213, 216, 213, 215, 217, 216, 213, 217, 216, 217, 218, 219, 220, 221, 219, 221, 222, 219, 222, 223, 219, 223, 224, 224, 223, 225, 224, 225, 226, 226, 225, 227, 226, 227, 228, 228, 227, 229, 228, 229, 230, 230, 229, 231, 230, 231, 232, 233, 234, 235, 233, 235, 236, 236, 235, 237, 236, 237, 238, 236, 238, 239, 233, 236, 240, 236, 239, 240, 233, 240, 241, 240, 239, 242, 241, 240, 243, 240, 242, 243, 241, 243, 244, 243, 242, 245, 244, 243, 246, 243, 245, 246, 244, 246, 247, 246, 245, 248, 247, 246, 249, 246, 248, 249, 249, 248, 250, 247, 249, 251, 249, 250, 252, 251, 249, 252, 251, 252, 253, 254, 255, 256, 254, 256, 257, 257, 256, 258, 257, 258, 259, 259, 258, 260, 259, 260, 261, 261, 260, 262, 261, 262, 263, 263, 262, 264, 263, 264, 265, 265, 264, 266, 265, 266, 267, 268, 269, 270, 268, 270, 271, 271, 272, 268, 271, 273, 272, 273, 274, 272, 273, 275, 274, 275, 276, 274, 275, 277, 276, 277, 278, 276, 277, 279, 278, 279, 280, 278, 279, 281, 280, 281, 282, 280, 281, 283, 282, 284, 282, 283, 284, 283, 285, 286, 287, 288, 286, 289, 287, 286, 290, 289, 286, 291, 290, 287, 289, 292, 287, 292, 293, 294, 293, 292, 294, 292, 295, 292, 296, 295, 289, 297, 292, 292, 297, 296, 289, 290, 297, 297, 298, 296, 297, 299, 298, 290, 299, 297, 299, 300, 298, 291, 301, 290, 290, 301, 299, 291, 302, 301, 299, 303, 300, 301, 303, 299, 303, 304, 300, 302, 305, 301, 301, 305, 303, 302, 306, 305, 303, 307, 304, 305, 307, 303, 307, 308, 304, 306, 309, 305, 305, 309, 307, 306, 310, 309, 307, 311, 308, 309, 311, 307, 311, 312, 308, 311, 313, 312, 310, 314, 309, 309, 314, 311, 314, 313, 311, 310, 315, 314, 314, 316, 313, 315, 316, 314, 315, 317, 316, 318, 319, 320, 318, 321, 319, 318, 322, 321, 318, 323, 322, 323, 324, 322, 323, 325, 324, 325, 326, 324, 325, 327, 326, 324, 326, 328, 329, 328, 326, 329, 330, 328, 324, 328, 331, 322, 324, 331, 331, 328, 330, 322, 331, 332, 321, 322, 332, 331, 330, 333, 332, 331, 333, 333, 330, 334, 333, 334, 335, 336, 333, 335, 332, 333, 336, 336, 335, 337, 321, 332, 338, 338, 332, 336, 339, 336, 337, 338, 336, 339, 339, 337, 340, 339, 340, 341, 339, 341, 342, 338, 339, 342, 321, 338, 343, 338, 342, 343, 321, 343, 319, 319, 343, 342, 319, 342, 344, 345, 346, 347, 345, 348, 346, 348, 345, 349, 348, 349, 350, 350, 349, 351, 350, 351, 352, 352, 351, 353, 352, 353, 354, 354, 353, 355, 354, 355, 356, 356, 355, 357, 356, 357, 358, 358, 357, 359, 358, 359, 360, 361, 360, 359, 361, 362, 360, 363, 364, 365, 363, 365, 366, 363, 366, 367, 363, 367, 368, 365, 369, 366, 365, 370, 369, 371, 369, 370, 371, 372, 369, 369, 372, 373, 366, 369, 374, 369, 373, 374, 366, 374, 367, 374, 373, 375, 374, 375, 376, 367, 374, 376, 376, 375, 377, 368, 367, 378, 367, 376, 378, 368, 378, 379, 376, 377, 380, 378, 376, 380, 380, 377, 381, 379, 378, 382, 378, 380, 382, 379, 382, 383, 380, 381, 384, 382, 380, 384, 384, 381, 385, 383, 382, 386, 382, 384, 386, 383, 386, 387, 384, 385, 388, 386, 384, 388, 388, 385, 389, 388, 389, 390, 387, 386, 391, 386, 388, 391, 391, 388, 390, 387, 391, 392, 391, 390, 393, 392, 391, 393, 392, 393, 394, 395, 396, 397, 395, 397, 398, 395, 398, 399, 395, 399, 400, 400, 399, 401, 400, 401, 402, 402, 401, 403, 402, 403, 404, 405, 406, 407, 405, 407, 408, 409, 408, 407, 409, 407, 410, 410, 407, 411, 410, 411, 412, 410, 412, 413, 409, 410, 414, 410, 413, 414, 409, 414, 415, 414, 413, 416, 415, 414, 417, 414, 416, 417, 417, 416, 418, 415, 417, 419, 417, 418, 420, 419, 417, 420, 419, 420, 421, 422, 421, 420, 422, 423, 421 }; internal static readonly HashSet<int> RemovedFaces = new HashSet<int> { 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255, 256, 257, 258, 259, 260, 261, 262, 263, 264, 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, 285, 286, 287, 288, 289, 290, 291 }; internal static int[] Filter(int vertexCount, int[] triangles) { if (vertexCount != 424 || !Same(triangles, Expected)) { return null; } List<int> list = new List<int>(); for (int i = 0; i < triangles.Length; i += 3) { if (!RemovedFaces.Contains(i / 3)) { list.Add(triangles[i]); list.Add(triangles[i + 1]); list.Add(triangles[i + 2]); } } return list.ToArray(); } internal static bool Same(int[] a, int[] b) { if (a == null || b == null || a.Length != b.Length) { return false; } for (int i = 0; i < a.Length; i++) { if (a[i] != b[i]) { return false; } } return true; } } [BepInPlugin("maving.softcapes", "Soft Capes", "1.0.0")] public sealed class Plugin : BaseUnityPlugin { private sealed class Profile { internal ConfigEntry<float> Bending; internal ConfigEntry<float> Restoration; internal ConfigEntry<float> Angle; internal float B; internal float R; internal float A; } private static readonly FieldInfo ShoulderHash = typeof(VisEquipment).GetField("m_currentShoulderItemHash", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); private static readonly FieldInfo ShoulderInstances = typeof(VisEquipment).GetField("m_shoulderItemInstances", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); private readonly Dictionary<MagicaCloth, TuningSnapshot> applied = new Dictionary<MagicaCloth, TuningSnapshot>(); private readonly Dictionary<string, Profile> profiles = new Dictionary<string, Profile>(); private float nextCheck; private ConfigEntry<KeyCode> toggleKey; private ConfigEntry<bool> hideAshCollar; private readonly Dictionary<MagicaCloth, AshCollarPatch> collars = new Dictionary<MagicaCloth, AshCollarPatch>(); private readonly HashSet<MagicaCloth> rejectedCollars = new HashSet<MagicaCloth>(); private bool soft = true; private bool ready; private string lastStatus; private Profile BindProfile(string section, float b, float r, float a) { return new Profile { Bending = ((BaseUnityPlugin)this).Config.Bind<float>(section, "BendingMultiplier", b, "Bending relative to original, 0–1. Toggle off/on reloads."), Restoration = ((BaseUnityPlugin)this).Config.Bind<float>(section, "RestorationMultiplier", r, "Shape restoration relative to original, 0–1."), Angle = ((BaseUnityPlugin)this).Config.Bind<float>(section, "AngleLimitMultiplier", a, "Allowed angle relative to original, 1–3.") }; } private void Awake() { //IL_0253: Unknown result type (might be due to invalid IL or missing references) //IL_0258: Unknown result type (might be due to invalid IL or missing references) toggleKey = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("General", "ToggleKey", (KeyCode)289, "Toggle key (for example F6, F7 or F8). None disables the hotkey. Save and use the current key off/on to reload, or restart the game."); soft = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "StartEnabled", true, "Enable this component at game startup. Hotkey toggles affect only the current session.").Value; hideAshCollar = ((BaseUnityPlugin)this).Config.Bind<bool>("Ash", "HideRaisedCollar", true, "Hide the raised Ashen collar. Toggle off/on reloads; OFF restores the collar. Does not change cloth vertices or physics."); profiles.Add("CapeLox", BindProfile("Lox", 0.15f, 0.15f, 2f)); profiles.Add("CapeFeather", BindProfile("Feather", 0.1f, 0.1f, 2.5f)); profiles.Add("CapeWolf", BindProfile("Wolf", 0.15f, 0.15f, 2f)); profiles.Add("CapeLinen", BindProfile("Linen", 0.05f, 0f, 3f)); profiles.Add("CapeTrollHide", BindProfile("Troll", 0f, 0f, 3f)); profiles.Add("CapeDeerHide", BindProfile("Deer", 0f, 0f, 3f)); profiles.Add("CapeAsh", BindProfile("Ash", 0.5f, 0.5f, 2.5f)); profiles.Add("CapeAsksvin", BindProfile("Asksvin", 0.05f, 0.05f, 2.5f)); profiles.Add("CapeDeepNorth", BindProfile("DeepNorth", 0.15f, 0.15f, 2f)); profiles.Add("CapeDeepNorthMage", BindProfile("DeepNorthMage", 0.15f, 0.15f, 2f)); try { if (ShoulderHash == null || ShoulderInstances == null) { throw new MissingFieldException("VisEquipment shoulder fields unavailable."); } ReadSettings(); ready = true; ((BaseUnityPlugin)this).Logger.LogInfo((object)("Soft Capes 1.0.0 ready: 10 supported local capes. Toggle key: " + ((object)toggleKey.Value/*cast due to .constrained prefix*/).ToString() + ". Compares soft/original modern physics. Remove Classic Capes and older test builds before using this release.")); } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogError((object)("Soft Capes inactive: " + ex)); } } private void ReadSettings() { ((BaseUnityPlugin)this).Config.Reload(); foreach (Profile value in profiles.Values) { TuningSnapshot.Validate(value.Bending.Value, value.Restoration.Value, value.Angle.Value); } foreach (Profile value2 in profiles.Values) { value2.B = value2.Bending.Value; value2.R = value2.Restoration.Value; value2.A = value2.Angle.Value; } } private void Update() { //IL_0043: 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) if (!ready) { return; } string text = ReleaseGuard.FindConflict((BaseUnityPlugin)(object)this); if (text != null) { RestoreAll(); ready = false; ((BaseUnityPlugin)this).Logger.LogError((object)("Disabled to avoid conflicting cape changes: remove " + text + " and restart the game.")); return; } try { bool flag = (int)toggleKey.Value != 0 && Input.GetKeyDown(toggleKey.Value); if (flag) { if (soft) { soft = false; RestoreAll(); } else { ReadSettings(); soft = true; } nextCheck = 0f; } if (!(Time.unscaledTime < nextCheck)) { nextCheck = Time.unscaledTime + 0.25f; Refresh(); if (flag && (Object)(object)Player.m_localPlayer != (Object)null) { ((Character)Player.m_localPlayer).Message((MessageType)2, lastStatus, 0, (Sprite)null, false); } } } catch (Exception ex) { soft = false; RestoreAll(); Status("Soft Capes: error; original physics restored. See log."); ((BaseUnityPlugin)this).Logger.LogError((object)("Soft Capes stopped; configured toggle key retries: " + ex)); if ((Object)(object)Player.m_localPlayer != (Object)null) { ((Character)Player.m_localPlayer).Message((MessageType)2, lastStatus, 0, (Sprite)null, false); } } } private void Refresh() { HashSet<MagicaCloth> hashSet = new HashSet<MagicaCloth>(); Profile value = null; string text = null; Player localPlayer = Player.m_localPlayer; if (soft && (Object)(object)localPlayer != (Object)null) { VisEquipment componentInChildren = ((Component)localPlayer).GetComponentInChildren<VisEquipment>(); if ((Object)(object)componentInChildren != (Object)null) { int num = (int)ShoulderHash.GetValue(componentInChildren); GameObject val = (((Object)(object)ObjectDB.instance != (Object)null) ? ObjectDB.instance.GetItemPrefab(num) : null); text = (((Object)(object)val != (Object)null) ? ((Object)val).name : null); if (text != null && profiles.TryGetValue(text, out value) && ShoulderInstances.GetValue(componentInChildren) is List<GameObject> list) { foreach (GameObject item in list) { if ((Object)(object)item == (Object)null || !item.activeInHierarchy) { continue; } MagicaCloth[] componentsInChildren = item.GetComponentsInChildren<MagicaCloth>(); foreach (MagicaCloth val2 in componentsInChildren) { if (((Behaviour)val2).isActiveAndEnabled && val2.IsValid()) { hashSet.Add(val2); } } } } } } foreach (MagicaCloth item2 in new List<MagicaCloth>(applied.Keys)) { if (!hashSet.Contains(item2)) { RestoreOne(item2); } } foreach (MagicaCloth item3 in hashSet) { if (text == "CapeAsh" && hideAshCollar.Value) { TryHideCollar(item3); } if (!applied.ContainsKey(item3)) { ClothSerializeData serializeData = item3.SerializeData; TuningSnapshot tuningSnapshot = new TuningSnapshot(serializeData); applied.Add(item3, tuningSnapshot); tuningSnapshot.Apply(serializeData, value.B, value.R, value.A); item3.SetParameterChange(); ((BaseUnityPlugin)this).Logger.LogInfo((object)(text + " tuned: bending " + F(tuningSnapshot.Bending) + " -> " + F(serializeData.triangleBendingConstraint.stiffness) + "; restoration " + F(tuningSnapshot.Restoration) + " -> " + F(serializeData.angleRestorationConstraint.stiffness.value) + "; angle " + F(tuningSnapshot.Angle) + " -> " + F(serializeData.angleLimitConstraint.limitAngle.value))); } } Status((!soft) ? "Soft Capes OFF (original modern physics)" : ((applied.Count > 0) ? ("Soft Capes ON (" + text + ")") : ((value != null) ? "Soft Capes waiting: wait for cloth loading or re-equip the cape." : "Soft Capes ON: equip a supported cape."))); } private void TryHideCollar(MagicaCloth cloth) { if (collars.ContainsKey(cloth) || rejectedCollars.Contains(cloth)) { return; } SkinnedMeshRenderer[] componentsInChildren = ((Component)cloth).GetComponentsInChildren<SkinnedMeshRenderer>(true); foreach (SkinnedMeshRenderer val in componentsInChildren) { Mesh customMesh = cloth.GetCustomMesh((Renderer)(object)val); if ((Object)(object)customMesh == (Object)null || (Object)(object)customMesh != (Object)(object)val.sharedMesh || (Object)(object)customMesh == (Object)(object)cloth.GetOriginalMesh((Renderer)(object)val)) { continue; } if (!customMesh.isReadable || customMesh.subMeshCount != 1) { RejectCollar(cloth); break; } int[] triangles = customMesh.GetTriangles(0); int[] array = AshCollarTopology.Filter(customMesh.vertexCount, triangles); if (array == null) { RejectCollar(cloth); break; } AshCollarPatch ashCollarPatch = new AshCollarPatch(customMesh, triangles, array); collars.Add(cloth, ashCollarPatch); ashCollarPatch.Apply(); ((BaseUnityPlugin)this).Logger.LogInfo((object)("CapeAsh raised collar hidden: " + (triangles.Length - array.Length) / 3 + " faces; cloth vertices unchanged.")); break; } } private void RejectCollar(MagicaCloth cloth) { rejectedCollars.Add(cloth); ((BaseUnityPlugin)this).Logger.LogWarning((object)"CapeAsh collar unchanged: mesh layout does not match the verified model."); } private static string F(float value) { return value.ToString("0.###", CultureInfo.InvariantCulture); } private void Status(string status) { if (!(lastStatus == status)) { lastStatus = status; ((BaseUnityPlugin)this).Logger.LogInfo((object)status); } } private void RestoreOne(MagicaCloth cloth) { try { if (collars.TryGetValue(cloth, out var value)) { value.Restore(); } if ((Object)(object)cloth != (Object)null) { if (applied.TryGetValue(cloth, out var value2)) { value2.Restore(cloth.SerializeData); } cloth.SetParameterChange(); } } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogError((object)("Could not restore cloth; re-equip the cape: " + ex)); } finally { applied.Remove(cloth); collars.Remove(cloth); rejectedCollars.Remove(cloth); } } private void RestoreAll() { HashSet<MagicaCloth> hashSet = new HashSet<MagicaCloth>(applied.Keys); hashSet.UnionWith(collars.Keys); foreach (MagicaCloth item in hashSet) { RestoreOne(item); } rejectedCollars.Clear(); } private void OnDisable() { RestoreAll(); } private void OnDestroy() { RestoreAll(); } } internal sealed class TuningSnapshot { internal readonly float Bending; internal readonly float Restoration; internal readonly float Angle; internal TuningSnapshot(ClothSerializeData data) { Bending = data.triangleBendingConstraint.stiffness; Restoration = data.angleRestorationConstraint.stiffness.value; Angle = data.angleLimitConstraint.limitAngle.value; } internal void Apply(ClothSerializeData data, float bending, float restoration, float angle) { Validate(bending, restoration, angle); data.triangleBendingConstraint.stiffness = Math.Min(1f, Bending * bending); data.angleRestorationConstraint.stiffness.value = Math.Min(1f, Restoration * restoration); data.angleLimitConstraint.limitAngle.value = Math.Min(180f, Angle * angle); } internal void Restore(ClothSerializeData data) { data.triangleBendingConstraint.stiffness = Bending; data.angleRestorationConstraint.stiffness.value = Restoration; data.angleLimitConstraint.limitAngle.value = Angle; } internal static void Validate(float bending, float restoration, float angle) { if (!InRange(bending, 0f, 1f) || !InRange(restoration, 0f, 1f) || !InRange(angle, 1f, 3f)) { throw new ArgumentException("BendingMultiplier and RestorationMultiplier must be 0–1; AngleLimitMultiplier must be 1–3. Use finite decimal values."); } } private static bool InRange(float value, float min, float max) { if (!float.IsNaN(value) && !float.IsInfinity(value) && value >= min) { return value <= max; } return false; } } } namespace SoftCapesRelease { internal static class ReleaseGuard { internal static string FindConflict(BaseUnityPlugin self) { foreach (PluginInfo value in Chainloader.PluginInfos.Values) { if (!((Object)(object)value.Instance == (Object)null) && !((Object)(object)value.Instance == (Object)(object)self) && ((Behaviour)value.Instance).enabled) { string name = value.Metadata.Name; if (name == "Classic Capes") { return name; } if (name == "Soft Capes" && value.Metadata.GUID != "maving.softcapes") { return "an older Soft Capes build"; } if (name == "Cape Collisions" && value.Metadata.GUID != "maving.capecollisions") { return "an older Cape Collisions build"; } } } return null; } } }
BepInEx/plugins/SoftCapes/CapeCollisions.dll
Decompiled 3 weeks agousing System; using System.Collections.Generic; using System.Diagnostics; using System.Reflection; using System.Runtime.CompilerServices; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using MagicaCloth2; using Microsoft.CodeAnalysis; using SoftCapesRelease; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: AssemblyVersion("0.0.0.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace CapeCollisions { internal static class ArmSettings { internal static void Validate(float ratio, float leftPadding, float rightPadding) { if (!Between(ratio, 0.1f, 0.5f) || !Between(leftPadding, 0f, 0.12f) || !Between(rightPadding, 0f, 0.12f)) { throw new ArgumentException("RadiusRatio must be 0.1–0.5 and LeftPadding/RightPadding 0–0.12 meters; finite numbers only."); } } private static bool Between(float x, float min, float max) { if (!float.IsNaN(x) && !float.IsInfinity(x) && x >= min) { return x <= max; } return false; } internal static float Radius(float length, float ratio, float padding) { return length * ratio + padding; } internal static float Height(float length, float radius) { return length + 2f * radius; } } [BepInPlugin("maving.capecollisions", "Cape Collisions", "1.0.0")] public sealed class Plugin : BaseUnityPlugin { private sealed class Profile { internal ConfigEntry<float> Ratio; internal ConfigEntry<float> Left; internal ConfigEntry<float> Right; internal float R; internal float L; internal float P; } private sealed class Arm { internal Transform Start; internal Transform End; internal MagicaCapsuleCollider Collider; internal float Ratio; internal float Padding; internal float LastLength = -1f; } private sealed class Attachment { internal ListPatch<ColliderComponent> Patch; internal readonly List<Arm> Arms = new List<Arm>(); internal bool Registered; } private static readonly FieldInfo ShoulderHash = typeof(VisEquipment).GetField("m_currentShoulderItemHash", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); private static readonly FieldInfo ShoulderInstances = typeof(VisEquipment).GetField("m_shoulderItemInstances", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); private readonly Dictionary<MagicaCloth, Attachment> attachments = new Dictionary<MagicaCloth, Attachment>(); private readonly Dictionary<string, Profile> profiles = new Dictionary<string, Profile>(); private float nextCheck; private ConfigEntry<KeyCode> toggleKey; private bool active = true; private bool ready; private string status; private Profile BindProfile(string section) { return new Profile { Ratio = ((BaseUnityPlugin)this).Config.Bind<float>(section, "RadiusRatio", 0.24f, "Base upper-arm radius ratio, 0.1–0.5."), Left = ((BaseUnityPlugin)this).Config.Bind<float>(section, "LeftPadding", 0.02f, "Extra left radius in meters, 0–0.12. Toggle off/on reloads."), Right = ((BaseUnityPlugin)this).Config.Bind<float>(section, "RightPadding", 0.02f, "Extra right radius in meters, 0–0.12. Toggle off/on reloads.") }; } private void Awake() { //IL_019c: Unknown result type (might be due to invalid IL or missing references) //IL_01a1: Unknown result type (might be due to invalid IL or missing references) toggleKey = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("General", "ToggleKey", (KeyCode)288, "Toggle key (for example F6, F7 or F8). None disables the hotkey. Save and use the current key off/on to reload, or restart the game."); active = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "StartEnabled", true, "Enable this component at game startup. Hotkey toggles affect only the current session.").Value; profiles.Add("CapeLox", BindProfile("UpperArms")); profiles.Add("CapeFeather", BindProfile("FeatherUpperArms")); profiles.Add("CapeWolf", BindProfile("WolfUpperArms")); profiles.Add("CapeLinen", BindProfile("LinenUpperArms")); profiles.Add("CapeTrollHide", BindProfile("TrollUpperArms")); profiles.Add("CapeDeerHide", BindProfile("DeerUpperArms")); profiles.Add("CapeAsh", BindProfile("AshUpperArms")); profiles.Add("CapeAsksvin", BindProfile("AsksvinUpperArms")); profiles.Add("CapeDeepNorth", BindProfile("DeepNorthUpperArms")); profiles.Add("CapeDeepNorthMage", BindProfile("DeepNorthMageUpperArms")); try { if (ShoulderHash == null || ShoulderInstances == null) { throw new MissingFieldException("VisEquipment shoulder fields unavailable."); } Reload(); ready = true; ((BaseUnityPlugin)this).Logger.LogInfo((object)("Cape Collisions 1.0.0 ready. Toggle key: " + ((object)toggleKey.Value/*cast due to .constrained prefix*/).ToString() + ". Toggles extra upper-arm collisions for 10 supported local capes. Soft Capes settings are independent.")); } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogError((object)("Cape Collisions inactive: " + ex)); } } private void Reload() { ((BaseUnityPlugin)this).Config.Reload(); foreach (Profile value in profiles.Values) { ArmSettings.Validate(value.Ratio.Value, value.Left.Value, value.Right.Value); } foreach (Profile value2 in profiles.Values) { value2.R = value2.Ratio.Value; value2.L = value2.Left.Value; value2.P = value2.Right.Value; } } private void Update() { //IL_0043: 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) if (!ready) { return; } string text = ReleaseGuard.FindConflict((BaseUnityPlugin)(object)this); if (text != null) { RestoreAll(); ready = false; ((BaseUnityPlugin)this).Logger.LogError((object)("Disabled to avoid conflicting cape changes: remove " + text + " and restart the game.")); return; } try { bool flag = (int)toggleKey.Value != 0 && Input.GetKeyDown(toggleKey.Value); if (flag) { if (active) { active = false; RestoreAll(); } else { Reload(); active = true; } nextCheck = 0f; } if (!(Time.unscaledTime < nextCheck)) { nextCheck = Time.unscaledTime + 0.25f; Refresh(); if (flag) { ShowStatus(); } } } catch (Exception ex) { Fail(ex); } } private void Refresh() { HashSet<MagicaCloth> hashSet = new HashSet<MagicaCloth>(); Profile value = null; string text = null; Player localPlayer = Player.m_localPlayer; VisEquipment val = (((Object)(object)localPlayer != (Object)null) ? ((Component)localPlayer).GetComponentInChildren<VisEquipment>() : null); if (active && (Object)(object)val != (Object)null && (Object)(object)ObjectDB.instance != (Object)null) { GameObject itemPrefab = ObjectDB.instance.GetItemPrefab((int)ShoulderHash.GetValue(val)); text = (((Object)(object)itemPrefab != (Object)null) ? ((Object)itemPrefab).name : null); if (text != null && profiles.TryGetValue(text, out value) && ShoulderInstances.GetValue(val) is List<GameObject> list) { foreach (GameObject item in list) { if ((Object)(object)item == (Object)null || !item.activeInHierarchy) { continue; } MagicaCloth[] componentsInChildren = item.GetComponentsInChildren<MagicaCloth>(); foreach (MagicaCloth val2 in componentsInChildren) { if (((Behaviour)val2).isActiveAndEnabled && val2.IsValid()) { hashSet.Add(val2); } } } } } foreach (MagicaCloth item2 in new List<MagicaCloth>(attachments.Keys)) { if (!hashSet.Contains(item2)) { RestoreOne(item2); } } foreach (MagicaCloth item3 in hashSet) { if (!attachments.TryGetValue(item3, out var value2)) { Add(item3, val, value, text); } else if (!value2.Registered && value2.Arms.TrueForAll((Arm a) => (Object)(object)a.Collider != (Object)null && ((ColliderComponent)a.Collider).UseTeamCount > 0)) { value2.Registered = true; ((BaseUnityPlugin)this).Logger.LogInfo((object)("Verified: " + value2.Arms.Count + " added arm colliders registered in MagicaCloth simulation.")); } } MagicaManager.OnPreSimulation = (Action)Delegate.Remove(MagicaManager.OnPreSimulation, new Action(FollowArms)); if (attachments.Count > 0) { MagicaManager.OnPreSimulation = (Action)Delegate.Combine(MagicaManager.OnPreSimulation, new Action(FollowArms)); } SetStatus((!active) ? "Cape Collisions OFF" : ((attachments.Count > 0) ? ("Cape Collisions ON (" + text + ")") : "Cape Collisions ON: waiting for a supported cape.")); } private void Add(MagicaCloth cloth, VisEquipment vis, Profile profile, string capeName) { SerializeData colliderCollisionConstraint = cloth.SerializeData.colliderCollisionConstraint; Attachment attachment = new Attachment { Patch = new ListPatch<ColliderComponent>(colliderCollisionConstraint.colliderList) }; attachments.Add(cloth, attachment); Dictionary<string, Transform> dictionary = new Dictionary<string, Transform>(StringComparer.Ordinal); SkinnedMeshRenderer[] componentsInChildren = ((Component)cloth).GetComponentsInChildren<SkinnedMeshRenderer>(true); for (int i = 0; i < componentsInChildren.Length; i++) { Transform[] bones = componentsInChildren[i].bones; foreach (Transform val in bones) { if ((Object)(object)val != (Object)null) { dictionary[((Object)val).name] = val; } } } if (!dictionary.ContainsKey("LeftArm") || !dictionary.ContainsKey("RightArm")) { Transform[] bones = ((Component)vis).GetComponentsInChildren<Transform>(true); foreach (Transform val2 in bones) { if (!dictionary.ContainsKey(((Object)val2).name)) { dictionary.Add(((Object)val2).name, val2); } } } ((BaseUnityPlugin)this).Logger.LogInfo((object)(capeName + " original collider list: " + attachment.Patch.Installed.Count + " entries.")); AddArm(attachment, dictionary, "LeftArm", "LeftForeArm", profile.R, profile.L); AddArm(attachment, dictionary, "RightArm", "RightForeArm", profile.R, profile.P); colliderCollisionConstraint.colliderList = attachment.Patch.Installed; cloth.SetParameterChange(); ((BaseUnityPlugin)this).Logger.LogInfo((object)(capeName + " arm collision patch: " + attachment.Arms.Count + " added; " + colliderCollisionConstraint.colliderList.Count + " total. Existing collider objects unchanged.")); } private void AddArm(Attachment attachment, Dictionary<string, Transform> bones, string startName, string endName, float ratio, float padding) { //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_00d7: Unknown result type (might be due to invalid IL or missing references) //IL_00dd: Expected O, but got Unknown //IL_011b: Unknown result type (might be due to invalid IL or missing references) //IL_0133: Unknown result type (might be due to invalid IL or missing references) //IL_014a: Unknown result type (might be due to invalid IL or missing references) //IL_014f: Unknown result type (might be due to invalid IL or missing references) if (!bones.TryGetValue(startName, out var value) || !bones.TryGetValue(endName, out var value2)) { throw new InvalidOperationException("Missing upper-arm bones: " + startName + "/" + endName); } foreach (ColliderComponent item in attachment.Patch.Installed) { if (!((Object)(object)item == (Object)null)) { bool num = OnUpperArm(((Component)item).transform, value, value2); bool flag = (int)item.symmetryMode != 0 && (OnUpperArm(item.symmetryTarget, value, value2) || OnUpperArm(item.ActiveSymmetryTarget, value, value2)); if (num || flag) { ((BaseUnityPlugin)this).Logger.LogInfo((object)(startName + " already has a cloth collider; skipping duplicate.")); return; } } } GameObject val = new GameObject("CapeCollisions_" + startName); val.SetActive(false); Arm arm = new Arm { Start = value, End = value2, Ratio = ratio, Padding = padding }; try { arm.Collider = val.AddComponent<MagicaCapsuleCollider>(); arm.Collider.direction = (Direction)1; arm.Collider.alignedOnCenter = true; ((ColliderComponent)arm.Collider).symmetryMode = (ColliderSymmetryMode)0; arm.Collider.reverseDirection = false; ((ColliderComponent)arm.Collider).center = Vector3.zero; MoveArm(arm); attachment.Arms.Add(arm); attachment.Patch.Add((ColliderComponent)(object)arm.Collider); val.SetActive(true); } catch { Object.Destroy((Object)(object)val); throw; } } private static bool OnUpperArm(Transform t, Transform start, Transform end) { if ((Object)(object)t != (Object)null && ((Object)(object)t == (Object)(object)start || t.IsChildOf(start)) && (Object)(object)t != (Object)(object)end) { return !t.IsChildOf(end); } return false; } private void FollowArms() { try { foreach (Attachment value in attachments.Values) { foreach (Arm arm in value.Arms) { MoveArm(arm); } } } catch (Exception ex) { Fail(ex); } } private void MoveArm(Arm arm) { //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_0046: 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_0075: Unknown result type (might be due to invalid IL or missing references) //IL_007a: 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_008f: Unknown result type (might be due to invalid IL or missing references) //IL_0094: Unknown result type (might be due to invalid IL or missing references) //IL_0096: Unknown result type (might be due to invalid IL or missing references) //IL_009b: Unknown result type (might be due to invalid IL or missing references) //IL_00a5: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)arm.Collider == (Object)null || (Object)(object)arm.Start == (Object)null || (Object)(object)arm.End == (Object)null) { return; } Vector3 val = arm.End.position - arm.Start.position; float magnitude = ((Vector3)(ref val)).magnitude; if (!(magnitude < 0.001f)) { Transform transform = ((Component)arm.Collider).transform; transform.position = (arm.Start.position + arm.End.position) * 0.5f; transform.rotation = Quaternion.FromToRotation(Vector3.up, val / magnitude); transform.localScale = Vector3.one; if (Math.Abs(magnitude - arm.LastLength) > 0.0001f) { float num = ArmSettings.Radius(magnitude, arm.Ratio, arm.Padding); arm.Collider.SetSize(num, num, ArmSettings.Height(magnitude, num)); ((ColliderComponent)arm.Collider).UpdateParameters(); arm.LastLength = magnitude; } } } private void RestoreOne(MagicaCloth cloth) { Attachment attachment = attachments[cloth]; try { if ((Object)(object)cloth != (Object)null) { SerializeData colliderCollisionConstraint = cloth.SerializeData.colliderCollisionConstraint; colliderCollisionConstraint.colliderList = attachment.Patch.RemoveFrom(colliderCollisionConstraint.colliderList); cloth.SetParameterChange(); } } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogError((object)("Collider list cleanup failed; re-equip cape: " + ex)); } finally { foreach (Arm arm in attachment.Arms) { if ((Object)(object)arm.Collider != (Object)null) { ((Component)arm.Collider).gameObject.SetActive(false); Object.Destroy((Object)(object)((Component)arm.Collider).gameObject); } } attachments.Remove(cloth); } } private void RestoreAll() { MagicaManager.OnPreSimulation = (Action)Delegate.Remove(MagicaManager.OnPreSimulation, new Action(FollowArms)); foreach (MagicaCloth item in new List<MagicaCloth>(attachments.Keys)) { RestoreOne(item); } } private void SetStatus(string value) { if (!(status == value)) { status = value; ((BaseUnityPlugin)this).Logger.LogInfo((object)value); } } private void ShowStatus() { if ((Object)(object)Player.m_localPlayer != (Object)null) { ((Character)Player.m_localPlayer).Message((MessageType)2, status, 0, (Sprite)null, false); } } private void Fail(Exception ex) { active = false; RestoreAll(); SetStatus("Cape Collisions OFF: error (see log)"); ((BaseUnityPlugin)this).Logger.LogError((object)("Cape Collisions stopped; configured toggle key retries: " + ex)); ShowStatus(); } private void OnDisable() { RestoreAll(); } private void OnDestroy() { RestoreAll(); } } internal sealed class ListPatch<T> where T : class { internal readonly List<T> Original; internal readonly List<T> Installed; private readonly List<T> additions = new List<T>(); internal ListPatch(List<T> original) { Original = original; Installed = ((original == null) ? new List<T>() : new List<T>(original)); } internal void Add(T item) { additions.Add(item); Installed.Add(item); } internal List<T> RemoveFrom(List<T> current) { if (current == null) { return null; } List<T> list = new List<T>(); foreach (T item in current) { if (!additions.Exists((T own) => own == item)) { list.Add(item); } } if (Original != null && list.Count == Original.Count) { bool flag = true; for (int num = 0; num < list.Count; num++) { flag &= list[num] == Original[num]; } if (flag) { return Original; } } return list; } } } namespace SoftCapesRelease { internal static class ReleaseGuard { internal static string FindConflict(BaseUnityPlugin self) { foreach (PluginInfo value in Chainloader.PluginInfos.Values) { if (!((Object)(object)value.Instance == (Object)null) && !((Object)(object)value.Instance == (Object)(object)self) && ((Behaviour)value.Instance).enabled) { string name = value.Metadata.Name; if (name == "Classic Capes") { return name; } if (name == "Soft Capes" && value.Metadata.GUID != "maving.softcapes") { return "an older Soft Capes build"; } if (name == "Cape Collisions" && value.Metadata.GUID != "maving.capecollisions") { return "an older Cape Collisions build"; } } } return null; } } }