Decompiled source of VenganceSwarmRedux v1.0.0

VenganceSwarmRedux.dll

Decompiled 13 hours ago
using System;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using Microsoft.CodeAnalysis;
using On.RoR2;
using On.RoR2.Artifacts;
using RiskOfOptions;
using RiskOfOptions.Options;
using RoR2;
using RoR2.Artifacts;
using RoR2.ContentManagement;
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("VenganceSwarmRedux")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+5386fc89406137ecc94121205261f577ee3b131d")]
[assembly: AssemblyProduct("VenganceSwarmRedux")]
[assembly: AssemblyTitle("VenganceSwarmRedux")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[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 VengeanceSwarmRedux
{
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInPlugin("com.FortressForce.VengeanceSwarmRedux", "VengeanceSwarmRedux", "1.0.0")]
	public class Plugin : BaseUnityPlugin
	{
		public const string PluginGUID = "com.FortressForce.VengeanceSwarmRedux";

		public const string PluginName = "VengeanceSwarmRedux";

		public const string PluginVersion = "1.0.0";

		private static readonly FieldInfo FieldSrcItemStacks = typeof(MasterCopySpawnCard).GetField("srcItemStacks", BindingFlags.Instance | BindingFlags.NonPublic);

		private static readonly FieldInfo FieldSrcTempItemRawValues = typeof(MasterCopySpawnCard).GetField("srcTempItemRawValues", BindingFlags.Instance | BindingFlags.NonPublic);

		private static readonly FieldInfo FieldSrcEquipment = typeof(MasterCopySpawnCard).GetField("srcEquipment", BindingFlags.Instance | BindingFlags.NonPublic);

		private static readonly FieldInfo FieldOnPreSpawnSetup = typeof(MasterCopySpawnCard).GetField("onPreSpawnSetup", BindingFlags.Instance | BindingFlags.NonPublic);

		private static int _lastInvasionCycleDropped = -1;

		public static ManualLogSource InstanceLogger { get; private set; } = null;

		public static Plugin Instance { get; private set; } = null;

		public static ConfigEntry<bool> IsEnabled { get; private set; } = null;

		public static ConfigEntry<bool> AllowBothDrops { get; private set; } = null;

		public static ConfigEntry<bool> EnableHighPopFix { get; private set; } = null;

		public static ConfigEntry<bool> EnableDebugLogging { get; private set; } = null;

		public void Awake()
		{
			Instance = this;
			InstanceLogger = ((BaseUnityPlugin)this).Logger;
			InitConfig();
			RiskOfOptionsIntegration.Init(IsEnabled, AllowBothDrops, EnableHighPopFix, EnableDebugLogging);
			RegisterHooks();
			InstanceLogger.LogInfo((object)"VengeanceSwarmRedux v1.0.0 initialized successfully.");
		}

		private void InitConfig()
		{
			IsEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("1. General", "Enabled", true, "Enables or disables the mod entirely.");
			AllowBothDrops = ((BaseUnityPlugin)this).Config.Bind<bool>("2. Gameplay", "Allow Both Swarm Drops", true, "If true, both Doppelgangers spawned under Artifact of Swarms will drop an item when defeated. If false, only one drop is awarded per invasion wave.");
			EnableHighPopFix = ((BaseUnityPlugin)this).Config.Bind<bool>("2. Gameplay", "High Population Fix", true, "Ensures Swarms Doppelganger clones ignore team member limits so they always spawn even when the map has high enemy population.");
			EnableDebugLogging = ((BaseUnityPlugin)this).Config.Bind<bool>("3. Debug", "Enable Debug Logging", false, "Enables detailed console logging for spawn events, item copies, and skin applications.");
		}

		private void RegisterHooks()
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Expected O, but got Unknown
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Expected O, but got Unknown
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Expected O, but got Unknown
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: Expected O, but got Unknown
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0055: Expected O, but got Unknown
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0066: Expected O, but got Unknown
			//IL_006d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0077: Expected O, but got Unknown
			MasterCopySpawnCard.GetPreSpawnSetupCallback += new hook_GetPreSpawnSetupCallback(OnMasterCopySpawnCardGetPreSpawnSetupCallback);
			SwarmsArtifactManager.OnSpawnCardOnSpawnedServerGlobal += new hook_OnSpawnCardOnSpawnedServerGlobal(OnSwarmsArtifactManagerOnSpawnCardOnSpawnedServerGlobal);
			CharacterBody.SetLoadoutServer += new hook_SetLoadoutServer(OnCharacterBodySetLoadoutServer);
			CharacterBody.UpdateMasterLink += new hook_UpdateMasterLink(OnCharacterBodyUpdateMasterLink);
			ModelSkinController.Start += new hook_Start(OnModelSkinControllerStart);
			CharacterMaster.OnDeserialize += new hook_OnDeserialize(OnCharacterMasterOnDeserialize);
			DoppelgangerInvasionManager.OnCharacterDeathGlobal += new hook_OnCharacterDeathGlobal(OnDoppelgangerDeathGlobal);
		}

		private Action<CharacterMaster> OnMasterCopySpawnCardGetPreSpawnSetupCallback(orig_GetPreSpawnSetupCallback orig, MasterCopySpawnCard self)
		{
			if (!IsEnabled.Value)
			{
				return orig.Invoke(self);
			}
			return delegate(CharacterMaster spawnedMaster)
			{
				if (!Object.op_Implicit((Object)(object)spawnedMaster) || !Object.op_Implicit((Object)(object)spawnedMaster.inventory))
				{
					return;
				}
				try
				{
					if (FieldSrcItemStacks?.GetValue(self) is int[] array)
					{
						spawnedMaster.inventory.AddItemsFrom(array, (Func<ItemIndex, bool>)((ItemIndex _) => true));
					}
					if (FieldSrcTempItemRawValues?.GetValue(self) is float[] array2)
					{
						for (int num = 0; num < array2.Length; num++)
						{
							if (array2[num] > 0f)
							{
								spawnedMaster.inventory.GiveItemTemp((ItemIndex)num, array2[num]);
							}
						}
					}
					if (FieldSrcEquipment?.GetValue(self) is EquipmentIndex[][] array3)
					{
						for (uint num2 = 0u; num2 < array3.Length; num2++)
						{
							if (array3[num2] != null)
							{
								for (uint num3 = 0u; num3 < array3[num2].Length; num3++)
								{
									spawnedMaster.inventory.SetEquipmentIndexForSlot(array3[num2][num3], num2, num3);
								}
							}
						}
					}
					if (self is DoppelgangerSpawnCard && spawnedMaster.inventory.GetItemCountEffective(Items.InvadingDoppelganger) == 0)
					{
						spawnedMaster.inventory.GiveItemPermanent(Items.InvadingDoppelganger, 1);
					}
					(FieldOnPreSpawnSetup?.GetValue(self) as Action<CharacterMaster>)?.Invoke(spawnedMaster);
					if (EnableDebugLogging.Value)
					{
						InstanceLogger.LogInfo((object)("[ItemSync] Successfully applied inventory snapshot to spawned master: " + ((Object)spawnedMaster).name));
					}
				}
				catch (Exception arg)
				{
					InstanceLogger.LogError((object)$"[ItemSync] Error in pre-spawn setup callback: {arg}");
				}
			};
		}

		private void OnSwarmsArtifactManagerOnSpawnCardOnSpawnedServerGlobal(orig_OnSpawnCardOnSpawnedServerGlobal orig, SpawnResult result)
		{
			//IL_009b: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_008e: 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_0042: Unknown result type (might be due to invalid IL or missing references)
			if (IsEnabled.Value && EnableHighPopFix.Value && result.spawnRequest != null)
			{
				if (result.spawnRequest.spawnCard is DoppelgangerSpawnCard)
				{
					goto IL_008e;
				}
				if (Object.op_Implicit((Object)(object)result.spawnedInstance))
				{
					CharacterMaster component = result.spawnedInstance.GetComponent<CharacterMaster>();
					if (component != null)
					{
						Inventory inventory = component.inventory;
						if (((inventory != null) ? new int?(inventory.GetItemCountEffective(Items.InvadingDoppelganger)) : ((int?)null)) > 0)
						{
							goto IL_008e;
						}
					}
				}
			}
			goto IL_009a;
			IL_009a:
			orig.Invoke(result);
			return;
			IL_008e:
			result.spawnRequest.ignoreTeamMemberLimit = true;
			goto IL_009a;
		}

		private void OnCharacterBodySetLoadoutServer(orig_SetLoadoutServer orig, CharacterBody self, Loadout loadout)
		{
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			orig.Invoke(self, loadout);
			if (!IsEnabled.Value || !Object.op_Implicit((Object)(object)self) || loadout == null)
			{
				return;
			}
			try
			{
				uint num = (self.skinIndex = loadout.bodyLoadoutManager.GetSkinIndex(self.bodyIndex));
				if (EnableDebugLogging.Value)
				{
					InstanceLogger.LogInfo((object)$"[SkinSync] SetLoadoutServer: Body {((Object)self).name} assigned skinIndex {num}");
				}
			}
			catch (Exception arg)
			{
				InstanceLogger.LogError((object)$"[SkinSync] Error in SetLoadoutServer hook: {arg}");
			}
		}

		private void OnCharacterBodyUpdateMasterLink(orig_UpdateMasterLink orig, CharacterBody self)
		{
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			orig.Invoke(self);
			if (!IsEnabled.Value || !Object.op_Implicit((Object)(object)self) || !Object.op_Implicit((Object)(object)self.master) || self.master.loadout == null)
			{
				return;
			}
			try
			{
				uint skinIndex = self.master.loadout.bodyLoadoutManager.GetSkinIndex(self.bodyIndex);
				ApplySkinToBody(self, skinIndex);
			}
			catch (Exception arg)
			{
				InstanceLogger.LogError((object)$"[SkinSync] Error in UpdateMasterLink hook: {arg}");
			}
		}

		private void OnModelSkinControllerStart(orig_Start orig, ModelSkinController self)
		{
			//IL_0075: Unknown result type (might be due to invalid IL or missing references)
			if (IsEnabled.Value && Object.op_Implicit((Object)(object)self))
			{
				try
				{
					CharacterModel component = ((Component)self).GetComponent<CharacterModel>();
					if (Object.op_Implicit((Object)(object)component) && Object.op_Implicit((Object)(object)component.body) && Object.op_Implicit((Object)(object)component.body.master) && component.body.master.loadout != null)
					{
						uint skinIndex = component.body.master.loadout.bodyLoadoutManager.GetSkinIndex(component.body.bodyIndex);
						if (skinIndex != 0)
						{
							component.body.skinIndex = skinIndex;
						}
					}
				}
				catch (Exception arg)
				{
					InstanceLogger.LogError((object)$"[SkinSync] Error in ModelSkinController.Start hook: {arg}");
				}
			}
			orig.Invoke(self);
		}

		private void OnCharacterMasterOnDeserialize(orig_OnDeserialize orig, CharacterMaster self, NetworkReader reader, bool initialState)
		{
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			orig.Invoke(self, reader, initialState);
			if (!IsEnabled.Value || !Object.op_Implicit((Object)(object)self) || NetworkServer.active || self.loadout == null)
			{
				return;
			}
			try
			{
				CharacterBody body = self.GetBody();
				if (Object.op_Implicit((Object)(object)body))
				{
					uint skinIndex = self.loadout.bodyLoadoutManager.GetSkinIndex(body.bodyIndex);
					ApplySkinToBody(body, skinIndex);
				}
			}
			catch (Exception arg)
			{
				InstanceLogger.LogError((object)$"[SkinSync] Error in CharacterMaster.OnDeserialize hook: {arg}");
			}
		}

		private static void ApplySkinToBody(CharacterBody body, uint skinIndex)
		{
			if (!Object.op_Implicit((Object)(object)body))
			{
				return;
			}
			body.skinIndex = skinIndex;
			if (!Object.op_Implicit((Object)(object)body.modelLocator) || !Object.op_Implicit((Object)(object)body.modelLocator.modelTransform))
			{
				return;
			}
			ModelSkinController component = ((Component)body.modelLocator.modelTransform).GetComponent<ModelSkinController>();
			if (Object.op_Implicit((Object)(object)component) && component.skins != null && skinIndex < (uint)component.skins.Length && component.currentSkinIndex != (int)skinIndex)
			{
				if (EnableDebugLogging.Value)
				{
					InstanceLogger.LogInfo((object)$"[SkinSync] Applying skinIndex {skinIndex} to ModelSkinController on body {((Object)body).name}");
				}
				((MonoBehaviour)component).StartCoroutine(component.ApplySkinAsync((int)skinIndex, (AsyncReferenceHandleUnloadType)2));
			}
		}

		private void OnDoppelgangerDeathGlobal(orig_OnCharacterDeathGlobal orig, DoppelgangerInvasionManager self, DamageReport damageReport)
		{
			if (!IsEnabled.Value || AllowBothDrops.Value || !Object.op_Implicit((Object)(object)self))
			{
				orig.Invoke(self, damageReport);
				return;
			}
			int num = -1;
			try
			{
				MethodInfo method = typeof(DoppelgangerInvasionManager).GetMethod("GetCurrentInvasionCycle", BindingFlags.Instance | BindingFlags.NonPublic);
				if (method != null)
				{
					num = (int)method.Invoke(self, null);
				}
			}
			catch
			{
				num = (Object.op_Implicit((Object)(object)Run.instance) ? Mathf.FloorToInt(Run.instance.GetRunStopwatch() / 600f) : (-1));
			}
			if (_lastInvasionCycleDropped == num && num != -1)
			{
				if (EnableDebugLogging.Value)
				{
					InstanceLogger.LogInfo((object)$"[LootDrop] Suppressing second swarm doppelganger drop for cycle {num} (AllowBothDrops is disabled).");
				}
			}
			else
			{
				_lastInvasionCycleDropped = num;
				orig.Invoke(self, damageReport);
			}
		}
	}
	public static class RiskOfOptionsIntegration
	{
		public const string GUID = "com.rune580.riskofoptions";

		public static bool IsLoaded => Chainloader.PluginInfos.ContainsKey("com.rune580.riskofoptions");

		public static void Init(ConfigEntry<bool> isEnabled, ConfigEntry<bool> allowBothDrops, ConfigEntry<bool> enableHighPopFix, ConfigEntry<bool> enableDebugLogging)
		{
			if (!IsLoaded)
			{
				return;
			}
			try
			{
				InitInternal(isEnabled, allowBothDrops, enableHighPopFix, enableDebugLogging);
			}
			catch (Exception ex)
			{
				ManualLogSource instanceLogger = Plugin.InstanceLogger;
				if (instanceLogger != null)
				{
					instanceLogger.LogWarning((object)("RiskOfOptions integration failed to initialize: " + ex.Message));
				}
			}
		}

		[MethodImpl(MethodImplOptions.NoInlining)]
		private static void InitInternal(ConfigEntry<bool> isEnabled, ConfigEntry<bool> allowBothDrops, ConfigEntry<bool> enableHighPopFix, ConfigEntry<bool> enableDebugLogging)
		{
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Expected O, but got Unknown
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Expected O, but got Unknown
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Expected O, but got Unknown
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Expected O, but got Unknown
			ModSettingsManager.SetModDescription("Fixes Doppelgangers (Umbras) skin synchronization and item/drop distribution when using Artifact of Vengeance and Artifact of Swarms.");
			ModSettingsManager.AddOption((BaseOption)new CheckBoxOption(isEnabled));
			ModSettingsManager.AddOption((BaseOption)new CheckBoxOption(allowBothDrops));
			ModSettingsManager.AddOption((BaseOption)new CheckBoxOption(enableHighPopFix));
			ModSettingsManager.AddOption((BaseOption)new CheckBoxOption(enableDebugLogging));
		}
	}
}