Decompiled source of BodyBlend v0.3.4

BodyBlend.dll

Decompiled 2 weeks ago
using 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 System.Threading;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BodyBlend.Utils;
using IL.RoR2;
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.0", FrameworkDisplayName = ".NET Standard 2.0")]
[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 SkinNameToken = "";

		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(SkinNameToken)?.GetValue(name)?.Value;
			if (!num.HasValue)
			{
				return 0f;
			}
			return num.Value / 100f;
		}

		private float FetchMaxBlendConfig(string name)
		{
			float? num = BodyBlendOptions.MaxBlendConfigs.GetValue(SkinNameToken)?.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 SetSkinNameToken(string name)
		{
			SkinNameToken = name;
		}
	}
	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 rederer)
		{
			targetRenderer = rederer;
		}

		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);
				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]
	internal class BBJsonConfig
	{
		public string skinName;

		public List<BBJsonPartControl> parts;
	}
	[Serializable]
	internal class BBJsonPartControl
	{
		public string name;

		public List<BBJsonCtrlTemplate> controls;
	}
	[Serializable]
	internal class BBJsonCtrlTemplate
	{
		public int targetRendererIndex = -1;

		public List<BBShapeControl> blendControls;

		public List<BBBoneControl> boneControls;
	}
	[Serializable]
	internal 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]
	internal class BBShapeControl
	{
		public int blendIdx = -1;

		public List<BBAnimKey> keyframes;
	}
	[Serializable]
	internal 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();
			val.min = 0.1f;
			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.3")]
	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.3";

		private const BindingFlags AllFlags = BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic;

		private static Dictionary<string, string> FoundJson = new Dictionary<string, string>();

		public static event Action AfterBodyBlendLoaded;

		private void Awake()
		{
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Expected O, but got Unknown
			ModelSkinController.ApplySkin += new Manipulator(ILModelSkinControllerApplySkin);
			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()
		{
			Debug.Log((object)"[BodyBlend] 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);
			}
		}

		public static void SetUpBodyBlend(SkinDef skinDef, GameObject model)
		{
			if (!(skinDef.nameToken == ""))
			{
				BodyBlendController component = model.GetComponent<BodyBlendController>();
				if (Object.op_Implicit((Object)(object)component))
				{
					Object.Destroy((Object)(object)component);
				}
				if (BodyBlendUtils.HasRegisteredSkinControl(skinDef.nameToken))
				{
					BodyBlendController controller = model.AddComponent<BodyBlendController>();
					controller.ApplyFromRegisteredBlendControls(model, skinDef.nameToken);
				}
			}
		}

		private static void LoadBodyBlendJson(SkinDef skinDef, bool overwrite = false)
		{
			//IL_007d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0083: Expected O, but got Unknown
			if ((!BodyBlendUtils.HasRegisteredSkinControl(skinDef.nameToken) || overwrite) && FoundJson.ContainsKey(skinDef.nameToken + ".json"))
			{
				string path = FoundJson[skinDef.nameToken + ".json"];
				Debug.Log((object)("[BodyBlend] Loading " + skinDef.nameToken + " BodyBlend config from json."));
				TextAsset val = new TextAsset(File.ReadAllText(path));
				if ((Object)(object)val != (Object)null)
				{
					BodyBlendUtils.RegisterFromJson(skinDef.nameToken, val);
				}
			}
		}

		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 text in array)
			{
				if (File.Exists(text) && !(Path.GetFileName(text) == "manifest.json"))
				{
					FoundJson[Path.GetFileName(text)] = text;
				}
			}
		}
	}
	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));
			}
		}
	}
	public class EnumMapper : IDisposable
	{
		public static readonly EnumMapper Instance = new EnumMapper();

		private readonly Dictionary<Type, Dictionary<string, object>> _stringsToEnums = new Dictionary<Type, Dictionary<string, object>>();

		private readonly Dictionary<Type, Dictionary<int, string>> _enumNumbersToStrings = new Dictionary<Type, Dictionary<int, string>>();

		private readonly ReaderWriterLockSlim _lock = new ReaderWriterLockSlim();

		public object EnumFromString(Type type, string value)
		{
			populateIfNotPresent(type);
			return _stringsToEnums[type][value];
		}

		public string StringFromEnum(object theEnum)
		{
			Type type = theEnum.GetType();
			populateIfNotPresent(type);
			return _enumNumbersToStrings[type][(int)theEnum];
		}

		private void populateIfNotPresent(Type type)
		{
			_lock.EnterUpgradeableReadLock();
			try
			{
				if (!_stringsToEnums.ContainsKey(type))
				{
					_lock.EnterWriteLock();
					try
					{
						populate(type);
						return;
					}
					finally
					{
						_lock.ExitWriteLock();
					}
				}
			}
			finally
			{
				_lock.ExitUpgradeableReadLock();
			}
		}

		private void populate(Type type)
		{
			Array values = Enum.GetValues(type);
			_stringsToEnums[type] = new Dictionary<string, object>(values.Length);
			_enumNumbersToStrings[type] = new Dictionary<int, string>(values.Length);
			for (int i = 0; i < values.Length; i++)
			{
				object value = values.GetValue(i);
				_stringsToEnums[type].Add(value.ToString(), value);
				_enumNumbersToStrings[type].Add((int)value, value.ToString());
			}
		}

		public void Dispose()
		{
			_lock.Dispose();
		}
	}
	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
			CharacterBody.Update += new hook_Update(UpdateBodyBlend);
		}

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

		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 GetSkinName(string skinNameToken)
		{
			return SkinNameMapping[skinNameToken];
		}

		public static void RegisterFromJson(string skinNameToken, TextAsset jsonFile)
		{
			BBJsonConfig bBJsonConfig = JsonUtility.FromJson<BBJsonConfig>(jsonFile.text);
			if (string.IsNullOrEmpty(bBJsonConfig.skinName))
			{
				SkinNameMapping[skinNameToken] = skinNameToken;
			}
			else
			{
				SkinNameMapping[skinNameToken] = bBJsonConfig.skinName;
			}
			DictionaryList<string, BlendControlTemplate> dictionaryList = bBJsonConfig.ToTemplates();
			foreach (KeyValuePair<string, List<BlendControlTemplate>> item in dictionaryList)
			{
				RegisterSkinBlendControl(skinNameToken, item.Key, item.Value);
			}
		}

		public static void RegisterSkinBlendControl(string skinNameToken, string blendName, List<BlendControlTemplate> templates)
		{
			if (!RegisteredSkinBlendControls.ContainsKey(skinNameToken))
			{
				RegisteredSkinBlendControls[skinNameToken] = new DictionaryList<string, BlendControlTemplate>();
			}
			if (!RegisteredSkinBlendControls[skinNameToken].ContainsKey(blendName))
			{
				RegisteredSkinBlendControls[skinNameToken][blendName] = new List<BlendControlTemplate>();
			}
			RegisteredSkinBlendControls[skinNameToken][blendName] = templates;
			PartsList.Add(blendName);
		}

		public static void RegisterSkinBlendControl(string skinNameToken, string blendName, BlendControlTemplate template)
		{
			RegisterSkinBlendControl(skinNameToken, 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, string skinNameToken)
		{
			if (!Object.op_Implicit((Object)(object)controller))
			{
				return;
			}
			CharacterModel component = modelObject.GetComponent<CharacterModel>();
			if (!Object.op_Implicit((Object)(object)component) || !RegisteredSkinBlendControls.ContainsKey(skinNameToken))
			{
				return;
			}
			controller.SetSkinNameToken(skinNameToken);
			foreach (KeyValuePair<string, List<BlendControlTemplate>> item in RegisteredSkinBlendControls[skinNameToken])
			{
				Debug.Log((object)("[BodyBlend] Applying " + skinNameToken + ": " + item.Key + " to BodyBlendController"));
				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 bool HasRegisteredSkinControl(string name)
		{
			return RegisteredSkinBlendControls.ContainsKey(name);
		}

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