Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of MagicShieldTweaks v0.1.1
MagicShieldTweaks.dll
Decompiled 16 hours agousing System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Reflection; 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 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("MagicShieldTweaks")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("MagicShieldTweaks")] [assembly: AssemblyCopyright("Copyright © 2026")] [assembly: AssemblyTrademark("")] [assembly: ComVisible(false)] [assembly: Guid("e3243d22-4307-4008-ba36-9f326008cde5")] [assembly: AssemblyFileVersion("0.1.1")] [assembly: TargetFramework(".NETFramework,Version=v4.8.1", FrameworkDisplayName = "")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("0.1.1.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 MagicShieldTweaks { [BepInPlugin("Searica.Valheim.MagicShieldTweaks", "MagicShieldTweaks", "0.1.1")] [BepInDependency("com.jotunn.jotunn", "2.30.2")] [NetworkCompatibility(/*Could not decode attribute arguments.*/)] [SynchronizationMode(/*Could not decode attribute arguments.*/)] internal sealed class MagicShieldTweaks : BaseUnityPlugin { internal const string Author = "Searica"; public const string PluginName = "MagicShieldTweaks"; public const string PluginGUID = "Searica.Valheim.MagicShieldTweaks"; public const string PluginVersion = "0.1.1"; private const string MainSection = "MagicShieldTweaks"; internal static MagicShieldTweaks Instance; private static ConfigFileWatcher ConfigWatcher; internal ConfigEntry<bool> AddStaffSecondaryAttacks; internal ConfigEntry<bool> ChangeShieldColorByHealth; internal ConfigEntry<Color> ShieldTargetColorZeroHealth; internal ConfigEntry<bool> PreventZeroDamageShieldSpam; internal ConfigEntry<bool> PreventLookVectorConsoleSpam; private 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.MagicShieldTweaks"); Game.isModded = true; ConfigWatcher = new ConfigFileWatcher(((BaseUnityPlugin)this).Config, 1000L); ConfigWatcher.OnConfigFileReloaded += delegate { UpdatePlugin(shouldSave: false); }; SynchronizationManager.OnConfigurationWindowClosed += delegate { UpdatePlugin(shouldSave: true); }; } public void OnDestroy() { ((BaseUnityPlugin)this).Config.Save(); } internal void SetUpConfigEntries() { //IL_00a5: Unknown result type (might be due to invalid IL or missing references) ((BaseUnityPlugin)this).Config.SaveOnConfigSet = false; Log.Verbosity = ConfigFileExtensions.BindConfigInOrder<Log.InfoLevel>(((BaseUnityPlugin)this).Config, "MagicShieldTweaks", "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); AddStaffSecondaryAttacks = ConfigFileExtensions.BindConfigInOrder<bool>(((BaseUnityPlugin)this).Config, "MagicShieldTweaks", "AddStaffSecondaryAttacks", true, "Whether to add secondary attacks to staffs that provide a shield-like status effect that allows you to remove the status effect.", false, false, true, (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null); ChangeShieldColorByHealth = ConfigFileExtensions.BindConfigInOrder<bool>(((BaseUnityPlugin)this).Config, "MagicShieldTweaks", "ChangeShieldColorByHealth", true, "Whether to change the color of your shield based on its current health.", false, false, true, (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null); ShieldTargetColorZeroHealth = ConfigFileExtensions.BindConfigInOrder<Color>(((BaseUnityPlugin)this).Config, "MagicShieldTweaks", "ShieldTargetColorZeroHealth", new Color(0f, 0f, 1f, 0.5f), "Shield will have this color at 0 health.", false, false, true, (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null); PreventZeroDamageShieldSpam = ConfigFileExtensions.BindConfigInOrder<bool>(((BaseUnityPlugin)this).Config, "MagicShieldTweaks", "PreventZeroDamageShieldSpam", true, "If you take 0 damage to your shield then prevent the hit effect from playing. Could prevent occasional hit effect spam.", false, false, true, (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null); PreventLookVectorConsoleSpam = ConfigFileExtensions.BindConfigInOrder<bool>(((BaseUnityPlugin)this).Config, "MagicShieldTweaks", "PreventLookVectorConsoleSpam", true, "If shield takes damage from an indirect hit then the 'Prevent Look Rotation Viewing Vector Is Zero' will no longer be shown in log file or console.", false, false, true, (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null); AddStaffSecondaryAttacks.SettingChanged += SetUpdatePlugin; ChangeShieldColorByHealth.SettingChanged += SetUpdatePlugin; ShieldTargetColorZeroHealth.SettingChanged += SetUpdatePlugin; PreventZeroDamageShieldSpam.SettingChanged += SetUpdatePlugin; PreventLookVectorConsoleSpam.SettingChanged += SetUpdatePlugin; ((BaseUnityPlugin)this).Config.Save(); } private static void SetUpdatePlugin(object obj, EventArgs e) { Instance.ShouldUpdatePlugin = !Instance.ShouldUpdatePlugin | Instance.ShouldUpdatePlugin; } private static void UpdatePlugin(bool shouldSave) { if (Instance.ShouldUpdatePlugin) { Instance.ShouldUpdatePlugin = false; if (shouldSave) { ((BaseUnityPlugin)Instance).Config.Save(); } } } } [HarmonyPatch] internal class ShieldPatches { [HarmonyPatch(typeof(Humanoid), "StartAttack")] public static class Humanoid_StartAttack_Staff { private static bool Prefix(Humanoid __instance, bool secondaryAttack) { if (secondaryAttack && TryGetPlayerWeapon(__instance, out var weapon) && StaffItemDataNamesToAnimations.ContainsKey(weapon.m_shared.m_name)) { if (!((Object)(object)weapon.m_shared.m_attackStatusEffect == (Object)null)) { return ((Character)Player.m_localPlayer).GetSEMan().HaveStatusEffect(weapon.m_shared.m_attackStatusEffect.NameHash()); } return true; } return true; } private static void Postfix(Humanoid __instance, bool __result) { if (__result && TryGetPlayerWeapon(__instance, out var weapon) && __instance.m_currentAttackIsSecondary && StaffItemDataNamesToAnimations.ContainsKey(weapon.m_shared.m_name)) { CurrentWeapon = weapon; CurrentAttack = __instance.m_currentAttack; } } } [HarmonyPatch(typeof(Attack), "FireProjectileBurst")] public static class Attack_FireProjectileBurst_Staff { private static float StatusEffectChance = 1f; private static void Prefix(Attack __instance, ref bool __state) { if (__instance == CurrentAttack && __instance.m_weapon == CurrentWeapon) { __state = true; StatusEffectChance = CurrentWeapon.m_shared.m_attackStatusEffectChance; CurrentWeapon.m_shared.m_attackStatusEffectChance = 0f; } else { __state = false; } } private static void Postfix(bool __state) { //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_00af: Unknown result type (might be due to invalid IL or missing references) //IL_00be: Unknown result type (might be due to invalid IL or missing references) //IL_00e0: Unknown result type (might be due to invalid IL or missing references) //IL_00e6: Unknown result type (might be due to invalid IL or missing references) if (!__state) { return; } CurrentWeapon.m_shared.m_attackStatusEffectChance = StatusEffectChance; StatusEffect attackStatusEffect = CurrentWeapon.m_shared.m_attackStatusEffect; SE_Shield val = (SE_Shield)(object)((attackStatusEffect is SE_Shield) ? attackStatusEffect : null); if (val != null) { val.m_breakEffects.Create(((Character)Player.m_localPlayer).GetCenterPoint(), ((Component)Player.m_localPlayer).transform.rotation, ((Component)Player.m_localPlayer).transform, ((Character)Player.m_localPlayer).GetRadius() * 2f, -1, default(ZDOID)); ((Character)Player.m_localPlayer).GetSEMan().RemoveStatusEffect((StatusEffect)(object)val, false); return; } StatusEffect attackStatusEffect2 = CurrentWeapon.m_shared.m_attackStatusEffect; SE_React val2 = (SE_React)(object)((attackStatusEffect2 is SE_React) ? attackStatusEffect2 : null); if (val2 != null) { ((StatusEffect)val2).m_stopEffects.Create(((Character)Player.m_localPlayer).GetCenterPoint(), ((Component)Player.m_localPlayer).transform.rotation, ((Component)Player.m_localPlayer).transform, ((Character)Player.m_localPlayer).GetRadius() * 2f, -1, default(ZDOID)); ((Character)Player.m_localPlayer).GetSEMan().RemoveStatusEffect((StatusEffect)(object)val2, false); } } } private static readonly Dictionary<SE_Shield, MeshRenderer> s_shieldSpheres = new Dictionary<SE_Shield, MeshRenderer>(); private static readonly MaterialPropertyBlock s_matBlock = new MaterialPropertyBlock(); private static readonly FieldInfo[] AttackFields = typeof(Attack).GetFields(); private const string ShieldStaffPrefabName = "StaffShield"; private const string ReactStaffPrefabName = "StaffFrostOrbs"; private static readonly HashSet<string> StaffPrefabNames = new HashSet<string> { "StaffShield", "StaffFrostOrbs" }; private static readonly Dictionary<string, string> StaffItemDataNamesToAnimations = new Dictionary<string, string> { { "$item_staffshield", "staff_trollsummon" }, { "$item_staff_frostorbs", "staff_trollsummon" } }; private static readonly HashSet<string> PatchedSecondaryAttackNames = new HashSet<string>(StaffItemDataNamesToAnimations.Values); private static ItemData CurrentWeapon; private static Attack CurrentAttack; [HarmonyPrefix] [HarmonyPriority(0)] [HarmonyPatch(typeof(SE_Shield), "OnDamaged")] private static bool SE_Shield_OnDamaged_PreventEffectSpam(ref HitData hit) { //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Unknown result type (might be due to invalid IL or missing references) if (MagicShieldTweaks.Instance.PreventZeroDamageShieldSpam.Value && hit.GetTotalDamage() == 0f) { return false; } if (MagicShieldTweaks.Instance.PreventLookVectorConsoleSpam.Value && ((Vector3)(ref hit.m_dir)).sqrMagnitude < 0.1f) { hit.m_dir = Vector3.down; } return true; } [HarmonyPostfix] [HarmonyPriority(800)] [HarmonyPatch(typeof(SE_Shield), "OnDamaged")] private static void UpdateShieldColorOnDamage(SE_Shield __instance, ref HitData hit) { UpdateShieldColor(__instance); } private static void UpdateShieldColor(SE_Shield shield) { //IL_00be: Unknown result type (might be due to invalid IL or missing references) //IL_00cd: Unknown result type (might be due to invalid IL or missing references) //IL_00df: Unknown result type (might be due to invalid IL or missing references) if (!MagicShieldTweaks.Instance.ChangeShieldColorByHealth.Value || shield.m_totalAbsorbDamage <= 0f || ((StatusEffect)shield).m_startEffectInstances == null || ((StatusEffect)shield).m_startEffectInstances.Length == 0 || !Object.op_Implicit((Object)(object)((StatusEffect)shield).m_startEffectInstances[0])) { return; } if (!s_shieldSpheres.TryGetValue(shield, out var value) || !Object.op_Implicit((Object)(object)value)) { Transform val = ((StatusEffect)shield).m_startEffectInstances[0].transform.Find("Sphere"); if (!Object.op_Implicit((Object)(object)val) || !((Component)val).TryGetComponent<MeshRenderer>(ref value) || !Object.op_Implicit((Object)(object)value)) { return; } s_shieldSpheres[shield] = value; } if (Object.op_Implicit((Object)(object)((Renderer)value).sharedMaterial)) { ((Renderer)value).GetPropertyBlock(s_matBlock); s_matBlock.SetColor(ShaderProps._Color, Color.Lerp(((Renderer)value).sharedMaterial.color, MagicShieldTweaks.Instance.ShieldTargetColorZeroHealth.Value, shield.m_damage / shield.m_totalAbsorbDamage)); ((Renderer)value).SetPropertyBlock(s_matBlock); } } [HarmonyPostfix] [HarmonyPatch(typeof(SE_Shield), "IsDone")] private static void SE_Shield_IsDone_ClearCachedSphere(SE_Shield __instance, bool __result) { if (__result) { s_shieldSpheres.Remove(__instance); } } [HarmonyPostfix] [HarmonyPatch(typeof(ZoneSystem), "OnDestroy")] private static void ZoneSystem_OnDestroy_ClearSpheresCache() { s_shieldSpheres.Clear(); } public static void PatchStaffShield(ItemData item) { //IL_00da: Unknown result type (might be due to invalid IL or missing references) //IL_00e4: Expected O, but got Unknown if (item == null || !StaffItemDataNamesToAnimations.TryGetValue(item.m_shared.m_name, out var value)) { return; } Attack attack = item.m_shared.m_secondaryAttack; if (attack != null && !(attack.m_attackAnimation == value)) { CollectionExtensions.Do<FieldInfo>((IEnumerable<FieldInfo>)AttackFields, (Action<FieldInfo>)delegate(FieldInfo f) { f.SetValue(attack, f.GetValue(item.m_shared.m_attack)); }); attack.m_attackEitr = 0f; attack.m_attackHealthPercentage = 0f; attack.m_attackAdrenaline = 0f; attack.m_attackUseAdrenaline = 0f; attack.m_attackHealthLowBlockUse = false; attack.m_attackAnimation = value; attack.m_startEffect = new EffectList(); } } [HarmonyPostfix] [HarmonyPriority(0)] [HarmonyPatch(typeof(ZoneSystem), "Start")] private static void AddStaffShieldSecondaryAttackOnZoneSystemStart() { ItemDrop val = default(ItemDrop); foreach (string staffPrefabName in StaffPrefabNames) { GameObject prefab = PrefabManager.Instance.GetPrefab(staffPrefabName); if (Object.op_Implicit((Object)(object)prefab) && prefab.TryGetComponent<ItemDrop>(ref val)) { PatchStaffShield(val.m_itemData); } } } [HarmonyPostfix] [HarmonyPriority(0)] [HarmonyPatch(typeof(Player), "Load")] private static void AddStaffShieldSecondaryAttackToPlayerItems(Player __instance) { if (Object.op_Implicit((Object)(object)__instance) && Player.m_localPlayerExists && !((Object)(object)__instance != (Object)(object)Player.m_localPlayer)) { CollectionExtensions.Do<ItemData>((IEnumerable<ItemData>)((Humanoid)__instance).GetInventory().m_inventory, (Action<ItemData>)PatchStaffShield); } } [HarmonyPostfix] [HarmonyPriority(0)] [HarmonyPatch(typeof(Humanoid), "EquipItem")] private static void AddStaffShieldSecondaryAttackOnEquipItem(Humanoid __instance, ItemData item, bool __result) { if (__result && Object.op_Implicit((Object)(object)__instance) && ((Character)__instance).IsPlayer() && !((Object)(object)__instance != (Object)(object)Player.m_localPlayer)) { PatchStaffShield(item); } } private static bool TryGetPlayerWeapon(Humanoid humanoid, out ItemData weapon) { if (!Object.op_Implicit((Object)(object)humanoid) || !((Character)humanoid).IsPlayer() || (Object)(object)humanoid != (Object)(object)Player.m_localPlayer) { weapon = null; return false; } weapon = humanoid.GetCurrentWeapon(); return weapon != null; } } } namespace MagicShieldTweaks.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; } } }