Decompiled source of AlliesAvoidImplosions v1.0.1

AlliesAvoidImplosions.dll

Decompiled 3 months 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.Permissions;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using IL.RoR2;
using Microsoft.CodeAnalysis;
using Mono.Cecil.Cil;
using MonoMod.Cil;
using On.RoR2.Projectile;
using R2API.Utils;
using RoR2;
using RoR2.CharacterAI;
using RoR2.Projectile;
using UnityEngine;
using UnityEngine.Networking;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("Chinchi")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyDescription("No more losing your hard-earned drone army to a void seed/invasion.")]
[assembly: AssemblyFileVersion("1.0.1.0")]
[assembly: AssemblyInformationalVersion("1.0.1+14c9222c3671456d2b7954f8d6d9d46814ef3085")]
[assembly: AssemblyProduct("AlliesAvoidImplosions")]
[assembly: AssemblyTitle("AlliesAvoidImplosions")]
[assembly: AssemblyMetadata("RepositoryUrl", "https://github.com/schinchi/AlliesAvoidImplosions")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.1.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.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 AlliesAvoidImplosions
{
	[BepInPlugin("Chinchi.AlliesAvoidImplosions", "AlliesAvoidImplosions", "1.0.1")]
	[NetworkCompatibility(/*Could not decode attribute arguments.*/)]
	public class AlliesAvoidImplosions : BaseUnityPlugin
	{
		public const string PluginGUID = "Chinchi.AlliesAvoidImplosions";

		public const string PluginAuthor = "Chinchi";

		public const string PluginName = "AlliesAvoidImplosions";

		public const string PluginVersion = "1.0.1";

		internal static ManualLogSource Logger;

		internal static readonly string[] defaultAllies = new string[10] { "Drone1Master", "Drone2Master", "DroneBackupMaster", "DroneMissileMaster", "EmergencyDroneMaster", "EquipmentDroneMaster", "FlameDroneMaster", "MegaDroneMaster", "Turret1Master", "DevotedLemurianMaster" };

		private void Awake()
		{
			Logger = ((BaseUnityPlugin)this).Logger;
			Configuration.Init(((BaseUnityPlugin)this).Config);
			RoR2Application.onLoad = (Action)Delegate.Combine(RoR2Application.onLoad, new Action(Hooks.LoadDataAndPatch));
		}

		private void OnEnable()
		{
			Hooks.Apply();
			Hooks.CollectCurrentProjectiles();
		}

		private void OnDisable()
		{
			Hooks.Undo();
			Hooks.implosions.Clear();
		}
	}
	internal class Configuration
	{
		internal static ConfigEntry<string> additionalBackupEntries;

		internal static ConfigEntry<string> blacklistedBackupEntries;

		internal static ConfigEntry<string> additionalImmuneEntries;

		internal static ConfigEntry<string> blacklistedImmuneEntries;

		internal static ConfigEntry<bool> immuneToVoidDeath;

		internal static ConfigEntry<float> evasionDistance;

		internal static void Init(ConfigFile config)
		{
			additionalBackupEntries = config.Bind<string>("1. Retreat", "Additional Retreat Entities", "EngiWalkerTurretMaster", "Additional characters that back away from implosions. Use the master name of the desired entities separated by comma. You can get a list of all masters with `list_ai` from DebugToolkit.");
			blacklistedBackupEntries = config.Bind<string>("1. Retreat", "Blacklisted Retreat Entities", "EmergencyDroneMaster", "Any characters to be blacklisted from exhibiting this behaviour. Mostly because another mod also modifies the AISkillDrivers to use the custom target and it creates an incompatibility. Use the master name of the desired entities separated by comma. You can get a list of all masters with `list_ai` from DebugToolkit.");
			immuneToVoidDeath = config.Bind<bool>("2. Immunity", "Immune To Void Death", false, "Allies affected by this mod are immune to void death. A sure-fire survival alternative.");
			additionalImmuneEntries = config.Bind<string>("2. Immunity", "Additional Immune Entities", "", "Additional characters to exhibit this behaviour. Use the master name of the desired entities separated by comma. You can get a list of all masters with `list_ai` from DebugToolkit.");
			blacklistedImmuneEntries = config.Bind<string>("2. Immunity", "Blacklisted Immune Entries", "", "Any characters to be blacklisted from exhibiting this behaviour. Use the master name of the desired entities separated by comma. You can get a list of all masters with `list_ai` from DebugToolkit.");
			evasionDistance = config.Bind<float>("3. General", "Evasion Distance", 35f, "The distance to back up from an implosion. The longer the distance, the safer the minion but the longer it will override its behaviour and prevent it from engaging other enemies. For reference, a Void Devastator's implosion has a radius of 22.5 m.");
		}
	}
	internal class GTFOHController : MonoBehaviour
	{
		private static readonly List<GTFOHController> instancesList = new List<GTFOHController>();

		private BaseAI ai;

		private void Awake()
		{
			instancesList.Add(this);
			ai = ((Component)this).GetComponent<BaseAI>();
		}

		private void OnDestroy()
		{
			instancesList.Remove(this);
		}

		private void FixedUpdate()
		{
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cf: Unknown result type (might be due to invalid IL or missing references)
			if (Hooks.implosions.Count == 0)
			{
				((Behaviour)this).enabled = false;
				return;
			}
			CharacterBody body = ai.body;
			if (!((Object)(object)body != (Object)null))
			{
				return;
			}
			GameObject val = null;
			float num = float.MaxValue;
			foreach (GameObject implosion in Hooks.implosions)
			{
				float num2 = Vector3.Distance(implosion.transform.position, body.transform.position);
				if (num2 < Configuration.evasionDistance.Value && num2 < num)
				{
					num = num2;
					val = implosion;
				}
			}
			if ((Object)(object)ai.customTarget.gameObject != (Object)(object)val)
			{
				ai.customTarget.gameObject = val;
				ai.BeginSkillDriver(ai.EvaluateSkillDrivers());
			}
		}

		internal static void EnableAll()
		{
			foreach (GTFOHController instances in instancesList)
			{
				((Behaviour)instances).enabled = true;
			}
		}
	}
	internal class Hooks
	{
		[CompilerGenerated]
		private static class <>O
		{
			public static hook_Awake <0>__OnSpawnProjectile;

			public static hook_OnDestroy <1>__OnDestroyProjectile;

			public static Manipulator <2>__IgnoreVoidDeathForAllies;
		}

		internal static readonly HashSet<GameObject> implosions = new HashSet<GameObject>();

		private static readonly HashSet<int> implosionProjectiles = new HashSet<int>();

		private static readonly HashSet<MasterIndex> backupMasterIndices = new HashSet<MasterIndex>();

		private static readonly HashSet<MasterIndex> immuneMasterIndices = new HashSet<MasterIndex>();

		internal static void Apply()
		{
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Expected O, but got Unknown
			//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_003b: Expected O, but got Unknown
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0061: Unknown result type (might be due to invalid IL or missing references)
			//IL_0067: Expected O, but got Unknown
			object obj = <>O.<0>__OnSpawnProjectile;
			if (obj == null)
			{
				hook_Awake val = OnSpawnProjectile;
				<>O.<0>__OnSpawnProjectile = val;
				obj = (object)val;
			}
			ProjectileController.Awake += (hook_Awake)obj;
			object obj2 = <>O.<1>__OnDestroyProjectile;
			if (obj2 == null)
			{
				hook_OnDestroy val2 = OnDestroyProjectile;
				<>O.<1>__OnDestroyProjectile = val2;
				obj2 = (object)val2;
			}
			ProjectileController.OnDestroy += (hook_OnDestroy)obj2;
			if (Configuration.immuneToVoidDeath.Value)
			{
				object obj3 = <>O.<2>__IgnoreVoidDeathForAllies;
				if (obj3 == null)
				{
					Manipulator val3 = IgnoreVoidDeathForAllies;
					<>O.<2>__IgnoreVoidDeathForAllies = val3;
					obj3 = (object)val3;
				}
				HealthComponent.TakeDamage += (Manipulator)obj3;
			}
		}

		internal static void Undo()
		{
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Expected O, but got Unknown
			//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_003b: Expected O, but got Unknown
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0061: Unknown result type (might be due to invalid IL or missing references)
			//IL_0067: Expected O, but got Unknown
			object obj = <>O.<0>__OnSpawnProjectile;
			if (obj == null)
			{
				hook_Awake val = OnSpawnProjectile;
				<>O.<0>__OnSpawnProjectile = val;
				obj = (object)val;
			}
			ProjectileController.Awake -= (hook_Awake)obj;
			object obj2 = <>O.<1>__OnDestroyProjectile;
			if (obj2 == null)
			{
				hook_OnDestroy val2 = OnDestroyProjectile;
				<>O.<1>__OnDestroyProjectile = val2;
				obj2 = (object)val2;
			}
			ProjectileController.OnDestroy -= (hook_OnDestroy)obj2;
			if (Configuration.immuneToVoidDeath.Value)
			{
				object obj3 = <>O.<2>__IgnoreVoidDeathForAllies;
				if (obj3 == null)
				{
					Manipulator val3 = IgnoreVoidDeathForAllies;
					<>O.<2>__IgnoreVoidDeathForAllies = val3;
					obj3 = (object)val3;
				}
				HealthComponent.TakeDamage -= (Manipulator)obj3;
			}
		}

		private static void IgnoreVoidDeathForAllies(ILContext il)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Expected O, but got Unknown
			//IL_0078: Unknown result type (might be due to invalid IL or missing references)
			ILCursor val = new ILCursor(il);
			if (!val.TryGotoNext(new Func<Instruction, bool>[2]
			{
				(Instruction x) => ILPatternMatchingExt.MatchLdfld<CharacterBody>(x, "bodyFlags"),
				(Instruction x) => ILPatternMatchingExt.MatchLdcI4(x, 2048)
			}))
			{
				AlliesAvoidImplosions.Logger.LogError((object)"Failed to patch HealthComponent.TakeDamage");
				return;
			}
			val.Index += 1;
			val.Emit(OpCodes.Ldarg_0);
			val.EmitDelegate<Func<BodyFlags, HealthComponent, BodyFlags>>((Func<BodyFlags, HealthComponent, BodyFlags>)delegate(BodyFlags bodyFlags, HealthComponent healthComponent)
			{
				//IL_003b: Unknown result type (might be due to invalid IL or missing references)
				//IL_001a: Unknown result type (might be due to invalid IL or missing references)
				//IL_0032: Unknown result type (might be due to invalid IL or missing references)
				//IL_0038: Unknown result type (might be due to invalid IL or missing references)
				//IL_0039: Unknown result type (might be due to invalid IL or missing references)
				CharacterMaster master = healthComponent.body.master;
				if (Object.op_Implicit((Object)(object)master) && immuneMasterIndices.Contains(master.masterIndex) && Configuration.immuneToVoidDeath.Value)
				{
					bodyFlags = (BodyFlags)(bodyFlags | 0x800);
				}
				return bodyFlags;
			});
		}

		private static void OnSpawnProjectile(orig_Awake orig, ProjectileController self)
		{
			orig.Invoke(self);
			if (NetworkServer.active && implosionProjectiles.Contains(self.catalogIndex))
			{
				implosions.Add(((Component)self).gameObject);
				GTFOHController.EnableAll();
			}
		}

		private static void OnDestroyProjectile(orig_OnDestroy orig, ProjectileController self)
		{
			if (NetworkServer.active && implosions.Contains(((Component)self).gameObject))
			{
				implosions.Remove(((Component)self).gameObject);
			}
			orig.Invoke(self);
		}

		internal static void CollectCurrentProjectiles()
		{
			if (!NetworkServer.active)
			{
				return;
			}
			foreach (ProjectileController instances in InstanceTracker.GetInstancesList<ProjectileController>())
			{
				if (implosionProjectiles.Contains(instances.catalogIndex))
				{
					implosions.Add(((Component)instances).gameObject);
				}
			}
		}

		internal static void LoadDataAndPatch()
		{
			BuildImplosionProjectileList();
			BuildAllyIDs();
			PatchAllies();
		}

		private static void BuildImplosionProjectileList()
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: 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)
			DamageTypeCombo val = DamageTypeCombo.op_Implicit((DamageType)65536);
			ProjectileController[] projectilePrefabProjectileControllerComponents = ProjectileCatalog.projectilePrefabProjectileControllerComponents;
			ProjectileDamage val3 = default(ProjectileDamage);
			foreach (ProjectileController val2 in projectilePrefabProjectileControllerComponents)
			{
				if (((Component)val2).TryGetComponent<ProjectileDamage>(ref val3) && DamageTypeCombo.op_Implicit(val3.damageType & val) == DamageTypeCombo.op_Implicit(val))
				{
					implosionProjectiles.Add(val2.catalogIndex);
				}
			}
		}

		private static void BuildAllyIDs()
		{
			BuildMasterIndices(Configuration.additionalBackupEntries, Configuration.blacklistedBackupEntries, backupMasterIndices);
			BuildMasterIndices(Configuration.additionalImmuneEntries, Configuration.blacklistedImmuneEntries, immuneMasterIndices);
		}

		private static void BuildMasterIndices(ConfigEntry<string> additionalConfig, ConfigEntry<string> blacklistedConfig, HashSet<MasterIndex> indices)
		{
			//IL_00aa: Unknown result type (might be due to invalid IL or missing references)
			HashSet<string> hashSet = new HashSet<string>(SplitConfig(blacklistedConfig));
			HashSet<string> hashSet2 = new HashSet<string>();
			string[] defaultAllies = AlliesAvoidImplosions.defaultAllies;
			foreach (string item in defaultAllies)
			{
				if (!hashSet.Contains(item))
				{
					hashSet2.Add(item);
				}
			}
			foreach (string item2 in SplitConfig(additionalConfig))
			{
				if (!hashSet.Contains(item2))
				{
					hashSet2.Add(item2);
				}
			}
			foreach (string item3 in hashSet2)
			{
				GameObject val = MasterCatalog.FindMasterPrefab(item3);
				if ((Object)(object)val != (Object)null)
				{
					indices.Add(val.GetComponent<CharacterMaster>().masterIndex);
				}
			}
			static IEnumerable<string> SplitConfig(ConfigEntry<string> config)
			{
				return from x in config.Value.Split(',')
					select x.Trim();
			}
		}

		private static void PatchAllies()
		{
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_007c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0093: Unknown result type (might be due to invalid IL or missing references)
			//IL_009a: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a1: Unknown result type (might be due to invalid IL or missing references)
			CharacterMotor val = default(CharacterMotor);
			RigidbodyMotor val2 = default(RigidbodyMotor);
			foreach (MasterIndex backupMasterIndex in backupMasterIndices)
			{
				GameObject masterPrefab = MasterCatalog.GetMasterPrefab(backupMasterIndex);
				if (!((Object)(object)masterPrefab != (Object)null))
				{
					continue;
				}
				CharacterMaster component = masterPrefab.GetComponent<CharacterMaster>();
				if (!Object.op_Implicit((Object)(object)component.bodyPrefab) || (!component.bodyPrefab.TryGetComponent<CharacterMotor>(ref val) && !component.bodyPrefab.TryGetComponent<RigidbodyMotor>(ref val2)))
				{
					continue;
				}
				AISkillDriver[] components = masterPrefab.GetComponents<AISkillDriver>();
				AISkillDriver obj = masterPrefab.AddComponent<AISkillDriver>();
				obj.customName = "BackUpFromImplosion";
				obj.skillSlot = (SkillSlot)(-1);
				obj.maxDistance = Configuration.evasionDistance.Value;
				obj.moveTargetType = (TargetType)3;
				obj.aimType = (AimType)1;
				obj.movementType = (MovementType)3;
				obj.shouldSprint = true;
				obj.ignoreNodeGraph = true;
				obj.driverUpdateTimerOverride = 1f;
				AISkillDriver[] array = components;
				foreach (AISkillDriver val3 in array)
				{
					AISkillDriver obj2 = masterPrefab.AddComponent<AISkillDriver>();
					FieldInfo[] fields = ((object)val3).GetType().GetFields();
					foreach (FieldInfo fieldInfo in fields)
					{
						fieldInfo.SetValue(obj2, fieldInfo.GetValue(val3));
					}
					Object.DestroyImmediate((Object)(object)val3);
				}
				masterPrefab.AddComponent<GTFOHController>();
			}
		}
	}
}