Some mods may be broken due to the recent Alloyed Collective update.
Decompiled source of BodyBlend v0.3.5
BodyBlend.dll
Decompiled a year agousing System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using BepInEx.Logging; using BodyBlend.Utils; using IL.RoR2; using IL.RoR2.SurvivorMannequins; using Mono.Cecil.Cil; using MonoMod.Cil; using On.RoR2; using RiskOfOptions; using RiskOfOptions.OptionConfigs; using RiskOfOptions.Options; using RoR2; using SuspiciousTentacle; using UnityEngine; using UnityEngine.Events; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("BodyBlend")] [assembly: AssemblyConfiguration("Debug")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0")] [assembly: AssemblyProduct("BodyBlend")] [assembly: AssemblyTitle("BodyBlend")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.0.0")] [module: UnverifiableCode] namespace BodyBlend { [DisallowMultipleComponent] public class BodyBlendController : MonoBehaviour { private BodyBlendUtils.DictionaryList<string, BodyBlendControl> bodyBlendControls = new BodyBlendUtils.DictionaryList<string, BodyBlendControl>(); private string SkinDefKey = ""; public void Awake() { } public void LateUpdate() { foreach (KeyValuePair<string, List<BodyBlendControl>> bodyBlendControl in bodyBlendControls) { float maxBlend = FetchMaxBlendConfig(bodyBlendControl.Key); float minBlend = FetchMinBlendConfig(bodyBlendControl.Key); foreach (BodyBlendControl item in bodyBlendControl.Value) { item.SetMinBlend(minBlend); item.SetMaxBlend(maxBlend); item.Update(Time.deltaTime); } } } private float FetchMinBlendConfig(string name) { float? num = BodyBlendOptions.MinBlendConfigs.GetValue(SkinDefKey)?.GetValue(name)?.Value; if (!num.HasValue) { return 0f; } return num.Value / 100f; } private float FetchMaxBlendConfig(string name) { float? num = BodyBlendOptions.MaxBlendConfigs.GetValue(SkinDefKey)?.GetValue(name)?.Value; if (!num.HasValue) { return 1f; } return num.Value / 100f; } public void SetBlendTargetWeight(string name, float value, string source = "Default", bool setInstant = false) { if (!HasBlendControl(name)) { return; } foreach (BodyBlendControl item in bodyBlendControls[name]) { item.SetTargetWeight(value, source); if (setInstant) { item.Update(0f, forceNoLerp: true); } } } public void SetBlendTargetWeightPercent(string name, float value, string source = "Default", bool setInstant = false) { if (!HasBlendControl(name)) { return; } foreach (BodyBlendControl item in bodyBlendControls[name]) { item.SetTargetWeightPercent(value, source); if (setInstant) { item.Update(0f, forceNoLerp: true); } } } public void RemoveBlendTargetWeight(string name, string source = "Default") { if (!HasBlendControl(name)) { return; } foreach (BodyBlendControl item in bodyBlendControls[name]) { item.RemoveTargetWeight(source); } } public bool HasBlendControl(string name) { return bodyBlendControls.ContainsKey(name); } internal void AddBlendControl(string name, BodyBlendControl control) { if (!bodyBlendControls.ContainsKey(name)) { bodyBlendControls[name] = new List<BodyBlendControl>(); } bodyBlendControls[name].Add(control); } internal void RemoveBlendControl(string name) { bodyBlendControls.Remove(name); } internal void SetSkinDefKey(string key) { SkinDefKey = key; } } internal class BodyBlendControl { public Dictionary<int, AnimationCurve> blendShapeControls = new Dictionary<int, AnimationCurve>(); public List<DynamicBoneControl> boneControls = new List<DynamicBoneControl>(); private SkinnedMeshRenderer targetRenderer; private float minBlend = 0f; private float maxBlend = 1f; private float elapsedTime = 0f; private float currentWeight = 0f; private Dictionary<string, float> targetWeights = new Dictionary<string, float> { { "Default", 0f } }; private Dictionary<string, float> targetWeightPercents = new Dictionary<string, float> { { "Default", 0f } }; public BodyBlendControl(SkinnedMeshRenderer targetRenderer = null) { this.targetRenderer = targetRenderer; } public void SetTargetRenderer(SkinnedMeshRenderer renderer) { targetRenderer = renderer; } public void AddDynBoneControl(List<DynamicBone> bones, AnimationCurve inertCurve = null, AnimationCurve elasticityCurve = null, AnimationCurve stiffnessCurve = null, AnimationCurve dampingCurve = null, DynBoneControlMode mode = DynBoneControlMode.FULL_CONTROL) { DynamicBoneControl dynamicBoneControl = new DynamicBoneControl(); dynamicBoneControl.SetBoneControls(bones, inertCurve, elasticityCurve, stiffnessCurve, dampingCurve, mode); boneControls.Add(dynamicBoneControl); } public void SetTargetWeight(float weight, string source) { targetWeights[source] = weight; } public void SetTargetWeightPercent(float weight, string source) { targetWeightPercents[source] = weight; } public void RemoveTargetWeight(string source) { targetWeights.Remove(source); } public void SetMaxBlend(float value) { maxBlend = value; } public void SetMinBlend(float value) { minBlend = value; } public void Update(float t, bool forceNoLerp = false) { elapsedTime += t; bool flag = forceNoLerp; if (BodyBlendOptions.BoneUpdateInterval.Value <= 0f) { flag = true; } else if (elapsedTime >= BodyBlendOptions.BoneUpdateInterval.Value) { elapsedTime -= BodyBlendOptions.BoneUpdateInterval.Value; elapsedTime = Mathf.Min(elapsedTime, BodyBlendOptions.BoneUpdateInterval.Value); flag = true; } if (BodyBlendOptions.UseLerp.Value && !forceNoLerp) { currentWeight = Mathf.Lerp(currentWeight, GetTargetWeight(), t * BodyBlendOptions.LerpSpeed.Value); } else { currentWeight = GetTargetWeight(); } float num = Mathf.Lerp(minBlend, maxBlend, currentWeight); if (flag && boneControls != null) { foreach (DynamicBoneControl boneControl in boneControls) { boneControl.UpdateBoneValues(num); } } if ((Object)(object)targetRenderer == (Object)null) { return; } foreach (KeyValuePair<int, AnimationCurve> blendShapeControl in blendShapeControls) { float num2 = blendShapeControl.Value.Evaluate(num); if (targetRenderer.sharedMesh.blendShapeCount > blendShapeControl.Key) { targetRenderer.SetBlendShapeWeight(blendShapeControl.Key, num2 * 100f); } } } private float GetTargetWeight() { float num = targetWeights.Values.Sum(); num = Mathf.Clamp01(num); float num2 = targetWeightPercents.Values.Max(); return Mathf.Lerp(num, 1f, num2); } } [Serializable] public class BBJsonConfig { public string skinName; public string skinDefName; public string skinDefToken; [NonSerialized] public string filePath; public List<BBJsonPartControl> parts; } [Serializable] public class BBJsonPartControl { public string name; public List<BBJsonCtrlTemplate> controls; } [Serializable] public class BBJsonCtrlTemplate { public int targetRendererIndex = -1; public List<BBShapeControl> blendControls; public List<BBBoneControl> boneControls; } [Serializable] public class BBBoneControl { public List<string> boneNames = new List<string>(); public List<BBAnimKey> inertCurve; public List<BBAnimKey> elasticityCurve; public List<BBAnimKey> stiffnessCurve; public List<BBAnimKey> dampingCurve; public string controlMode = "FULL_CONTROL"; } [Serializable] public class BBShapeControl { public int blendIdx = -1; public List<BBAnimKey> keyframes; } [Serializable] public class BBAnimKey { public List<float> val; } internal static class BBJsonExtensions { private static Dictionary<string, DynBoneControlMode> DynBoneDict = new Dictionary<string, DynBoneControlMode> { { "ZERO_TO_BASE", DynBoneControlMode.ZERO_TO_BASE }, { "BASE_TO_ONE", DynBoneControlMode.BASE_TO_ONE }, { "FULL_CONTROL", DynBoneControlMode.FULL_CONTROL } }; public static BodyBlendUtils.DictionaryList<string, BodyBlendUtils.BlendControlTemplate> ToTemplates(this BBJsonConfig self) { BodyBlendUtils.DictionaryList<string, BodyBlendUtils.BlendControlTemplate> dictionaryList = new BodyBlendUtils.DictionaryList<string, BodyBlendUtils.BlendControlTemplate>(); foreach (BBJsonPartControl part in self.parts) { List<BodyBlendUtils.BlendControlTemplate> list = new List<BodyBlendUtils.BlendControlTemplate>(); foreach (BBJsonCtrlTemplate control in part.controls) { BodyBlendUtils.BlendControlTemplate blendControlTemplate = new BodyBlendUtils.BlendControlTemplate(); blendControlTemplate.targetRendererIndex = control.targetRendererIndex; blendControlTemplate.blendShapeControls = control.blendControls.ToAnimDict(); blendControlTemplate.boneControls = (from bone in control.boneControls select bone.ToTemplate() into bone where bone != null select bone).ToList(); list.Add(blendControlTemplate); } dictionaryList.Add(part.name, list); } return dictionaryList; } private static Dictionary<int, AnimationCurve> ToAnimDict(this List<BBShapeControl> self) { if (self == null || self.Count() < 1) { return null; } Dictionary<int, AnimationCurve> dictionary = new Dictionary<int, AnimationCurve>(); foreach (BBShapeControl item in self) { dictionary.Add(item.blendIdx, item.keyframes.ToAnimationCurve()); } return dictionary; } private static BodyBlendUtils.BoneControlTemplate ToTemplate(this BBBoneControl self) { if (self == null) { return null; } BodyBlendUtils.BoneControlTemplate boneControlTemplate = new BodyBlendUtils.BoneControlTemplate(); boneControlTemplate.associatedDynBoneNames = self.boneNames; boneControlTemplate.dynBoneInertCurve = self.inertCurve.ToAnimationCurve(); boneControlTemplate.dynBoneElasticityCurve = self.elasticityCurve.ToAnimationCurve(); boneControlTemplate.dynBoneStiffnessCurve = self.stiffnessCurve.ToAnimationCurve(); boneControlTemplate.dynBoneDampingCurve = self.dampingCurve.ToAnimationCurve(); if (DynBoneDict.ContainsKey(self.controlMode)) { boneControlTemplate.dynBoneControlMode = DynBoneDict[self.controlMode]; } return boneControlTemplate; } private static AnimationCurve ToAnimationCurve(this List<BBAnimKey> self) { if (self == null || self.Count < 2) { return null; } Keyframe[] keyframes = self.Where((BBAnimKey elem) => elem.val.Count == 2 || elem.val.Count == 4 || elem.val.Count == 6).Select((Func<BBAnimKey, Keyframe>)delegate(BBAnimKey elem) { //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Unknown result type (might be due to invalid IL or missing references) //IL_008e: Unknown result type (might be due to invalid IL or missing references) //IL_0056: 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_0091: Unknown result type (might be due to invalid IL or missing references) List<float> val = elem.val; if (val.Count == 2) { return new Keyframe(val[0], val[1]); } return (val.Count == 4) ? new Keyframe(val[0], val[1], val[2], val[3]) : new Keyframe(val[0], val[1], val[2], val[3], val[4], val[5]); }).ToArray(); return BodyBlendUtils.MakeAnimationCurve(keyframes); } } internal static class BodyBlendOptions { internal static ConfigEntry<bool> UseLerp; internal static ConfigEntry<float> LerpSpeed; internal static ConfigEntry<float> BoneUpdateInterval; internal static BodyBlendUtils.NestedDictionary<string, string, ConfigEntry<float>> MinBlendConfigs = new BodyBlendUtils.NestedDictionary<string, string, ConfigEntry<float>>(); internal static BodyBlendUtils.NestedDictionary<string, string, ConfigEntry<float>> MaxBlendConfigs = new BodyBlendUtils.NestedDictionary<string, string, ConfigEntry<float>>(); internal static List<string> SusTentacleParts = new List<string>(); internal static bool CheckUseLerp() { return !UseLerp.Value; } internal static void InitializeOptions(ConfigFile config) { //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Expected O, but got Unknown //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Expected O, but got Unknown //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Expected O, but got Unknown //IL_0084: Unknown result type (might be due to invalid IL or missing references) //IL_008e: Expected O, but got Unknown //IL_008f: Unknown result type (might be due to invalid IL or missing references) //IL_0099: Expected O, but got Unknown //IL_00be: Unknown result type (might be due to invalid IL or missing references) //IL_00c3: Unknown result type (might be due to invalid IL or missing references) //IL_00ce: Unknown result type (might be due to invalid IL or missing references) //IL_00d9: Unknown result type (might be due to invalid IL or missing references) //IL_00e9: Expected O, but got Unknown //IL_00e4: Unknown result type (might be due to invalid IL or missing references) //IL_00ee: Expected O, but got Unknown //IL_010a: Unknown result type (might be due to invalid IL or missing references) //IL_0114: Expected O, but got Unknown //IL_010f: Unknown result type (might be due to invalid IL or missing references) //IL_0119: Expected O, but got Unknown //IL_01ac: 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_01bc: Unknown result type (might be due to invalid IL or missing references) //IL_01cc: Expected O, but got Unknown //IL_01c7: Unknown result type (might be due to invalid IL or missing references) //IL_01d1: Expected O, but got Unknown //IL_0211: 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_0221: Unknown result type (might be due to invalid IL or missing references) //IL_0231: Expected O, but got Unknown //IL_022c: Unknown result type (might be due to invalid IL or missing references) //IL_0236: Expected O, but got Unknown UseLerp = config.Bind<bool>("BodyBlend", "Use Lerp", true, "Enable smooth transition. If disabled, the model instantly changes to match the target size."); ModSettingsManager.AddOption((BaseOption)new CheckBoxOption(UseLerp, new CheckBoxConfig())); LerpSpeed = config.Bind<float>("BodyBlend", "Lerp Speed", 2f, "If lerp is enabled, this controls how fast the model changes to match the target size.\nDefault: 2.0"); ConfigEntry<float> lerpSpeed = LerpSpeed; StepSliderConfig val = new StepSliderConfig(); ((SliderConfig)val).min = 0.1f; ((SliderConfig)val).max = 10f; val.increment = 0.1f; ((BaseOptionConfig)val).checkIfDisabled = new IsDisabledDelegate(CheckUseLerp); ModSettingsManager.AddOption((BaseOption)new StepSliderOption(lerpSpeed, val)); BoneUpdateInterval = config.Bind<float>("BodyBlend", "Bone Update Interval", 0.15f, "Intervals between each update to dynamic bone.\nDefault: 0.15"); ModSettingsManager.AddOption((BaseOption)new StepSliderOption(BoneUpdateInterval, new StepSliderConfig { min = 0.01f, max = 1f, increment = 0.01f })); ModSettingsManager.AddOption((BaseOption)new GenericButtonOption("", "BodyBlend", "Click to reload all BodyBlend configuration files in the /plugins folder.\nCheck the console log to see if files have been reloaded.\nAny adjustment will only apply after entering a new stage.\nIf you have added a new part in the config file, you'll need to restart the game for BodyBlend to work properly.", "Reload Config Files", new UnityAction(BodyBlendPlugin.ReloadJson))); foreach (KeyValuePair<string, BodyBlendUtils.DictionaryList<string, BodyBlendUtils.BlendControlTemplate>> registeredSkinBlendControl in BodyBlendUtils.RegisteredSkinBlendControls) { string key = registeredSkinBlendControl.Key; string skinName = BodyBlendUtils.GetSkinName(key); Dictionary<string, ConfigEntry<float>> dictionary = new Dictionary<string, ConfigEntry<float>>(); Dictionary<string, ConfigEntry<float>> dictionary2 = new Dictionary<string, ConfigEntry<float>>(); foreach (KeyValuePair<string, List<BodyBlendUtils.BlendControlTemplate>> item in registeredSkinBlendControl.Value) { string key2 = item.Key; ConfigEntry<float> val2 = config.Bind<float>(skinName, "Min " + key2 + " Size", 0f, "Minimum size for the part \"" + key2 + "\".\nBodyBlend will interpolate the size from the minimum value to the maximum value."); ModSettingsManager.AddOption((BaseOption)new SliderOption(val2, new SliderConfig { min = 0f, max = 100f })); dictionary.Add(key2, val2); ConfigEntry<float> val3 = config.Bind<float>(skinName, "Max " + key2 + " Size", 100f, "Maximum size for the part \"" + key2 + "\".\nBodyBlend will interpolate the size from the minimum value to the maximum value."); ModSettingsManager.AddOption((BaseOption)new SliderOption(val3, new SliderConfig { min = 0f, max = 100f })); dictionary2.Add(key2, val3); } MinBlendConfigs.Add(key, dictionary); MaxBlendConfigs.Add(key, dictionary2); } } } [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInPlugin("com.Maiesen.BodyBlend", "BodyBlend", "0.3.5")] public sealed class BodyBlendPlugin : BaseUnityPlugin { public const string MODNAME = "BodyBlend"; public const string AUTHOR = "Maiesen"; public const string GUID = "com.Maiesen.BodyBlend"; public const string VERSION = "0.3.5"; private const BindingFlags AllFlags = BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic; private static Dictionary<string, BBJsonConfig> FoundJson = new Dictionary<string, BBJsonConfig>(); public static event Action AfterBodyBlendLoaded; private void Awake() { //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Expected O, but got Unknown //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Expected O, but got Unknown Log.Init(((BaseUnityPlugin)this).Logger); ModelSkinController.ApplySkin += new Manipulator(ILModelSkinControllerApplySkin); SurvivorMannequinSlotController.ApplyLoadoutToMannequinInstance += new Manipulator(ILSurvivorMannequinSlotControllerApplyLoadout); if (SuspiciousTentacleCompatibility.enabled) { SuspiciousTentacleCompatibility.HookGrowthProgress(); } } private void Start() { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Expected O, but got Unknown //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Unknown result type (might be due to invalid IL or missing references) Texture2D val = new Texture2D(2, 2, (TextureFormat)4, false); using (MemoryStream memoryStream = new MemoryStream()) { Assembly.GetExecutingAssembly().GetManifestResourceStream("BodyBlend.icon.png").CopyTo(memoryStream); ImageConversion.LoadImage(val, memoryStream.ToArray()); } Sprite modIcon = Sprite.Create(val, new Rect(0f, 0f, (float)((Texture)val).height, (float)((Texture)val).width), new Vector2(0.5f, 0.5f), 100f); ModSettingsManager.SetModIcon(modIcon); RoR2Application.onLoad = (Action)Delegate.Combine(RoR2Application.onLoad, new Action(OnLoad)); SearchConfigJson(); } private void OnLoad() { SkinDef[] allSkinDefs = SkinCatalog.allSkinDefs; foreach (SkinDef skinDef in allSkinDefs) { LoadBodyBlendJson(skinDef); } BodyBlendOptions.InitializeOptions(((BaseUnityPlugin)this).Config); BodyBlendPlugin.AfterBodyBlendLoaded?.Invoke(); if (SuspiciousTentacleCompatibility.enabled) { SuspiciousTentacleCompatibility.SetupCompatibility(); } } public static void ReloadJson() { Log.Info("Reloading config files."); SearchConfigJson(); BodyBlendUtils.ClearRegister(); SkinDef[] allSkinDefs = SkinCatalog.allSkinDefs; foreach (SkinDef skinDef in allSkinDefs) { LoadBodyBlendJson(skinDef, overwrite: true); } } public static List<string> GetBodyBlendParts() { return BodyBlendUtils.PartsList.ToList(); } internal static void ILModelSkinControllerApplySkin(ILContext il) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Expected O, but got Unknown //IL_0086: Unknown result type (might be due to invalid IL or missing references) //IL_0092: Unknown result type (might be due to invalid IL or missing references) //IL_00b4: Unknown result type (might be due to invalid IL or missing references) //IL_00c0: Unknown result type (might be due to invalid IL or missing references) //IL_00cc: Unknown result type (might be due to invalid IL or missing references) //IL_00d8: Unknown result type (might be due to invalid IL or missing references) ILCursor val = new ILCursor(il); if (val.TryGotoNext((MoveType)2, new Func<Instruction, bool>[3] { (Instruction x) => ILPatternMatchingExt.MatchLdarg(x, 0), (Instruction x) => ILPatternMatchingExt.MatchCall<Component>(x, "get_gameObject"), (Instruction x) => ILPatternMatchingExt.MatchCallvirt<SkinDef>(x, "Apply") })) { val.Emit(OpCodes.Ldarg_0); val.Emit(OpCodes.Ldfld, typeof(ModelSkinController).GetField("skins", BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)); val.Emit(OpCodes.Ldarg_1); val.Emit(OpCodes.Ldelem_Ref); val.Emit(OpCodes.Ldarg_0); val.Emit(OpCodes.Call, (MethodBase)typeof(Component).GetMethod("get_gameObject")); val.EmitDelegate<Action<SkinDef, GameObject>>((Action<SkinDef, GameObject>)SetUpBodyBlend); } } internal static void ILSurvivorMannequinSlotControllerApplyLoadout(ILContext il) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Expected O, but got Unknown //IL_0093: Unknown result type (might be due to invalid IL or missing references) //IL_00a6: Unknown result type (might be due to invalid IL or missing references) //IL_00b9: Unknown result type (might be due to invalid IL or missing references) ILCursor val = new ILCursor(il); int localSkinDefIndex = -1; int localGameObjectIndex = -1; if (val.TryGotoNext((MoveType)2, new Func<Instruction, bool>[4] { (Instruction x) => ILPatternMatchingExt.MatchLdloc(x, ref localSkinDefIndex), (Instruction x) => ILPatternMatchingExt.MatchLdloc(x, ref localGameObjectIndex), (Instruction x) => ILPatternMatchingExt.MatchCallvirt<Component>(x, "get_gameObject"), (Instruction x) => ILPatternMatchingExt.MatchCallvirt<SkinDef>(x, "Apply") })) { val.Emit(OpCodes.Ldloc_S, (byte)localSkinDefIndex); val.Emit(OpCodes.Ldloc_S, (byte)localGameObjectIndex); val.Emit(OpCodes.Callvirt, (MethodBase)typeof(Component).GetMethod("get_gameObject")); val.EmitDelegate<Action<SkinDef, GameObject>>((Action<SkinDef, GameObject>)SetUpBodyBlend); } } public static void SetUpBodyBlend(SkinDef skinDef, GameObject model) { if (!(skinDef.nameToken == "") || !(((Object)skinDef).name == "")) { BodyBlendController component = model.GetComponent<BodyBlendController>(); if ((Object)(object)component != (Object)null) { Object.DestroyImmediate((Object)(object)component); } if (BodyBlendUtils.FindRegisteredSkinControl(skinDef) != null) { BodyBlendController controller = model.AddComponent<BodyBlendController>(); controller.ApplyFromRegisteredBlendControls(model, skinDef); Log.Info("BodyBlend set up successfully."); } } } private static void LoadBodyBlendJson(SkinDef skinDef, bool overwrite = false) { if (BodyBlendUtils.FindRegisteredSkinControl(skinDef) != null && !overwrite) { return; } string text = BodyBlendUtils.FindSkinDefKey(FoundJson, skinDef); if (text == null) { return; } Log.Info("Loading " + ((Object)skinDef).name + "|" + skinDef.nameToken + " BodyBlend config from json."); BBJsonConfig bBJsonConfig = FoundJson[text]; if (overwrite) { string filePath = bBJsonConfig.filePath; BBJsonConfig bBJsonConfig2 = LoadJson(filePath); if (bBJsonConfig2 == null) { Log.Warning("Fail to reload BodyBlend config."); } FoundJson[text] = bBJsonConfig2; bBJsonConfig = bBJsonConfig2; } if (bBJsonConfig != null) { BodyBlendUtils.RegisterFromJson(bBJsonConfig, skinDef); } } private static void SearchConfigJson() { FoundJson.Clear(); string bepInExAssemblyDirectory = Paths.BepInExAssemblyDirectory; string fullPath = Path.GetFullPath(Path.Combine(bepInExAssemblyDirectory, "..\\plugins\\")); string[] files = Directory.GetFiles(fullPath, "*.json", SearchOption.AllDirectories); string[] array = files; foreach (string path in array) { if (!File.Exists(path) || Path.GetFileName(path) == "manifest.json") { continue; } BBJsonConfig bBJsonConfig = LoadJson(path); if (bBJsonConfig != null) { string text = BodyBlendUtils.MakeSkinDefKey(bBJsonConfig.skinDefName, bBJsonConfig.skinDefToken); if (text == null) { text = (bBJsonConfig.skinDefToken = Path.GetFileName(path).Replace(".json", "")); } FoundJson[text] = bBJsonConfig; } } } private static BBJsonConfig LoadJson(string path) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Expected O, but got Unknown TextAsset val = new TextAsset(File.ReadAllText(path)); BBJsonConfig bBJsonConfig = JsonUtility.FromJson<BBJsonConfig>(val.text); if (bBJsonConfig != null) { bBJsonConfig.filePath = path; } return bBJsonConfig; } } public enum DynBoneControlMode { ZERO_TO_BASE, FULL_CONTROL, BASE_TO_ONE } internal class DynamicBoneControl { private struct DynBoneValue { public float m_Damping; public float m_Elasticity; public float m_Stiffness; public float m_Inert; public DynBoneValue(float d, float e, float s, float i) { m_Damping = d; m_Elasticity = e; m_Stiffness = s; m_Inert = i; } } private List<DynBoneValue> defaultBones = null; private List<DynamicBone> dynamicBones = null; private AnimationCurve inertCurve = null; private AnimationCurve elasticityCurve = null; private AnimationCurve stiffnessCurve = null; private AnimationCurve dampingCurve = null; private DynBoneControlMode dynBoneControlMode = DynBoneControlMode.FULL_CONTROL; internal void SetBoneControls(List<DynamicBone> bones, AnimationCurve inertCurve = null, AnimationCurve elasticityCurve = null, AnimationCurve stiffnessCurve = null, AnimationCurve dampingCurve = null, DynBoneControlMode dynBoneControlMode = DynBoneControlMode.FULL_CONTROL) { if (bones != null && bones.Count > 0) { dynamicBones = bones; this.inertCurve = inertCurve; this.elasticityCurve = elasticityCurve; this.stiffnessCurve = stiffnessCurve; this.dampingCurve = dampingCurve; this.dynBoneControlMode = dynBoneControlMode; SaveDynBoneValues(dynamicBones); } } internal void UpdateBoneValues(float currentWeight) { if (dynamicBones != null) { for (int i = 0; i < dynamicBones.Count; i++) { dynamicBones[i].m_Inert = GetDynBoneValue(inertCurve, defaultBones[i].m_Inert, currentWeight); dynamicBones[i].m_Elasticity = GetDynBoneValue(elasticityCurve, defaultBones[i].m_Elasticity, currentWeight); dynamicBones[i].m_Stiffness = GetDynBoneValue(stiffnessCurve, defaultBones[i].m_Stiffness, currentWeight); dynamicBones[i].m_Damping = GetDynBoneValue(dampingCurve, defaultBones[i].m_Damping, currentWeight); UpdateDynBoneParameters(dynamicBones[i]); } } } private float GetDynBoneValue(AnimationCurve curve, float defaultValue, float currentWeight) { if (curve == null) { return defaultValue; } float num = Mathf.Clamp01(curve.Evaluate(currentWeight)); return dynBoneControlMode switch { DynBoneControlMode.BASE_TO_ONE => Mathf.Lerp(defaultValue, 1f, num), DynBoneControlMode.ZERO_TO_BASE => Mathf.Lerp(0f, defaultValue, num), DynBoneControlMode.FULL_CONTROL => num, _ => defaultValue, }; } private static void UpdateDynBoneParameters(DynamicBone bone) { List<Particle> particles = bone.m_Particles; for (int i = 0; i < particles.Count; i++) { Particle val = particles[i]; val.m_Damping = bone.m_Damping; val.m_Elasticity = bone.m_Elasticity; val.m_Stiffness = bone.m_Stiffness; val.m_Inert = bone.m_Inert; val.m_Radius = bone.m_Radius; float boneTotalLength = bone.m_BoneTotalLength; if (boneTotalLength > 0f) { float num = val.m_BoneLength / boneTotalLength; if (bone.m_DampingDistrib != null && bone.m_DampingDistrib.keys.Length != 0) { val.m_Damping *= bone.m_DampingDistrib.Evaluate(num); } if (bone.m_ElasticityDistrib != null && bone.m_ElasticityDistrib.keys.Length != 0) { val.m_Elasticity *= bone.m_ElasticityDistrib.Evaluate(num); } if (bone.m_StiffnessDistrib != null && bone.m_StiffnessDistrib.keys.Length != 0) { val.m_Stiffness *= bone.m_StiffnessDistrib.Evaluate(num); } if (bone.m_InertDistrib != null && bone.m_InertDistrib.keys.Length != 0) { val.m_Inert *= bone.m_InertDistrib.Evaluate(num); } if (bone.m_RadiusDistrib != null && bone.m_RadiusDistrib.keys.Length != 0) { val.m_Radius *= bone.m_RadiusDistrib.Evaluate(num); } } val.m_Damping = Mathf.Clamp01(val.m_Damping); val.m_Elasticity = Mathf.Clamp01(val.m_Elasticity); val.m_Stiffness = Mathf.Clamp01(val.m_Stiffness); val.m_Inert = Mathf.Clamp01(val.m_Inert); val.m_Radius = Mathf.Max(val.m_Radius, 0f); } } private void SaveDynBoneValues(List<DynamicBone> dynamicBones) { if (defaultBones == null) { defaultBones = new List<DynBoneValue>(); } foreach (DynamicBone dynamicBone in dynamicBones) { defaultBones.Add(new DynBoneValue(dynamicBone.m_Damping, dynamicBone.m_Elasticity, dynamicBone.m_Stiffness, dynamicBone.m_Inert)); } } } internal static class Log { private static ManualLogSource _logSource; internal static void Init(ManualLogSource logSource) { _logSource = logSource; } internal static void Debug(object data) { _logSource.LogDebug(data); } internal static void Error(object data) { _logSource.LogError(data); } internal static void Fatal(object data) { _logSource.LogFatal(data); } internal static void Info(object data) { _logSource.LogInfo(data); } internal static void Message(object data) { _logSource.LogMessage(data); } internal static void Warning(object data) { _logSource.LogWarning(data); } } internal class SuspiciousTentacleCompatibility { private static bool? _enabled; private static BaseUnityPlugin SuspiciousTentaclePlugin; private static ConfigEntry<bool> EnableSusTentacleCompat; private static Dictionary<string, float> DefaultPartInfluences = new Dictionary<string, float> { { "Belly", 25f } }; protected static Dictionary<string, ConfigEntry<float>> PartInfluences = new Dictionary<string, ConfigEntry<float>>(); public static bool enabled { get { if (!_enabled.HasValue) { _enabled = Chainloader.PluginInfos.ContainsKey("com.RuneFoxMods.SuspiciousTentacle"); } return _enabled.Value; } } private static void FindPluginInstance() { foreach (KeyValuePair<string, PluginInfo> pluginInfo in Chainloader.PluginInfos) { BepInPlugin metadata = pluginInfo.Value.Metadata; if (metadata.GUID.Equals("com.RuneFoxMods.SuspiciousTentacle")) { SuspiciousTentaclePlugin = pluginInfo.Value.Instance; break; } } } public static ICollection<string> GetParts() { return PartInfluences.Keys; } public static float GetInfluence(string part) { return PartInfluences[part].Value / 100f; } public static void SetupCompatibility() { //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Expected O, but got Unknown //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Expected O, but got Unknown FindPluginInstance(); ConfigFile config = SuspiciousTentaclePlugin.Config; EnableSusTentacleCompat = config.Bind<bool>("BodyBlend", "Enable Compatibility", true, "Enable compatibility with BodyBlend."); ModSettingsManager.AddOption((BaseOption)new CheckBoxOption(EnableSusTentacleCompat, new CheckBoxConfig()), "com.RuneFoxMods.SuspiciousTentacle", "SuspiciousTentacle"); foreach (KeyValuePair<string, float> defaultPartInfluence in DefaultPartInfluences) { CreatePartConfig(config, defaultPartInfluence.Key, defaultPartInfluence.Value); } foreach (string bodyBlendPart in BodyBlendPlugin.GetBodyBlendParts()) { if (!PartInfluences.ContainsKey(bodyBlendPart)) { CreatePartConfig(config, bodyBlendPart, 0f); } } } private static void CreatePartConfig(ConfigFile config, string part, float defaultInfluence) { //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Expected O, but got Unknown //IL_005e: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Expected O, but got Unknown //IL_0069: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Expected O, but got Unknown ConfigEntry<float> val = config.Bind<float>("BodyBlend", part + " Influence", defaultInfluence, "Determine how much \"" + part + "\" will get influenced by Suspicious Tentacle per egg.\n" + $"Default: {defaultInfluence:0}"); SliderConfig val2 = new SliderConfig(); val2.min = 0f; val2.max = 100f; ((BaseOptionConfig)val2).checkIfDisabled = new IsDisabledDelegate(GetSusTentacleBodyBlendDisable); ModSettingsManager.AddOption((BaseOption)new SliderOption(val, val2), "com.RuneFoxMods.SuspiciousTentacle", "SuspiciousTentacle"); PartInfluences[part] = val; } public static void HookGrowthProgress() { //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Expected O, but got Unknown //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Expected O, but got Unknown CharacterBody.Update += new hook_Update(UpdateBodyBlend); CharacterBody.OnBuffFinalStackLost += new hook_OnBuffFinalStackLost(ClearBodyBlend); } private static float GetBuffDuration(CharacterBody body, BuffIndex buffIndex) { //IL_0007: 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) List<TimedBuff> timedBuffs = body.timedBuffs; if (timedBuffs == null) { return 0f; } if (timedBuffs.Count > 0) { return timedBuffs.Max((TimedBuff p) => (p.buffIndex == buffIndex) ? p.timer : 0f); } return 0f; } protected static void UpdateBodyBlend(orig_Update orig, CharacterBody body) { //IL_0026: 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_0035: 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_006f: Unknown result type (might be due to invalid IL or missing references) orig.Invoke(body); if (!EnableSusTentacleCompat.Value) { return; } BuffIndex buffIndex = ((SuspiciousTentacle)SuspiciousTentaclePlugin).EggGrowthDebuff.buffIndex; if (!body.HasBuff(buffIndex)) { return; } float num = 0f; if (SuspiciousTentacle.EggGrowthTime.Value > 0f) { num = 1f - GetBuffDuration(body, buffIndex) / SuspiciousTentacle.EggGrowthTime.Value; } float num2 = body.inventory.GetItemCount(SuspiciousTentacle.SusTentacleItemDef); if (num2 < 1f) { num2 = 1f; } BodyBlendController component = ((Component)body.modelLocator.modelTransform).gameObject.GetComponent<BodyBlendController>(); if (!Object.op_Implicit((Object)(object)component)) { return; } foreach (string key in PartInfluences.Keys) { float influence = GetInfluence(key); if (influence > 0.0001f) { component.SetBlendTargetWeight(key, num * influence * num2, "SuspiciousTentacle"); } else { component.RemoveBlendTargetWeight(key, "SuspiciousTentacle"); } } } protected static void ClearBodyBlend(orig_OnBuffFinalStackLost orig, CharacterBody body, BuffDef buffDef) { //IL_004b: Unknown result type (might be due to invalid IL or missing references) orig.Invoke(body, buffDef); if (!EnableSusTentacleCompat.Value) { return; } BodyBlendController component = ((Component)body.modelLocator.modelTransform).gameObject.GetComponent<BodyBlendController>(); if (!Object.op_Implicit((Object)(object)component) || !((Object)(object)buffDef == (Object)(object)((SuspiciousTentacle)SuspiciousTentaclePlugin).EggGrowthDebuff)) { return; } foreach (string key in PartInfluences.Keys) { component.RemoveBlendTargetWeight(key, "SuspiciousTentacle"); } } private static bool GetSusTentacleBodyBlendDisable() { return !EnableSusTentacleCompat.Value; } } } namespace BodyBlend.Utils { public static class BodyBlendUtils { public class DictionaryList<K1, V> : Dictionary<K1, List<V>> { } public class NestedDictionaryList<K1, K2, V> : Dictionary<K1, DictionaryList<K2, V>> { } public class NestedDictionary<K1, K2, V> : Dictionary<K1, Dictionary<K2, V>> { } public class BlendControlTemplate { public int targetRendererIndex = -1; public Dictionary<int, AnimationCurve> blendShapeControls = new Dictionary<int, AnimationCurve>(); public List<BoneControlTemplate> boneControls = new List<BoneControlTemplate>(); } public class BoneControlTemplate { public List<string> associatedDynBoneNames = new List<string>(); public AnimationCurve dynBoneInertCurve = null; public AnimationCurve dynBoneElasticityCurve = null; public AnimationCurve dynBoneStiffnessCurve = null; public AnimationCurve dynBoneDampingCurve = null; public DynBoneControlMode dynBoneControlMode = DynBoneControlMode.FULL_CONTROL; } public static NestedDictionaryList<string, string, BlendControlTemplate> RegisteredSkinBlendControls = new NestedDictionaryList<string, string, BlendControlTemplate>(); internal static HashSet<string> PartsList = new HashSet<string>(); private static Dictionary<string, string> SkinNameMapping = new Dictionary<string, string>(); public static void ClearRegister() { RegisteredSkinBlendControls.Clear(); PartsList.Clear(); } public static string MakeSkinDefKey(BBJsonConfig config) { return MakeSkinDefKey(config.skinDefName, config.skinDefToken); } public static string MakeSkinDefKey(string skinDefName = null, string skinDefToken = null) { List<string> list = new List<string>(); if (!string.IsNullOrEmpty(skinDefName)) { list.Add(skinDefName); } if (!string.IsNullOrEmpty(skinDefToken)) { list.Add(skinDefToken); } if (list.Count == 0) { return null; } return string.Join("|", list); } public static string FindSkinDefKey<T>(Dictionary<string, T> dict, SkinDef skinDef) { return FindSkinDefKey(dict, ((Object)skinDef).name, skinDef.nameToken); } public static string FindSkinDefKey<T>(Dictionary<string, T> dict, string skinDefName = null, string skinDefToken = null) { if (dict.ContainsKey(skinDefName)) { return skinDefName; } if (dict.ContainsKey(skinDefToken)) { return skinDefToken; } if (dict.ContainsKey(skinDefName + "|" + skinDefToken)) { return skinDefName + "|" + skinDefToken; } return null; } public static string GetSkinName(string skinDefKey) { return SkinNameMapping[skinDefKey]; } public static void RegisterFromJson(BBJsonConfig jsonConfig, SkinDef skinDef) { string text = MakeSkinDefKey(jsonConfig); if (text == null) { return; } if (string.IsNullOrEmpty(jsonConfig.skinName)) { SkinNameMapping[text] = ((Object)skinDef).name; } else { SkinNameMapping[text] = jsonConfig.skinName; } DictionaryList<string, BlendControlTemplate> dictionaryList = jsonConfig.ToTemplates(); foreach (KeyValuePair<string, List<BlendControlTemplate>> item in dictionaryList) { RegisterSkinBlendControl(text, item.Key, item.Value); } } public static void RegisterSkinBlendControl(string skinDefKey, string blendName, List<BlendControlTemplate> templates) { if (!RegisteredSkinBlendControls.ContainsKey(skinDefKey)) { RegisteredSkinBlendControls[skinDefKey] = new DictionaryList<string, BlendControlTemplate>(); } if (!RegisteredSkinBlendControls[skinDefKey].ContainsKey(blendName)) { RegisteredSkinBlendControls[skinDefKey][blendName] = new List<BlendControlTemplate>(); } RegisteredSkinBlendControls[skinDefKey][blendName] = templates; PartsList.Add(blendName); } public static void RegisterSkinBlendControl(string skinDefKey, string blendName, BlendControlTemplate template) { RegisterSkinBlendControl(skinDefKey, blendName, new List<BlendControlTemplate> { template }); } public static TV GetValue<TK, TV>(this IDictionary<TK, TV> dict, TK key, TV defaultValue = default(TV)) { TV value; return dict.TryGetValue(key, out value) ? value : defaultValue; } internal static void ApplyFromRegisteredBlendControls(this BodyBlendController controller, GameObject modelObject, SkinDef skinDef) { if (!Object.op_Implicit((Object)(object)controller)) { return; } CharacterModel component = modelObject.GetComponent<CharacterModel>(); if (!Object.op_Implicit((Object)(object)component)) { return; } string text = FindSkinDefKey(RegisteredSkinBlendControls, skinDef); if (!RegisteredSkinBlendControls.ContainsKey(text)) { return; } controller.SetSkinDefKey(text); foreach (KeyValuePair<string, List<BlendControlTemplate>> item in RegisteredSkinBlendControls[text]) { controller.ApplyFromTemplates(component, item.Key, item.Value); } } private static void ApplyFromTemplates(this BodyBlendController controller, CharacterModel charModel, string blendName, List<BlendControlTemplate> templates) { foreach (BlendControlTemplate template in templates) { if (template == null || template.targetRendererIndex < 0) { break; } SkinnedMeshRenderer renderer = GetRenderer(charModel, template.targetRendererIndex); if (!Object.op_Implicit((Object)(object)renderer)) { continue; } BodyBlendControl bodyBlendControl = new BodyBlendControl(renderer); bodyBlendControl.blendShapeControls = template.blendShapeControls; if (template.boneControls != null && template.boneControls.Count > 0) { foreach (BoneControlTemplate boneControl in template.boneControls) { List<DynamicBone> bones = FindDynamicBones(charModel, boneControl.associatedDynBoneNames); bodyBlendControl.AddDynBoneControl(bones, boneControl.dynBoneInertCurve, boneControl.dynBoneElasticityCurve, boneControl.dynBoneStiffnessCurve, boneControl.dynBoneDampingCurve, boneControl.dynBoneControlMode); } } controller.AddBlendControl(blendName, bodyBlendControl); } } public static string FindRegisteredSkinControl(SkinDef skinDef) { return FindSkinDefKey(RegisteredSkinBlendControls, skinDef); } public static List<DynamicBone> FindDynamicBones(CharacterModel charModel, params string[] names) { List<DynamicBone> list = new List<DynamicBone>(); foreach (string boneName in names) { DynamicBone val = FindDynamicBone(charModel, boneName); if (Object.op_Implicit((Object)(object)val)) { list.Add(val); } } return list; } public static List<DynamicBone> FindDynamicBones(CharacterModel charModel, List<string> names) { List<DynamicBone> list = new List<DynamicBone>(); foreach (string name in names) { DynamicBone val = FindDynamicBone(charModel, name); if (Object.op_Implicit((Object)(object)val)) { list.Add(val); } } return list; } public static DynamicBone FindDynamicBone(CharacterModel charModel, string boneName) { Transform? obj = ((IEnumerable<Transform>)((Component)charModel).gameObject.GetComponentsInChildren<Transform>()).FirstOrDefault((Func<Transform, bool>)((Transform c) => ((Object)((Component)c).gameObject).name == boneName)); GameObject val = ((obj != null) ? ((Component)obj).gameObject : null); if (Object.op_Implicit((Object)(object)val)) { return val.GetComponent<DynamicBone>(); } return null; } public static SkinnedMeshRenderer GetRenderer(CharacterModel charModel, int index) { //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Expected O, but got Unknown if (index < charModel.baseRendererInfos.Length) { return (SkinnedMeshRenderer)charModel.baseRendererInfos[index].renderer; } return null; } public static AnimationCurve MakeAnimationCurve(params Keyframe[] keyframes) { //IL_0002: 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_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Expected O, but got Unknown return new AnimationCurve(keyframes) { postWrapMode = (WrapMode)8, preWrapMode = (WrapMode)8 }; } } }