Decompiled source of FoodTweaks v0.1.2

FoodTweaks.dll

Decompiled 9 hours ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using FoodTweaks.Extensions;
using HarmonyLib;
using Jotunn.Extensions;
using Jotunn.Managers;
using Jotunn.Utils;
using Logging;
using Microsoft.CodeAnalysis;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyTitle("FoodTweaks")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("ToolTweaks")]
[assembly: AssemblyCopyright("Copyright ©  2026")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("e3243d22-4307-4008-ba36-9f326008cde5")]
[assembly: AssemblyFileVersion("0.1.2")]
[assembly: TargetFramework(".NETFramework,Version=v4.8.1", FrameworkDisplayName = "")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("0.1.2.0")]
[module: UnverifiableCode]
[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 Logging
{
	internal static class Log
	{
		internal enum InfoLevel
		{
			Low,
			Medium,
			High
		}

		private static ManualLogSource logSource;

		internal static ConfigEntry<InfoLevel> Verbosity { get; set; }

		internal static InfoLevel VerbosityLevel => Verbosity.Value;

		internal static bool IsVerbosityLow => Verbosity.Value >= InfoLevel.Low;

		internal static bool IsVerbosityMedium => Verbosity.Value >= InfoLevel.Medium;

		internal static bool IsVerbosityHigh => Verbosity.Value >= InfoLevel.High;

		internal static void Init(ManualLogSource logSource)
		{
			Log.logSource = logSource;
		}

		internal static void LogDebug(object data)
		{
			logSource.LogDebug(data);
		}

		internal static void LogError(object data)
		{
			logSource.LogError(data);
		}

		internal static void LogFatal(object data)
		{
			logSource.LogFatal(data);
		}

		internal static void LogMessage(object data)
		{
			logSource.LogMessage(data);
		}

		internal static void LogWarning(object data)
		{
			logSource.LogWarning(data);
		}

		internal static void LogInfo(object data, InfoLevel level = InfoLevel.Low)
		{
			if (Verbosity == null || VerbosityLevel >= level)
			{
				logSource.LogInfo(data);
			}
		}

		internal static void LogGameObject(GameObject prefab, bool includeChildren = false)
		{
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0071: Expected O, but got Unknown
			LogInfo("***** " + ((Object)prefab).name + " *****");
			Component[] components = prefab.GetComponents<Component>();
			for (int i = 0; i < components.Length; i++)
			{
				LogComponent(components[i]);
			}
			if (!includeChildren)
			{
				return;
			}
			LogInfo("***** " + ((Object)prefab).name + " (children) *****");
			foreach (Transform item in prefab.transform)
			{
				Transform val = item;
				if (Object.op_Implicit((Object)(object)val))
				{
					LogInfo(" - " + ((Object)val).name);
					components = ((Component)val).GetComponents<Component>();
					for (int i = 0; i < components.Length; i++)
					{
						LogComponent(components[i]);
					}
				}
			}
		}

		internal static void LogComponent(Component compo)
		{
			if (!Object.op_Implicit((Object)(object)compo))
			{
				return;
			}
			try
			{
				LogInfo("--- " + ((object)compo).GetType().Name + ": " + ((Object)compo).name + " ---");
			}
			catch (Exception ex)
			{
				LogError(ex.ToString());
				LogWarning("Could not get type name for component!");
				return;
			}
			try
			{
				foreach (PropertyInfo declaredProperty in AccessTools.GetDeclaredProperties(((object)compo).GetType()))
				{
					try
					{
						LogInfo($" - {declaredProperty.Name} = {declaredProperty.GetValue(compo)}");
					}
					catch (Exception ex2)
					{
						LogError(ex2.ToString());
						LogWarning("Could not get property: " + declaredProperty.Name + " for component!");
					}
				}
			}
			catch (Exception ex3)
			{
				LogError(ex3.ToString());
				LogWarning("Could not get properties for component!");
			}
			try
			{
				foreach (FieldInfo declaredField in AccessTools.GetDeclaredFields(((object)compo).GetType()))
				{
					try
					{
						LogInfo($" - {declaredField.Name} = {declaredField.GetValue(compo)}");
					}
					catch (Exception ex4)
					{
						LogError(ex4.ToString());
						LogWarning("Could not get field: " + declaredField.Name + " for component!");
					}
				}
			}
			catch (Exception ex5)
			{
				LogError(ex5.ToString());
				LogWarning("Could not get fields for component!");
			}
		}
	}
}
namespace FoodTweaks
{
	[BepInPlugin("Searica.Valheim.FoodTweaks", "FoodTweaks", "0.1.2")]
	[BepInDependency("com.jotunn.jotunn", "2.30.2")]
	[NetworkCompatibility(/*Could not decode attribute arguments.*/)]
	[SynchronizationMode(/*Could not decode attribute arguments.*/)]
	internal sealed class FoodTweaks : BaseUnityPlugin
	{
		internal const string Author = "Searica";

		public const string PluginName = "FoodTweaks";

		public const string PluginGUID = "Searica.Valheim.FoodTweaks";

		public const string PluginVersion = "0.1.2";

		private const string MainSection = "Global";

		internal static FoodTweaks Instance;

		private static ConfigFileWatcher ConfigWatcher;

		internal ConfigEntry<bool> EnableExtraStaminaRegen;

		internal ConfigEntry<bool> EnableExtraEitrRegen;

		internal ConfigEntry<float> ExtraStaminaRegenFromFood;

		internal ConfigEntry<float> ExtraEitrRegenFromFood;

		private static bool ShouldUpdatePlugin;

		public void Awake()
		{
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Expected O, but got Unknown
			Instance = this;
			Log.Init(((BaseUnityPlugin)this).Logger);
			SetUpConfigEntries();
			Harmony.CreateAndPatchAll(Assembly.GetExecutingAssembly(), "Searica.Valheim.FoodTweaks");
			Game.isModded = true;
			ConfigWatcher = new ConfigFileWatcher(((BaseUnityPlugin)this).Config, 1000L);
			ConfigWatcher.OnConfigFileReloaded += delegate
			{
				UpdatePlugin(shouldSave: false);
			};
			SynchronizationManager.OnConfigurationWindowClosed += delegate
			{
				UpdatePlugin(shouldSave: true);
			};
			SynchronizationManager.OnConfigurationSynchronized += delegate
			{
				UpdatePlugin(shouldSave: true);
			};
		}

		public void OnDestroy()
		{
			((BaseUnityPlugin)this).Config.Save();
		}

		internal void SetUpConfigEntries()
		{
			((BaseUnityPlugin)this).Config.SaveOnConfigSet = false;
			Log.Verbosity = ConfigFileExtensions.BindConfigInOrder<Log.InfoLevel>(((BaseUnityPlugin)this).Config, "Global", "Verbosity", Log.InfoLevel.Low, "Low will log basic information about the mod. Medium will log information that is useful for troubleshooting. High will log a lot of information, do not set it to this without good reason as it will slow Down your game.", false, false, true, (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
			EnableExtraStaminaRegen = ConfigFileExtensions.BindConfigInOrder<bool>(((BaseUnityPlugin)this).Config, "Global", "EnableExtraStaminaRegen", true, "Whether to enable gaining extra stamina regen from food.", true, false, true, (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
			ExtraStaminaRegenFromFood = ConfigFileExtensions.BindConfigInOrder<float>(((BaseUnityPlugin)this).Config, "Global", "ExtraStaminaRegenFromFood", 1f, "The percent increase in stamina regen per 10 points of stamina gained from food.", true, false, true, (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 50f), (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
			EnableExtraEitrRegen = ConfigFileExtensions.BindConfigInOrder<bool>(((BaseUnityPlugin)this).Config, "Global", "EnableExtraEitrRegen", true, "Whether to enable gaining extra eitr regen from food.", true, false, true, (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
			ExtraEitrRegenFromFood = ConfigFileExtensions.BindConfigInOrder<float>(((BaseUnityPlugin)this).Config, "Global", "ExtraEitrRegenFromFood", 1f, "The percent increase in eitr per 10 points of eitr gained from food.", true, false, true, (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 50f), (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
			EnableExtraStaminaRegen.SettingChanged += SetUpdatePlugin;
			ExtraStaminaRegenFromFood.SettingChanged += SetUpdatePlugin;
			EnableExtraEitrRegen.SettingChanged += SetUpdatePlugin;
			ExtraEitrRegenFromFood.SettingChanged += SetUpdatePlugin;
			((BaseUnityPlugin)this).Config.Save();
		}

		private static void SetUpdatePlugin(object obj, EventArgs e)
		{
			ShouldUpdatePlugin = !ShouldUpdatePlugin | ShouldUpdatePlugin;
		}

		private static void UpdatePlugin(bool shouldSave)
		{
			if (ShouldUpdatePlugin)
			{
				ShouldUpdatePlugin = false;
				if (shouldSave)
				{
					((BaseUnityPlugin)Instance).Config.Save();
				}
			}
		}
	}
	[HarmonyPatch]
	internal static class FoodPatches
	{
		private static readonly MethodInfo ModifyStaminaRegenInfo = AccessTools.Method(typeof(SEMan), "ModifyStaminaRegen", (Type[])null, (Type[])null);

		private static readonly MethodInfo ModifyEitrRegenInfo = AccessTools.Method(typeof(SEMan), "ModifyEitrRegen", (Type[])null, (Type[])null);

		private static readonly FieldInfo SEManInfo = AccessTools.Field(typeof(Character), "m_seman");

		private static float BaseStamina = float.NaN;

		private static float BaseEitr = 0f;

		private static float StaminaFromFood = 0f;

		private static float EitrFromFood = 0f;

		private static string[] tooltipTokens = new string[5] { "$se_staminaregen", "$item_food_regen", "$item_food_duration", "$item_food_eitr", "$item_food_stamina" };

		[HarmonyPostfix]
		[HarmonyPriority(800)]
		[HarmonyBefore(new string[] { "shudnal.StaminaExtended", "shudnal.EitrMagicExtended", "MidnightsFX.ImpactfulSkills" })]
		[HarmonyPatch(typeof(Player), "GetTotalFoodValue")]
		private static void GetStatsFromFood(Player __instance, float stamina, float eitr)
		{
			if (Object.op_Implicit((Object)(object)__instance) && !((Object)(object)__instance != (Object)(object)Player.m_localPlayer))
			{
				StaminaFromFood = stamina - __instance.m_baseStamina;
				EitrFromFood = eitr;
			}
		}

		[HarmonyPostfix]
		[HarmonyPriority(800)]
		[HarmonyBefore(new string[] { "shudnal.MyLittleUI", "shudnal.StaminaExtended", "shudnal.EitrMagicExtended" })]
		[HarmonyPatch(typeof(ItemData), "GetTooltip", new Type[]
		{
			typeof(ItemData),
			typeof(int),
			typeof(bool),
			typeof(float),
			typeof(int),
			typeof(bool)
		})]
		private static void ItemDrop_ItemData_GetTooltip_StaminaRegenTooltipForFoodRegen(ItemData item, ref string __result)
		{
			if (!FoodTweaks.Instance.EnableExtraStaminaRegen.Value && !FoodTweaks.Instance.EnableExtraEitrRegen.Value)
			{
				return;
			}
			string[] array;
			if (FoodTweaks.Instance.EnableExtraStaminaRegen.Value && IsFoodItemForExtraStaminaRegeneration(item, out var foodStamina))
			{
				int num = -1;
				array = tooltipTokens;
				foreach (string value in array)
				{
					num = __result.IndexOf(value, StringComparison.InvariantCulture);
					if (num != -1)
					{
						break;
					}
				}
				if (num == -1)
				{
					return;
				}
				string text = $"\n$se_staminaregen: <color=#ffff80ff>{GetStaminaRegenerationValueFromStaminaPoints(foodStamina):P1}</color> ($item_current:<color=yellow>{GetStaminaMultiplier():P1}</color>)";
				int num2 = __result.IndexOf("\n", num, StringComparison.InvariantCulture);
				if (num2 != -1)
				{
					__result = __result.Insert(num2, text);
				}
				else
				{
					__result += text;
				}
			}
			if (!FoodTweaks.Instance.EnableExtraEitrRegen.Value || !IsFoodItemForExtraEitrRegeneration(item, out var foodEitr))
			{
				return;
			}
			int num3 = -1;
			array = tooltipTokens;
			foreach (string value2 in array)
			{
				num3 = __result.IndexOf(value2, StringComparison.InvariantCulture);
				if (num3 != -1)
				{
					break;
				}
			}
			if (num3 != -1)
			{
				string text2 = $"\n$se_eitrregen: <color=#9090ffff>{GetEitrRegenerationValueFromEitrPoints(foodEitr):P1}</color> ($item_current:<color=yellow>{GetEitrMultiplier():P1}</color>)";
				int num4 = __result.IndexOf("\n", num3, StringComparison.InvariantCulture);
				if (num4 != -1)
				{
					__result = __result.Insert(num4, text2);
				}
				else
				{
					__result += text2;
				}
			}
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(TextsDialog), "AddActiveEffects")]
		private static void TextsDialog_AddActiveEffects_SeasonTooltipWhenBuffDisabled(TextsDialog __instance)
		{
			if (Object.op_Implicit((Object)(object)Player.m_localPlayer))
			{
				float staminaMultiplier = GetStaminaMultiplier();
				if (staminaMultiplier >= 0.01f)
				{
					TextInfo obj = __instance.m_texts[0];
					obj.m_text += Localization.instance.Localize($"\n$se_staminaregen ($item_food): <color=orange>{staminaMultiplier:P1}</color>");
				}
				float eitrMultiplier = GetEitrMultiplier();
				if (eitrMultiplier >= 0.01f)
				{
					TextInfo obj2 = __instance.m_texts[0];
					obj2.m_text += Localization.instance.Localize($"\n$se_eitrregen ($item_food): <color=orange>{eitrMultiplier:P1}</color>");
				}
			}
		}

		public static float GetStaminaMultiplier()
		{
			return GetStaminaRegenerationValueFromStaminaPoints(StaminaFromFood);
		}

		private static float GetStaminaRegenerationValueFromStaminaPoints(float points)
		{
			return Mathf.Max(0f, FoodTweaks.Instance.ExtraStaminaRegenFromFood.Value / 100f * (points / 10f));
		}

		private static bool IsFoodItemForExtraStaminaRegeneration(ItemData item, out float foodStamina)
		{
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Invalid comparison between Unknown and I4
			foodStamina = 0f;
			if (!Player.m_localPlayerExists)
			{
				return false;
			}
			if ((int)item.m_shared.m_itemType == 2)
			{
				foodStamina = item.m_shared.m_foodStamina;
			}
			if (!(foodStamina > 0f))
			{
				if ((Object)(object)item.m_shared.m_appendToolTip != (Object)null)
				{
					return IsFoodItemForExtraStaminaRegeneration(item.m_shared.m_appendToolTip.m_itemData, out foodStamina);
				}
				return false;
			}
			return true;
		}

		public static float GetEitrMultiplier()
		{
			return GetEitrRegenerationValueFromEitrPoints(EitrFromFood);
		}

		private static float GetEitrRegenerationValueFromEitrPoints(float points)
		{
			return Mathf.Max(0f, FoodTweaks.Instance.ExtraEitrRegenFromFood.Value / 100f) * (points / 10f);
		}

		private static bool IsFoodItemForExtraEitrRegeneration(ItemData item, out float foodEitr)
		{
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Invalid comparison between Unknown and I4
			foodEitr = 0f;
			if (!Player.m_localPlayerExists)
			{
				return false;
			}
			if ((int)item.m_shared.m_itemType == 2)
			{
				foodEitr = item.m_shared.m_foodEitr;
			}
			if (!(foodEitr > 0f))
			{
				if ((Object)(object)item.m_shared.m_appendToolTip != (Object)null)
				{
					return IsFoodItemForExtraEitrRegeneration(item.m_shared.m_appendToolTip.m_itemData, out foodEitr);
				}
				return false;
			}
			return true;
		}

		[HarmonyTranspiler]
		[HarmonyPatch(typeof(Player), "UpdateStats", new Type[] { typeof(float) })]
		private static IEnumerable<CodeInstruction> UpdateRegenMultipliers(IEnumerable<CodeInstruction> instructions)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Expected O, but got Unknown
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Expected O, but got Unknown
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			//IL_004e: Expected O, but got Unknown
			//IL_0077: Unknown result type (might be due to invalid IL or missing references)
			//IL_007d: Expected O, but got Unknown
			//IL_008f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0095: Expected O, but got Unknown
			CodeMatcher val = new CodeMatcher(instructions, (ILGenerator)null);
			CodeMatch[] matches = (CodeMatch[])(object)new CodeMatch[2]
			{
				new CodeMatch((Func<CodeInstruction, bool>)((CodeInstruction instruct) => CodeInstructionExtensions.IsLdloc(instruct, (LocalBuilder)null)), (string)null),
				new CodeMatch((OpCode?)OpCodes.Callvirt, (object)ModifyStaminaRegenInfo, (string)null)
			};
			CodeMatch[] matches2 = (CodeMatch[])(object)new CodeMatch[2]
			{
				new CodeMatch((Func<CodeInstruction, bool>)((CodeInstruction instruct) => CodeInstructionExtensions.IsLdloc(instruct, (LocalBuilder)null)), (string)null),
				new CodeMatch((OpCode?)OpCodes.Callvirt, (object)ModifyEitrRegenInfo, (string)null)
			};
			for (int num = 0; num < 2; num++)
			{
				if (num == 0)
				{
					ApplyILPatch(val, matches, "Patching stamina regen", Transpilers.EmitDelegate<<>A{00000008}<Player, float>>((<>A{00000008}<Player, float>)UpdateStaminaMultipler_Delegate));
				}
				else
				{
					ApplyILPatch(val, matches2, "Patching eitr regen", Transpilers.EmitDelegate<<>A{00000008}<Player, float>>((<>A{00000008}<Player, float>)UpdateEitrMultipler_Delegate));
				}
			}
			return val.InstructionEnumeration();
		}

		private static void ApplyILPatch(CodeMatcher matcher, CodeMatch[] matches, string message, CodeInstruction instruction)
		{
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: Expected O, but got Unknown
			if (matcher.TryMatchStartForward(message, matches))
			{
				CodeInstruction instruction2 = matcher.Instruction;
				matcher.Advance(matches.Length).InsertAndAdvance((CodeInstruction[])(object)new CodeInstruction[2]
				{
					new CodeInstruction(OpCodes.Ldarg_0, (object)null),
					instruction2
				}).InsertAndAdvance((CodeInstruction[])(object)new CodeInstruction[1] { instruction });
			}
		}

		private static void UpdateStaminaMultipler_Delegate(Player player, ref float multiplier)
		{
			if (!((Object)(object)player != (Object)(object)Player.m_localPlayer))
			{
				multiplier += GetStaminaMultiplier();
			}
		}

		private static void UpdateEitrMultipler_Delegate(Player player, ref float multiplier)
		{
			if (!((Object)(object)player != (Object)(object)Player.m_localPlayer))
			{
				multiplier += GetEitrMultiplier();
			}
		}
	}
}
namespace FoodTweaks.Extensions
{
	internal static class CopyExtensions
	{
		private static class FieldInfoCache
		{
			private const BindingFlags AllBindings = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.GetField | BindingFlags.SetField | BindingFlags.GetProperty | BindingFlags.SetProperty;

			private static readonly Dictionary<Type, FieldInfo[]> dictionaryCache = new Dictionary<Type, FieldInfo[]>();

			internal static FieldInfo[] GetFieldInfo(Type type)
			{
				if (dictionaryCache.TryGetValue(type, out var value))
				{
					return value;
				}
				CacheTypeFieldInfo(type);
				return dictionaryCache[type];
			}

			internal static void CacheTypeFieldInfo(Type type)
			{
				if (!dictionaryCache.ContainsKey(type))
				{
					dictionaryCache.Add(type, type.GetFields(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.GetField | BindingFlags.SetField | BindingFlags.GetProperty | BindingFlags.SetProperty));
				}
			}
		}

		public static void CopyFields<T>(this T target, T source)
		{
			if (target == null || source == null)
			{
				throw new Exception("Target or/and Source Objects are null");
			}
			FieldInfo[] fieldInfo = FieldInfoCache.GetFieldInfo(source.GetType());
			foreach (FieldInfo fieldInfo2 in fieldInfo)
			{
				if (!(fieldInfo2 == null) && fieldInfo2.IsInitOnly)
				{
					fieldInfo2.SetValue(target, fieldInfo2.GetValue(source));
				}
			}
		}
	}
	internal static class EventExtensions
	{
		public static void SafeInvoke(this Action events)
		{
			if (events == null)
			{
				return;
			}
			Delegate[] invocationList = events.GetInvocationList();
			for (int i = 0; i < invocationList.Length; i++)
			{
				Action action = (Action)invocationList[i];
				try
				{
					action();
				}
				catch (Exception ex)
				{
					Log.LogWarning($"Exception thrown at event {new StackFrame(1).GetMethod().Name} in {action.Method.DeclaringType.Name}.{action.Method.Name}:\n{ex}");
				}
			}
		}

		public static void SafeInvoke<TArg1>(this Action<TArg1> events, TArg1 arg1)
		{
			if (events == null)
			{
				return;
			}
			Delegate[] invocationList = events.GetInvocationList();
			for (int i = 0; i < invocationList.Length; i++)
			{
				Action<TArg1> action = (Action<TArg1>)invocationList[i];
				try
				{
					action(arg1);
				}
				catch (Exception ex)
				{
					Log.LogWarning($"Exception thrown at event {new StackFrame(1).GetMethod().Name} in {action.Method.DeclaringType.Name}.{action.Method.Name}:\n{ex}");
				}
			}
		}

		public static void SafeInvoke<TArg1, TArg2>(this Action<TArg1, TArg2> events, TArg1 arg1, TArg2 arg2)
		{
			if (events == null)
			{
				return;
			}
			Delegate[] invocationList = events.GetInvocationList();
			for (int i = 0; i < invocationList.Length; i++)
			{
				Action<TArg1, TArg2> action = (Action<TArg1, TArg2>)invocationList[i];
				try
				{
					action(arg1, arg2);
				}
				catch (Exception ex)
				{
					Log.LogWarning($"Exception thrown at event {new StackFrame(1).GetMethod().Name} in {action.Method.DeclaringType.Name}.{action.Method.Name}:\n{ex}");
				}
			}
		}

		public static void SafeInvoke<TEventArg>(this EventHandler<TEventArg> events, object sender, TEventArg arg1)
		{
			if (events == null)
			{
				return;
			}
			Delegate[] invocationList = events.GetInvocationList();
			for (int i = 0; i < invocationList.Length; i++)
			{
				EventHandler<TEventArg> eventHandler = (EventHandler<TEventArg>)invocationList[i];
				try
				{
					eventHandler(sender, arg1);
				}
				catch (Exception ex)
				{
					Log.LogWarning($"Exception thrown at event {new StackFrame(1).GetMethod().Name} in {eventHandler.Method.DeclaringType.Name}.{eventHandler.Method.Name}:\n{ex}");
				}
			}
		}
	}
	public static class GameObjectExtensions
	{
		public static bool HasAnyComponent(this GameObject gameObject, params Type[] components)
		{
			foreach (Type type in components)
			{
				if (Object.op_Implicit((Object)(object)gameObject.GetComponent(type)))
				{
					return true;
				}
			}
			return false;
		}

		public static bool HasAnyComponent(this GameObject gameObject, params string[] componentNames)
		{
			foreach (string text in componentNames)
			{
				if (Object.op_Implicit((Object)(object)gameObject.GetComponent(text)))
				{
					return true;
				}
			}
			return false;
		}

		public static bool HasAllComponents(this GameObject gameObject, params string[] componentNames)
		{
			foreach (string text in componentNames)
			{
				if (!Object.op_Implicit((Object)(object)gameObject.GetComponent(text)))
				{
					return false;
				}
			}
			return true;
		}

		public static bool HasAllComponents(this GameObject gameObject, params Type[] components)
		{
			foreach (Type type in components)
			{
				if (!Object.op_Implicit((Object)(object)gameObject.GetComponent(type)))
				{
					return false;
				}
			}
			return true;
		}

		public static bool HasAnyComponentInChildren(this GameObject gameObject, bool includeInactive = false, params Type[] components)
		{
			foreach (Type type in components)
			{
				if (Object.op_Implicit((Object)(object)gameObject.GetComponentInChildren(type, includeInactive)))
				{
					return true;
				}
			}
			return false;
		}

		public static Transform FindDeepChild(this GameObject gameObject, string childName, IterativeSearchType searchType = (IterativeSearchType)1)
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			return gameObject.transform.FindDeepChild(childName, searchType);
		}
	}
	internal static class TypeExtensions
	{
		internal static List<T> GetAllPublicConstantValues<T>(this Type type)
		{
			return (from x in type.GetFields(BindingFlags.Static | BindingFlags.Public | BindingFlags.FlattenHierarchy)
				where x.IsLiteral && !x.IsInitOnly && x.FieldType == typeof(T)
				select (T)x.GetRawConstantValue()).ToList();
		}

		internal static List<T> GetAllPublicStaticValues<T>(this Type type)
		{
			return (from x in type.GetFields(BindingFlags.Static | BindingFlags.Public)
				where x.FieldType == typeof(T)
				select (T)x.GetValue(null)).ToList();
		}
	}
	internal static class GenericExtensions
	{
		internal static T Ref<T>(this T o) where T : Object
		{
			if (!Object.op_Implicit((Object)(object)o))
			{
				return default(T);
			}
			return o;
		}
	}
	internal static class IEnumerableExtensions
	{
		internal static void Dispose(this IEnumerable<IDisposable> collection)
		{
			foreach (IDisposable item in collection)
			{
				if (item != null)
				{
					try
					{
						item.Dispose();
					}
					catch (Exception)
					{
						Log.LogWarning("Could not dispose of item");
					}
				}
			}
		}
	}
	internal static class ReflectionUtils
	{
		public const BindingFlags AllBindings = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.GetField | BindingFlags.SetField | BindingFlags.GetProperty | BindingFlags.SetProperty;

		internal static MethodInfo GetMethod(Type type, string name, Type[] types)
		{
			MethodInfo[] methods = type.GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.GetField | BindingFlags.SetField | BindingFlags.GetProperty | BindingFlags.SetProperty);
			foreach (MethodInfo methodInfo in methods)
			{
				if (methodInfo.Name == name && HasMatchingParameterTypes(0, types, methodInfo.GetParameters()))
				{
					return methodInfo;
				}
			}
			return null;
		}

		internal static MethodInfo GetGenericMethod(Type type, string name, int genericParameterCount, Type[] types)
		{
			MethodInfo[] methods = type.GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.GetField | BindingFlags.SetField | BindingFlags.GetProperty | BindingFlags.SetProperty);
			foreach (MethodInfo methodInfo in methods)
			{
				if (methodInfo.IsGenericMethod && methodInfo.ContainsGenericParameters && methodInfo.Name == name && HasMatchingParameterTypes(genericParameterCount, types, methodInfo.GetParameters()))
				{
					return methodInfo;
				}
			}
			return null;
		}

		private static bool HasMatchingParameterTypes(int genericParameterCount, Type[] types, ParameterInfo[] parameters)
		{
			if (parameters.Length < genericParameterCount || parameters.Length != types.Length)
			{
				return false;
			}
			int num = 0;
			for (int i = 0; i < parameters.Length; i++)
			{
				if (parameters[i].ParameterType.IsGenericParameter)
				{
					num++;
				}
				else if (types[i] != parameters[i].ParameterType)
				{
					return false;
				}
			}
			if (num != genericParameterCount)
			{
				return false;
			}
			return true;
		}
	}
	public static class StringExtensions
	{
		public static bool ContainsAny(this string str, params string[] substrings)
		{
			foreach (string value in substrings)
			{
				if (str.Contains(value))
				{
					return true;
				}
			}
			return false;
		}

		public static bool EndsWithAny(this string str, params string[] suffixes)
		{
			foreach (string value in suffixes)
			{
				if (str.EndsWith(value))
				{
					return true;
				}
			}
			return false;
		}

		public static bool StartsWithAny(this string str, params string[] prefixes)
		{
			foreach (string value in prefixes)
			{
				if (str.StartsWith(value))
				{
					return true;
				}
			}
			return false;
		}

		public static string RemoveSuffix(this string s, string suffix)
		{
			if (s.EndsWith(suffix))
			{
				return s.Substring(0, s.Length - suffix.Length);
			}
			return s;
		}

		public static string RemovePrefix(this string s, string prefix)
		{
			if (s.StartsWith(prefix))
			{
				return s.Substring(prefix.Length, s.Length - prefix.Length);
			}
			return s;
		}

		public static string CapitalizeFirstLetter(this string s)
		{
			if (s.Length == 0)
			{
				return s;
			}
			if (s.Length == 1)
			{
				return $"{char.ToUpper(s[0])}";
			}
			return char.ToUpper(s[0]) + s.Substring(1);
		}
	}
	public static class TransformExtensions
	{
		public static Transform FindDeepChild(this Transform transform, string childName, IterativeSearchType searchType = (IterativeSearchType)1)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			return Utils.FindChild(transform, childName, searchType);
		}
	}
	internal static class Utils
	{
		public static bool TryMatchStartForward(this CodeMatcher matcher, string failureMessage, params CodeMatch[] matches)
		{
			matcher.MatchStartForward(matches);
			if (matcher.IsInvalid)
			{
				Log.LogWarning(failureMessage + " Anchor instruction not found, it was likely consumed by another mod. Skipping this patch.");
				return false;
			}
			return true;
		}

		public static bool TryMatchEndForward(this CodeMatcher matcher, string failureMessage, params CodeMatch[] matches)
		{
			matcher.MatchEndForward(matches);
			if (matcher.IsInvalid)
			{
				Log.LogWarning(failureMessage + " Anchor instruction not found, it was likely consumed by another mod. Skipping this patch.");
				return false;
			}
			return true;
		}
	}
}