Decompiled source of HidePlayerExplosions v0.1.4

HidePlayerExplosions.dll

Decompiled a day ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using BepInEx;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Pigeon.Movement;
using UnityEngine;
using UnityEngine.SceneManagement;
using XpScaling;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: IgnoresAccessChecksTo("Assembly-CSharp")]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("HidePlayerExplosions")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+ae4467691bf4c871254e0db66c519e3d61aeb370")]
[assembly: AssemblyProduct("HidePlayerExplosions")]
[assembly: AssemblyTitle("HidePlayerExplosions")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.0.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 XpScaling
{
	internal static class PatchGroup
	{
		public static bool TryApply(string harmonyId, Action<string> logError, params Type[] patches)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Expected O, but got Unknown
			Harmony val = new Harmony(harmonyId);
			try
			{
				foreach (Type type in patches)
				{
					val.PatchAll(type);
				}
				return true;
			}
			catch (Exception arg)
			{
				val.UnpatchSelf();
				logError($"{harmonyId} is off, could not patch: {arg}");
				return false;
			}
		}
	}
}
namespace HidePlayerExplosions
{
	internal enum Maker
	{
		Unknown,
		Player,
		Other
	}
	internal static class ExplosionRules
	{
		public const int Never = -2;

		public const int Always = -1;

		public static int VisualRule(string methodName, string[] parameterNames)
		{
			if (methodName.Contains("FirstPerson") || methodName.Contains("ClientSwitch"))
			{
				return -1;
			}
			for (int i = 0; i < parameterNames.Length; i++)
			{
				if (parameterNames[i] == "firstPerson")
				{
					return i;
				}
			}
			return -2;
		}

		public static bool LeadsTo<T>(T source, Func<T, bool> isWanted, Func<T, T> baseOf, Func<T, T> parentOf, int maxDepth = 8) where T : class
		{
			int num = 0;
			while (source != null && num < maxDepth)
			{
				if (isWanted(source))
				{
					return true;
				}
				T val = baseOf(source);
				if (val != null && isWanted(val))
				{
					return true;
				}
				source = parentOf(source);
				num++;
			}
			return false;
		}

		public static Maker EnterSourced(bool playerMade)
		{
			if (!playerMade)
			{
				return Maker.Other;
			}
			return Maker.Player;
		}

		public static Maker EnterVisual(Maker current, bool playerVisual)
		{
			if (!(current == Maker.Unknown && playerVisual))
			{
				return current;
			}
			return Maker.Player;
		}

		public static Maker EnterTarget(Maker current)
		{
			if (current != Maker.Player)
			{
				return current;
			}
			return Maker.Unknown;
		}
	}
	internal sealed class Hider<TEffect, TPart>
	{
		private readonly Func<TEffect, TPart[]> partsOf;

		private readonly Func<TPart, bool> isOn;

		private readonly Action<TPart, bool> turn;

		private readonly Dictionary<TEffect, TPart[]> parts = new Dictionary<TEffect, TPart[]>();

		private readonly Dictionary<TEffect, List<TPart>> turnedOff = new Dictionary<TEffect, List<TPart>>();

		public int Count => turnedOff.Count;

		public Hider(Func<TEffect, TPart[]> partsOf, Func<TPart, bool> isOn, Action<TPart, bool> turn)
		{
			this.partsOf = partsOf;
			this.isOn = isOn;
			this.turn = turn;
		}

		public void Hide(TEffect effect)
		{
			if (!parts.TryGetValue(effect, out var value))
			{
				value = (parts[effect] = partsOf(effect));
			}
			if (!turnedOff.TryGetValue(effect, out var value2))
			{
				value2 = (turnedOff[effect] = new List<TPart>());
			}
			TPart[] array2 = value;
			foreach (TPart val in array2)
			{
				if (isOn(val))
				{
					turn(val, arg2: false);
					value2.Add(val);
				}
			}
		}

		public bool Show(TEffect effect)
		{
			if (!turnedOff.TryGetValue(effect, out var value))
			{
				return false;
			}
			turnedOff.Remove(effect);
			foreach (TPart item in value)
			{
				turn(item, arg2: true);
			}
			return true;
		}

		public void Forget(Func<TEffect, bool> gone)
		{
			foreach (TEffect item in parts.Keys.Where(gone).ToList())
			{
				parts.Remove(item);
				turnedOff.Remove(item);
			}
		}
	}
	[MycoMod(/*Could not decode attribute arguments.*/)]
	[BepInPlugin("hideplayerexplosions", "HidePlayerExplosions", "0.1.4")]
	[BepInProcess("Mycopunk.exe")]
	public class Plugin : BaseUnityPlugin
	{
		[HarmonyPatch]
		private static class SourcedExplosionPatch
		{
			private static IEnumerable<MethodBase> TargetMethods()
			{
				return Found(from m in AccessTools.GetDeclaredMethods(typeof(GameManager))
					where m.Name.StartsWith("SpawnExplosion") && m.GetParameters().Any((ParameterInfo p) => p.Name == "source" && p.ParameterType == typeof(IDamageSource))
					select m, "sourced explosion methods");
			}

			private static void Prefix(IDamageSource source, out Maker? __state)
			{
				__state = maker;
				maker = ExplosionRules.EnterSourced(IsPlayerMade(source));
			}

			private static void Finalizer(Maker? __state)
			{
				Restore(__state);
			}
		}

		[HarmonyPatch]
		private static class BulletExplosionPatch
		{
			private static IEnumerable<MethodBase> TargetMethods()
			{
				return Found(Find(), "bullet Detonate and OnFire methods");
			}

			private static IEnumerable<MethodBase> Find()
			{
				Type[] typesFromAssembly = AccessTools.GetTypesFromAssembly(typeof(IBullet).Assembly);
				foreach (Type type in typesFromAssembly)
				{
					if (!type.IsClass || !typeof(IBullet).IsAssignableFrom(type))
					{
						continue;
					}
					foreach (MethodInfo declaredMethod in AccessTools.GetDeclaredMethods(type))
					{
						if (!declaredMethod.IsStatic && !declaredMethod.IsAbstract && !declaredMethod.IsGenericMethod && (declaredMethod.Name == "Detonate" || declaredMethod.Name == "OnFire"))
						{
							yield return declaredMethod;
						}
					}
				}
			}

			private static void Prefix(IDamageSource __instance, out Maker? __state)
			{
				__state = maker;
				maker = ExplosionRules.EnterSourced(IsPlayerMade(__instance));
			}

			private static void Finalizer(Maker? __state)
			{
				Restore(__state);
			}
		}

		[HarmonyPatch]
		private static class TargetDamagePatch
		{
			private static IEnumerable<MethodBase> TargetMethods()
			{
				return Found(Find(), "target Damage methods");
			}

			private static IEnumerable<MethodBase> Find()
			{
				Type[] typesFromAssembly = AccessTools.GetTypesFromAssembly(typeof(ITarget).Assembly);
				foreach (Type type in typesFromAssembly)
				{
					if (!type.IsClass || !typeof(ITarget).IsAssignableFrom(type))
					{
						continue;
					}
					foreach (MethodInfo declaredMethod in AccessTools.GetDeclaredMethods(type))
					{
						if (!declaredMethod.IsStatic && !declaredMethod.IsAbstract && !declaredMethod.IsGenericMethod && (declaredMethod.Name == "Damage" || declaredMethod.Name.EndsWith(".Damage")) && declaredMethod.GetParameters().Any((ParameterInfo p) => p.ParameterType == typeof(IDamageSource)))
						{
							yield return declaredMethod;
						}
					}
				}
			}

			private static void Prefix(out Maker? __state)
			{
				__state = maker;
				maker = ExplosionRules.EnterTarget(maker);
			}

			private static void Finalizer(Maker? __state)
			{
				Restore(__state);
			}
		}

		[HarmonyPatch]
		private static class OwnedVisualPatch
		{
			private static IEnumerable<MethodBase> TargetMethods()
			{
				return Found(VisualMethods((int rule) => rule == -1), "owner-switching explosion methods");
			}

			private static void Prefix(out Maker? __state)
			{
				__state = maker;
				maker = ExplosionRules.EnterVisual(maker, playerVisual: true);
			}

			private static void Finalizer(Maker? __state)
			{
				Restore(__state);
			}
		}

		[HarmonyPatch]
		private static class FirstPersonFlagPatch
		{
			private static IEnumerable<MethodBase> TargetMethods()
			{
				return Found(VisualMethods((int rule) => rule >= 0), "first-person explosion methods");
			}

			private static void Prefix(bool firstPerson, out Maker? __state)
			{
				__state = maker;
				maker = ExplosionRules.EnterVisual(maker, firstPerson);
			}

			private static void Finalizer(Maker? __state)
			{
				Restore(__state);
			}
		}

		[HarmonyPatch]
		private static class ExplosionPrefabPatch
		{
			private static IEnumerable<MethodBase> TargetMethods()
			{
				return Found(from m in AccessTools.GetDeclaredMethods(typeof(Global))
					where m.ReturnType == typeof(BurstEffect) && (m.Name == "GetExplosionEffect" || m.Name == "get_DefaultExplosionEffect")
					select m, "explosion effect lookups");
			}

			private static void Postfix(BurstEffect __result)
			{
				if ((Object)(object)__result != (Object)null)
				{
					explosionPrefabs.Add(__result);
				}
			}
		}

		[HarmonyPatch]
		private static class BurstEffectSpawnPatch
		{
			private static IEnumerable<MethodBase> TargetMethods()
			{
				return Found(Find(), "effect spawn methods");
			}

			private static IEnumerable<MethodBase> Find()
			{
				foreach (Type item in BurstEffectTypes())
				{
					foreach (MethodInfo declaredMethod in AccessTools.GetDeclaredMethods(item))
					{
						ParameterInfo[] parameters = declaredMethod.GetParameters();
						if (declaredMethod.IsStatic && !declaredMethod.IsGenericMethod && typeof(BurstEffect).IsAssignableFrom(declaredMethod.ReturnType) && parameters.Length != 0 && typeof(BurstEffect).IsAssignableFrom(parameters[0].ParameterType))
						{
							yield return declaredMethod;
						}
					}
				}
			}

			private static void Postfix(BurstEffect __0, BurstEffect __result)
			{
				HideOrShow(__0, __result, atSpawn: true);
			}
		}

		[HarmonyPatch]
		private static class BurstEffectPlayPatch
		{
			private static IEnumerable<MethodBase> TargetMethods()
			{
				return Found(Find(), "effect Play methods");
			}

			private static IEnumerable<MethodBase> Find()
			{
				foreach (Type item in BurstEffectTypes())
				{
					foreach (MethodInfo declaredMethod in AccessTools.GetDeclaredMethods(item))
					{
						if (!declaredMethod.IsStatic && !declaredMethod.IsAbstract && !declaredMethod.IsGenericMethod && declaredMethod.Name == "Play")
						{
							yield return declaredMethod;
						}
					}
				}
			}

			private static void Prefix(BurstEffect __instance, out bool __state)
			{
				__state = __instance.isPlaying;
			}

			private static void Postfix(BurstEffect __instance, bool __state)
			{
				if (!__state)
				{
					HideOrShow(__instance.prefab, __instance, atSpawn: false);
				}
			}
		}

		private static Maker maker;

		private static readonly HashSet<BurstEffect> explosionPrefabs = new HashSet<BurstEffect>();

		private static bool listedElementPrefabs;

		private static readonly Hider<BurstEffect, Renderer> hider = new Hider<BurstEffect, Renderer>((BurstEffect effect) => ((Component)effect).GetComponentsInChildren<Renderer>(true), (Renderer renderer) => (Object)(object)renderer != (Object)null && renderer.enabled, delegate(Renderer renderer, bool on)
		{
			if ((Object)(object)renderer != (Object)null)
			{
				renderer.enabled = on;
			}
		});

		private static readonly HashSet<BurstEffect> hiddenAtSpawn = new HashSet<BurstEffect>();

		private static readonly Func<IDamageSource, bool> isPlayers = IsPlayers;

		private static readonly Func<IDamageSource, IDamageSource> baseOf = (IDamageSource source) => source.BaseSource;

		private static readonly Func<IDamageSource, IDamageSource> parentOf = (IDamageSource source) => source.ParentSource;

		private void Awake()
		{
			if (PatchGroup.TryApply("hideplayerexplosions", (Action<string>)((BaseUnityPlugin)this).Logger.LogError, new Type[8]
			{
				typeof(SourcedExplosionPatch),
				typeof(BulletExplosionPatch),
				typeof(TargetDamagePatch),
				typeof(OwnedVisualPatch),
				typeof(FirstPersonFlagPatch),
				typeof(ExplosionPrefabPatch),
				typeof(BurstEffectSpawnPatch),
				typeof(BurstEffectPlayPatch)
			}))
			{
				SceneManager.sceneLoaded += delegate
				{
					ForgetDestroyed();
				};
			}
		}

		private static void ForgetDestroyed()
		{
			hider.Forget((BurstEffect effect) => (Object)(object)effect == (Object)null);
			hiddenAtSpawn.RemoveWhere((BurstEffect effect) => (Object)(object)effect == (Object)null);
		}

		private static bool IsPlayers(IDamageSource source)
		{
			if (!(source is Player) && !(source is IGear))
			{
				Turret val = (Turret)(object)((source is Turret) ? source : null);
				if (val != null)
				{
					return (Object)(object)val.player != (Object)null;
				}
				return false;
			}
			return true;
		}

		private static bool IsPlayerMade(IDamageSource source)
		{
			try
			{
				return ExplosionRules.LeadsTo(source, isPlayers, baseOf, parentOf);
			}
			catch (Exception)
			{
				return false;
			}
		}

		private static void ListElementPrefabs()
		{
			Global instance = Global.Instance;
			if ((Object)(object)instance == (Object)null || instance.statusEffects == null)
			{
				return;
			}
			listedElementPrefabs = true;
			StatusEffectData[] statusEffects = instance.statusEffects;
			foreach (StatusEffectData val in statusEffects)
			{
				if (!((Object)(object)val == (Object)null))
				{
					Add(val.explosionEffect);
					Add(val.minimalExplosionEffect);
					Add(val.firstPersonExplosionEffect);
				}
			}
			static void Add(BurstEffect prefab)
			{
				if ((Object)(object)prefab != (Object)null)
				{
					explosionPrefabs.Add(prefab);
				}
			}
		}

		private static void HideOrShow(BurstEffect prefab, BurstEffect effect, bool atSpawn)
		{
			bool flag = maker == Maker.Player;
			if ((!flag && hider.Count == 0) || (Object)(object)effect == (Object)null)
			{
				return;
			}
			if (flag)
			{
				if (!listedElementPrefabs)
				{
					ListElementPrefabs();
				}
				flag = (Object)(object)prefab != (Object)null && explosionPrefabs.Contains(prefab);
			}
			if (flag)
			{
				hider.Hide(effect);
				if (atSpawn)
				{
					hiddenAtSpawn.Add(effect);
				}
			}
			else if (atSpawn || !hiddenAtSpawn.Contains(effect))
			{
				hider.Show(effect);
				hiddenAtSpawn.Remove(effect);
			}
		}

		private static void Restore(Maker? saved)
		{
			if (saved.HasValue)
			{
				maker = saved.Value;
			}
		}

		private static List<MethodBase> Found(IEnumerable<MethodBase> methods, string what)
		{
			List<MethodBase> list = methods.ToList();
			if (list.Count == 0)
			{
				throw new MissingMethodException("no " + what + " found in the game");
			}
			return list;
		}

		private static IEnumerable<MethodBase> VisualMethods(Func<int, bool> rule)
		{
			return from m in AccessTools.GetDeclaredMethods(typeof(GameManager))
				where m.Name.StartsWith("SpawnExplosionVisual") && rule(ExplosionRules.VisualRule(m.Name, (from p in m.GetParameters()
					select p.Name).ToArray()))
				select m;
		}

		private static IEnumerable<Type> BurstEffectTypes()
		{
			return AccessTools.GetTypesFromAssembly(typeof(BurstEffect).Assembly).Where(typeof(BurstEffect).IsAssignableFrom);
		}
	}
}
namespace System.Runtime.CompilerServices
{
	[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
	internal sealed class IgnoresAccessChecksToAttribute : Attribute
	{
		public IgnoresAccessChecksToAttribute(string assemblyName)
		{
		}
	}
}