Decompiled source of AerosSkillsTurretCompat v1.2.0

BepInEx/plugins/stimmchen-AerosSkillsTurretCompat/AerosSkillsTurretCompat.dll

Decompiled 3 weeks 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.Bootstrap;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using RoR2;
using UnityEngine;

[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("AerosSkillsTurretCompat")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("AerosSkillsTurretCompat")]
[assembly: AssemblyTitle("AerosSkillsTurretCompat")]
[assembly: AssemblyVersion("1.0.0.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.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

		public NullableAttribute(byte P_0)
		{
			NullableFlags = new byte[1] { P_0 };
		}

		public NullableAttribute(byte[] P_0)
		{
			NullableFlags = P_0;
		}
	}
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableContextAttribute : Attribute
	{
		public readonly byte Flag;

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
	[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 AerosSkillsTurretCompat
{
	[BepInPlugin("stimmchen.AerosSkillsTurretCompat", "AerosSkills Turret Compat", "1.2.0")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public class Plugin : BaseUnityPlugin
	{
		private readonly struct SpawnRecord
		{
			public readonly float Time;

			public readonly Vector3 Position;

			public SpawnRecord(float time, Vector3 position)
			{
				//IL_0008: Unknown result type (might be due to invalid IL or missing references)
				//IL_0009: Unknown result type (might be due to invalid IL or missing references)
				Time = time;
				Position = position;
			}
		}

		[HarmonyPatch(typeof(CharacterMaster), "AddDeployable")]
		private static class CharacterMasterAddDeployablePatch
		{
			private static void Postfix(CharacterMaster __instance, Deployable deployable, DeployableSlot slot)
			{
				//IL_001d: Unknown result type (might be due to invalid IL or missing references)
				//IL_001e: Unknown result type (might be due to invalid IL or missing references)
				if (!((Object)(object)__instance == (Object)null) && !((Object)(object)deployable == (Object)null) && TryGetAerosBigTurretSlot(out var slot2) && slot == slot2 && !TrySuppressLikelyDuplicateSpawn(__instance, deployable))
				{
					TryAttachSyncedComponent(__instance, deployable);
					TryApplyInfusionInheritance(__instance, deployable);
				}
			}
		}

		public const string PluginGuid = "stimmchen.AerosSkillsTurretCompat";

		public const string PluginName = "AerosSkills Turret Compat";

		public const string PluginVersion = "1.2.0";

		private const string AerosGuid = "com.Dragonyck.AerosSkills";

		private const string SyncedGuid = "com.WPhantom.SyncedTurrets";

		private const string InfusedGuid = "Chinchi.InfusedTurrets";

		private const string TurretDeathNoteGuid = "Watch_Me_Be_Meh.TurretDeathNote";

		private static Plugin? _instance;

		private static FieldInfo? _aerosBigTurretField;

		private static Type? _syncedTurretsType;

		private static FieldInfo? _syncedCharacterMasterField;

		private static MethodInfo? _syncedInitMethod;

		private static bool _hasSynced;

		private static bool _hasInfused;

		private static bool _hasTurretDeathNote;

		private static MethodInfo? _tdnDeathHandlerMethod;

		private const float DuplicateSpawnWindowSeconds = 0.3f;

		private const float DuplicateSpawnDistanceMeters = 1f;

		private static readonly Dictionary<int, SpawnRecord> LastAerosSpawnByOwner = new Dictionary<int, SpawnRecord>();

		private void Awake()
		{
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Expected O, but got Unknown
			//IL_012d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0133: Expected O, but got Unknown
			_instance = this;
			Harmony val = new Harmony("stimmchen.AerosSkillsTurretCompat");
			_hasSynced = Chainloader.PluginInfos.ContainsKey("com.WPhantom.SyncedTurrets");
			_hasInfused = Chainloader.PluginInfos.ContainsKey("Chinchi.InfusedTurrets");
			_hasTurretDeathNote = Chainloader.PluginInfos.ContainsKey("Watch_Me_Be_Meh.TurretDeathNote");
			Type type = AccessTools.TypeByName("AerosSkills.Prefabs");
			_aerosBigTurretField = (((object)type != null) ? AccessTools.Field(type, "bigTurret") : null);
			if (_hasSynced)
			{
				_syncedTurretsType = AccessTools.TypeByName("SyncedTurrets.SyncTurrets");
				_syncedCharacterMasterField = ((_syncedTurretsType != null) ? AccessTools.Field(_syncedTurretsType, "characterMaster") : null);
				_syncedInitMethod = ((_syncedTurretsType != null) ? AccessTools.Method(_syncedTurretsType, "Init", (Type[])null, (Type[])null) : null);
			}
			if (_hasTurretDeathNote)
			{
				Type type2 = AccessTools.TypeByName("TurretDeathNote.TurretDeathNote");
				Type type3 = AccessTools.TypeByName("TurretDeathNote.TurretDeathNote+<>c");
				if (type2 != null && type3 != null)
				{
					_tdnDeathHandlerMethod = AccessTools.Method(type3, "<Awake>b__6_0", (Type[])null, (Type[])null);
					if (_tdnDeathHandlerMethod != null)
					{
						HarmonyMethod val2 = new HarmonyMethod(typeof(Plugin), "TurretDeathNoteDeathHandlerFinalizer", (Type[])null);
						val.Patch((MethodBase)_tdnDeathHandlerMethod, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, val2, (HarmonyMethod)null);
					}
				}
				else
				{
					((BaseUnityPlugin)this).Logger.LogWarning((object)"TurretDeathNote detected but type lookup failed.");
				}
				GlobalEventManager.onCharacterDeathGlobal += OnCharacterDeathGlobalCompat;
			}
			val.PatchAll();
			((BaseUnityPlugin)this).Logger.LogInfo((object)$"Loaded. Aeros slot hook active. Synced_Turrets present={_hasSynced}, InfusedTurrets present={_hasInfused}, TurretDeathNote present={_hasTurretDeathNote}");
		}

		private void OnDestroy()
		{
			if (_hasTurretDeathNote)
			{
				GlobalEventManager.onCharacterDeathGlobal -= OnCharacterDeathGlobalCompat;
			}
		}

		private static bool TryGetAerosBigTurretSlot(out DeployableSlot slot)
		{
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Expected I4, but got Unknown
			slot = (DeployableSlot)0;
			if (_aerosBigTurretField == null)
			{
				return false;
			}
			try
			{
				object value = _aerosBigTurretField.GetValue(null);
				if (value is DeployableSlot)
				{
					DeployableSlot val = (DeployableSlot)value;
					slot = (DeployableSlot)(int)val;
					return true;
				}
			}
			catch (Exception arg)
			{
				Plugin? instance = _instance;
				if (instance != null)
				{
					((BaseUnityPlugin)instance).Logger.LogError((object)$"Failed reading AerosSkills bigTurret slot: {arg}");
				}
			}
			return false;
		}

		private static void TryAttachSyncedComponent(CharacterMaster ownerMaster, Deployable deployable)
		{
			if (!_hasSynced || _syncedTurretsType == null)
			{
				return;
			}
			GameObject gameObject = ((Component)deployable).gameObject;
			if ((Object)(object)gameObject == (Object)null)
			{
				return;
			}
			try
			{
				if ((Object)(object)gameObject.GetComponent(_syncedTurretsType) != (Object)null)
				{
					return;
				}
				Component val = gameObject.AddComponent(_syncedTurretsType);
				if (!((Object)(object)val == (Object)null))
				{
					_syncedCharacterMasterField?.SetValue(val, ownerMaster);
					_syncedInitMethod?.Invoke(val, null);
					Plugin? instance = _instance;
					if (instance != null)
					{
						((BaseUnityPlugin)instance).Logger.LogInfo((object)("Synced_Turrets compat attached to " + ((Object)((Component)deployable).gameObject).name + " for owner " + ((Object)ownerMaster).name + "."));
					}
				}
			}
			catch (Exception arg)
			{
				Plugin? instance2 = _instance;
				if (instance2 != null)
				{
					((BaseUnityPlugin)instance2).Logger.LogError((object)$"Failed attaching Synced_Turrets component: {arg}");
				}
			}
		}

		private static void TryApplyInfusionInheritance(CharacterMaster ownerMaster, Deployable deployable)
		{
			if (!_hasInfused)
			{
				return;
			}
			try
			{
				CharacterMaster component = ((Component)deployable).GetComponent<CharacterMaster>();
				Inventory val = ((component != null) ? component.inventory : null);
				Inventory inventory = ownerMaster.inventory;
				if ((Object)(object)val == (Object)null || (Object)(object)inventory == (Object)null)
				{
					return;
				}
				uint infusionBonus = inventory.infusionBonus;
				if (infusionBonus != 0)
				{
					val.AddInfusionBonus(infusionBonus);
					Plugin? instance = _instance;
					if (instance != null)
					{
						((BaseUnityPlugin)instance).Logger.LogInfo((object)$"InfusedTurrets compat copied infusionBonus={infusionBonus} to {((Object)((Component)deployable).gameObject).name}.");
					}
				}
			}
			catch (Exception arg)
			{
				Plugin? instance2 = _instance;
				if (instance2 != null)
				{
					((BaseUnityPlugin)instance2).Logger.LogError((object)$"Failed applying InfusedTurrets compatibility: {arg}");
				}
			}
		}

		private static Exception? TurretDeathNoteDeathHandlerFinalizer(Exception __exception)
		{
			if (__exception is MissingFieldException)
			{
				Plugin? instance = _instance;
				if (instance != null)
				{
					((BaseUnityPlugin)instance).Logger.LogWarning((object)"Suppressed TurretDeathNote MissingFieldException on this RoR2 build.");
				}
				return null;
			}
			return __exception;
		}

		private static bool TrySuppressLikelyDuplicateSpawn(CharacterMaster ownerMaster, Deployable deployable)
		{
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bb: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a2: Unknown result type (might be due to invalid IL or missing references)
			int instanceID = ((Object)ownerMaster).GetInstanceID();
			float unscaledTime = Time.unscaledTime;
			Vector3 position = ((Component)deployable).transform.position;
			if (LastAerosSpawnByOwner.TryGetValue(instanceID, out var value))
			{
				bool num = unscaledTime - value.Time <= 0.3f;
				bool flag = Vector3.Distance(position, value.Position) <= 1f;
				if (num && flag)
				{
					ownerMaster.RemoveDeployable(deployable);
					CharacterMaster component = ((Component)deployable).GetComponent<CharacterMaster>();
					if ((Object)(object)component != (Object)null)
					{
						component.TrueKill();
					}
					else
					{
						Object.Destroy((Object)(object)((Component)deployable).gameObject);
					}
					Plugin? instance = _instance;
					if (instance != null)
					{
						((BaseUnityPlugin)instance).Logger.LogWarning((object)$"Suppressed duplicate Aeros turret spawn for owner {((Object)ownerMaster).name} at {position}.");
					}
					return true;
				}
			}
			LastAerosSpawnByOwner[instanceID] = new SpawnRecord(unscaledTime, position);
			return false;
		}

		private static void OnCharacterDeathGlobalCompat(DamageReport report)
		{
			if (!_hasTurretDeathNote || (Object)(object)report?.victimMaster == (Object)null || !IsAerosTurretVictim(report.victimMaster))
			{
				return;
			}
			try
			{
				string attackerName = GetAttackerName(report);
				if (attackerName != "???")
				{
					Chat.AddMessage("Engineer's turret was killed by: " + attackerName);
				}
				else
				{
					Chat.AddMessage("damageReport.damageInfo.inflictor: " + (object)report.damageInfo.inflictor);
				}
			}
			catch (Exception arg)
			{
				Plugin? instance = _instance;
				if (instance != null)
				{
					((BaseUnityPlugin)instance).Logger.LogError((object)$"Failed TurretDeathNote compatibility handler: {arg}");
				}
			}
		}

		private static bool IsAerosTurretVictim(CharacterMaster victimMaster)
		{
			string text = (Object.op_Implicit((Object)(object)victimMaster.bodyPrefab) ? ((Object)victimMaster.bodyPrefab).name : null);
			if (!string.IsNullOrEmpty(text) && text.Contains("AerosBigTurretBody", StringComparison.Ordinal))
			{
				return true;
			}
			string text2 = (Object.op_Implicit((Object)(object)((Component)victimMaster).gameObject) ? ((Object)((Component)victimMaster).gameObject).name : null);
			if (!string.IsNullOrEmpty(text2) && text2.Contains("AerosBigTurretMaster", StringComparison.Ordinal))
			{
				return true;
			}
			return false;
		}

		private static string GetAttackerName(DamageReport report)
		{
			if ((Object)(object)report.attackerMaster != (Object)null)
			{
				PlayerCharacterMasterController playerCharacterMasterController = report.attackerMaster.playerCharacterMasterController;
				if ((Object)(object)playerCharacterMasterController != (Object)null && (Object)(object)playerCharacterMasterController.networkUser != (Object)null)
				{
					return playerCharacterMasterController.networkUser.userName;
				}
				if ((Object)(object)report.attackerBody != (Object)null)
				{
					return Util.GetBestBodyName(((Component)report.attackerBody).gameObject);
				}
			}
			if ((Object)(object)report.attacker != (Object)null)
			{
				return Util.GetBestBodyName(report.attacker);
			}
			return "???";
		}
	}
}