Decompiled source of SummonFocusFire Stand A Lone v0.0.1

SummonFocusFire.dll

Decompiled a day ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using EntityStates;
using Microsoft.CodeAnalysis;
using On.RoR2;
using RoR2;
using RoR2.CharacterAI;
using UnityEngine;
using UnityEngine.Networking;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("SummonFocusFire")]
[assembly: AssemblyConfiguration("Debug")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("SummonFocusFire")]
[assembly: AssemblyTitle("SummonFocusFire")]
[assembly: AssemblyVersion("1.0.0.0")]
[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 SummonFocusFire
{
	[BepInPlugin("com.chronicrick.summonfocusfire", "SummonFocusFire - Stand-A-Lone", "0.0.1")]
	public sealed class Plugin : BaseUnityPlugin
	{
		private sealed class Order
		{
			public CharacterMaster owner;

			public GameObject target;

			public float expires;
		}

		private sealed class PrimaryWindow
		{
			public int lastStock;

			public float expires;

			public Type activationType;
		}

		private ConfigEntry<bool> enabled;

		private ConfigEntry<bool> includeMobile;

		private ConfigEntry<bool> includeStationary;

		private ConfigEntry<float> mobileRadius;

		private ConfigEntry<float> stationaryRadius;

		private ConfigEntry<float> focusSeconds;

		private ConfigEntry<float> commandCooldown;

		private readonly Dictionary<CharacterMaster, Order> orders = new Dictionary<CharacterMaster, Order>();

		private readonly Dictionary<CharacterMaster, float> lastCommand = new Dictionary<CharacterMaster, float>();

		private readonly Dictionary<CharacterMaster, PrimaryWindow> primaryWindows = new Dictionary<CharacterMaster, PrimaryWindow>();

		private float nextScan;

		private void Awake()
		{
			//IL_00ff: Unknown result type (might be due to invalid IL or missing references)
			//IL_0109: Expected O, but got Unknown
			enabled = ((BaseUnityPlugin)this).Config.Bind<bool>("FocusFire", "Enabled", true, "Server-authoritative player-owned ally focus fire.");
			includeMobile = ((BaseUnityPlugin)this).Config.Bind<bool>("FocusFire", "IncludeMobileAllies", true, "Allow mobile AI allies to pursue designated targets.");
			includeStationary = ((BaseUnityPlugin)this).Config.Bind<bool>("FocusFire", "IncludeStationaryAllies", true, "Only command stationary AI allies if target is already in attack range.");
			mobileRadius = ((BaseUnityPlugin)this).Config.Bind<float>("FocusFire", "MobilePursuitRadiusMeters", 150f, "Maximum distance between a mobile ally and the designated target.");
			stationaryRadius = ((BaseUnityPlugin)this).Config.Bind<float>("FocusFire", "StationaryAttackRangeMeters", 45f, "Conservative range gate for stationary AI; does not change weapon range.");
			focusSeconds = ((BaseUnityPlugin)this).Config.Bind<float>("FocusFire", "FocusDurationSeconds", 8f, "Duration of a focus-fire order after a qualifying hit.");
			commandCooldown = ((BaseUnityPlugin)this).Config.Bind<float>("FocusFire", "CommandCooldownSeconds", 0.15f, "Rate-limit commands from rapid-fire primaries.");
			HealthComponent.TakeDamage += new hook_TakeDamage(OnDamage);
			((BaseUnityPlugin)this).Logger.LogInfo((object)"Summon Focus Fire - Stand-A-Lone 0.0.1 loaded (experimental primary-hit classifier).");
		}

		private void OnDestroy()
		{
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Expected O, but got Unknown
			HealthComponent.TakeDamage -= new hook_TakeDamage(OnDamage);
		}

		private void FixedUpdate()
		{
			if (!NetworkServer.active || !enabled.Value || Time.fixedTime < nextScan)
			{
				return;
			}
			nextScan = Time.fixedTime + 0.05f;
			foreach (CharacterMaster readOnlyInstances in CharacterMaster.readOnlyInstancesList)
			{
				if (!Object.op_Implicit((Object)(object)readOnlyInstances) || !Object.op_Implicit((Object)(object)((Component)readOnlyInstances).GetComponent<PlayerCharacterMasterController>()))
				{
					continue;
				}
				CharacterBody body = readOnlyInstances.GetBody();
				if (Object.op_Implicit((Object)(object)body) && Object.op_Implicit((Object)(object)body.skillLocator) && Object.op_Implicit((Object)(object)body.skillLocator.primary))
				{
					GenericSkill primary = body.skillLocator.primary;
					if (!primaryWindows.TryGetValue(readOnlyInstances, out var value))
					{
						value = new PrimaryWindow
						{
							lastStock = primary.stock
						};
						primaryWindows[readOnlyInstances] = value;
					}
					if (primary.stock < value.lastStock)
					{
						value.expires = Time.fixedTime + 0.35f;
					}
					value.lastStock = primary.stock;
					if ((Object)(object)primary.skillDef != (Object)null && ((SerializableEntityStateType)(ref primary.skillDef.activationState)).stateType != null)
					{
						value.activationType = ((SerializableEntityStateType)(ref primary.skillDef.activationState)).stateType;
					}
				}
			}
			List<CharacterMaster> list = new List<CharacterMaster>();
			foreach (KeyValuePair<CharacterMaster, Order> order in orders)
			{
				if (!Object.op_Implicit((Object)(object)order.Key) || !Object.op_Implicit((Object)(object)order.Value.owner) || !Object.op_Implicit((Object)(object)order.Value.target) || Time.fixedTime > order.Value.expires)
				{
					list.Add(order.Key);
				}
				else
				{
					ApplyTarget(order.Key, order.Value.target);
				}
			}
			foreach (CharacterMaster item in list)
			{
				orders.Remove(item);
			}
		}

		private void OnDamage(orig_TakeDamage orig, HealthComponent victim, DamageInfo info)
		{
			//IL_00d5: 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_027c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0287: Unknown result type (might be due to invalid IL or missing references)
			//IL_028c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0291: Unknown result type (might be due to invalid IL or missing references)
			orig.Invoke(victim, info);
			if (!NetworkServer.active || !enabled.Value || !Object.op_Implicit((Object)(object)victim) || !Object.op_Implicit((Object)(object)victim.body) || info == null || !Object.op_Implicit((Object)(object)info.attacker))
			{
				return;
			}
			CharacterBody val = info.attacker.GetComponent<CharacterBody>() ?? info.attacker.GetComponentInParent<CharacterBody>();
			if (!Object.op_Implicit((Object)(object)val) || !Object.op_Implicit((Object)(object)val.master) || !Object.op_Implicit((Object)(object)((Component)val.master).GetComponent<PlayerCharacterMasterController>()) || !victim.alive || (Object)(object)victim.body.teamComponent == (Object)null || (Object)(object)val.teamComponent == (Object)null || victim.body.teamComponent.teamIndex == val.teamComponent.teamIndex || !IsLikelyDirectPrimaryHit(val, info) || (lastCommand.TryGetValue(val.master, out var value) && Time.fixedTime - value < commandCooldown.Value))
			{
				return;
			}
			lastCommand[val.master] = Time.fixedTime;
			int num = 0;
			foreach (CharacterMaster readOnlyInstances in CharacterMaster.readOnlyInstancesList)
			{
				if (!Object.op_Implicit((Object)(object)readOnlyInstances) || (Object)(object)readOnlyInstances == (Object)(object)val.master || !Object.op_Implicit((Object)(object)readOnlyInstances.minionOwnership) || (Object)(object)readOnlyInstances.minionOwnership.ownerMaster != (Object)(object)val.master)
				{
					continue;
				}
				CharacterBody body = readOnlyInstances.GetBody();
				if (!Object.op_Implicit((Object)(object)body) || !Object.op_Implicit((Object)(object)body.healthComponent) || !body.healthComponent.alive)
				{
					continue;
				}
				BaseAI component = ((Component)readOnlyInstances).GetComponent<BaseAI>();
				if (!Object.op_Implicit((Object)(object)component))
				{
					continue;
				}
				bool flag = IsStationary(readOnlyInstances, body);
				if ((!flag || includeStationary.Value) && (flag || includeMobile.Value))
				{
					float num2 = (flag ? stationaryRadius.Value : mobileRadius.Value);
					Vector3 val2 = body.corePosition - victim.body.corePosition;
					if (!(((Vector3)(ref val2)).sqrMagnitude > num2 * num2))
					{
						orders[readOnlyInstances] = new Order
						{
							owner = val.master,
							target = ((Component)victim.body).gameObject,
							expires = Time.fixedTime + focusSeconds.Value
						};
						ApplyTarget(readOnlyInstances, ((Component)victim.body).gameObject);
						num++;
					}
				}
			}
			if (num > 0)
			{
				((BaseUnityPlugin)this).Logger.LogDebug((object)("[FOCUS] " + ((Object)val).name + " directed " + num + " owned AI allies."));
			}
		}

		private bool IsLikelyDirectPrimaryHit(CharacterBody attacker, DamageInfo info)
		{
			if (!primaryWindows.TryGetValue(attacker.master, out var value))
			{
				return false;
			}
			bool flag = Time.fixedTime <= value.expires;
			bool flag2 = false;
			if (value.activationType != null)
			{
				EntityStateMachine[] components = ((Component)attacker).GetComponents<EntityStateMachine>();
				foreach (EntityStateMachine val in components)
				{
					if ((Object)(object)val != (Object)null && val.state != null && value.activationType.IsAssignableFrom(((object)val.state).GetType()))
					{
						flag2 = true;
					}
				}
			}
			if (!flag && !flag2)
			{
				return false;
			}
			return (Object)(object)info.inflictor == (Object)null || (Object)(object)info.inflictor == (Object)(object)((Component)attacker).gameObject;
		}

		private static bool IsStationary(CharacterMaster master, CharacterBody body)
		{
			string text = (((Object)master).name + " " + ((Object)body).name).ToLowerInvariant();
			if (text.Contains("turret") || text.Contains("polyp") || text.Contains("offshoot") || text.Contains("flower"))
			{
				return true;
			}
			CharacterMotor characterMotor = body.characterMotor;
			return (Object)(object)characterMotor == (Object)null && (Object)(object)((Component)body).GetComponent<Rigidbody>() == (Object)null;
		}

		private void ApplyTarget(CharacterMaster master, GameObject target)
		{
			if (!Object.op_Implicit((Object)(object)master) || !Object.op_Implicit((Object)(object)target))
			{
				return;
			}
			BaseAI component = ((Component)master).GetComponent<BaseAI>();
			if (!Object.op_Implicit((Object)(object)component))
			{
				return;
			}
			try
			{
				object currentEnemy = component.currentEnemy;
				if (currentEnemy != null)
				{
					MethodInfo method = currentEnemy.GetType().GetMethod("SetTarget", BindingFlags.Instance | BindingFlags.Public, null, new Type[1] { typeof(GameObject) }, null);
					if (method != null)
					{
						method.Invoke(currentEnemy, new object[1] { target });
					}
				}
			}
			catch (Exception ex)
			{
				((BaseUnityPlugin)this).Logger.LogDebug((object)("[FOCUS] AI target assignment unavailable: " + ex.Message));
			}
		}
	}
}