Decompiled source of EnemyVariety v0.3.0

EnemyVariety.dll

Decompiled 4 days ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security.Permissions;
using BepInEx;
using BepInEx.Configuration;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Mono.Cecil.Cil;
using MonoMod.Cil;
using RoR2;
using UnityEngine;
using UnityEngine.AddressableAssets;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("0.3.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.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace Local.Enemy.Variety
{
	internal class Override : MonoBehaviour
	{
		internal Type value;

		internal static void Set(CombatDirector director, Type value)
		{
			Override @override = ((Component)director).GetComponent<Override>();
			if (value == Type.None)
			{
				if (!Object.op_Implicit((Object)(object)@override))
				{
					return;
				}
				Object.Destroy((Object)(object)@override);
			}
			else if (@override == null)
			{
				@override = ((Component)director).gameObject.AddComponent<Override>();
			}
			Console.WriteLine($"Set override to '{value}' for \"{((Object)director).name}\".");
			@override.value = value;
		}

		internal static Type Get(CombatDirector director)
		{
			Override @override = default(Override);
			if (!((Component)director).TryGetComponent<Override>(ref @override))
			{
				return Type.None;
			}
			return @override.value;
		}

		public static void Clear(CombatDirector director)
		{
			ref bool hasStartedWave = ref director.hasStartedWave;
			if (hasStartedWave)
			{
				Set(director, Type.None);
			}
			hasStartedWave = false;
		}
	}
	internal enum Type
	{
		None,
		Scene,
		Boss,
		Shrine,
		Card
	}
	internal class Hook
	{
		[HarmonyPatch(typeof(CombatDirector), "Simulate")]
		[HarmonyTranspiler]
		private static IEnumerable<CodeInstruction> TriggerWaveStart(IEnumerable<CodeInstruction> IL)
		{
			System.Type typeFromHandle = typeof(CombatDirector);
			FieldInfo indicator = typeFromHandle.GetField("hasStartedWave", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			FieldInfo configuration = typeFromHandle.GetField("shouldSpawnOneWave");
			foreach (CodeInstruction instruction in IL)
			{
				yield return instruction;
				if (CodeInstructionExtensions.LoadsField(instruction, configuration, false))
				{
					yield return new CodeInstruction(OpCodes.Pop, (object)null);
					yield return new CodeInstruction(OpCodes.Ldc_I4_1, (object)null);
				}
				else if (CodeInstructionExtensions.LoadsField(instruction, indicator, false))
				{
					yield return new CodeInstruction(OpCodes.Ldarg_0, (object)null);
					yield return new CodeInstruction(OpCodes.Ldfld, (object)configuration);
					yield return new CodeInstruction(OpCodes.And, (object)null);
					yield return new CodeInstruction(OpCodes.Ldarg_0, (object)null);
					yield return new CodeInstruction(OpCodes.Call, (object)typeof(Override).GetMethod("Clear"));
				}
			}
		}

		[HarmonyPatch(typeof(CombatDirector), "SpendAllCreditsOnMapSpawns")]
		[HarmonyPrefix]
		private static void PopulateScene(CombatDirector __instance)
		{
			__instance.hasStartedWave = true;
			Override.Set(__instance, Type.Scene);
		}

		[HarmonyPatch(typeof(CombatDirector), "SpendAllCreditsOnMapSpawns")]
		[HarmonyPostfix]
		private static void EndScene(CombatDirector __instance)
		{
			Override.Clear(__instance);
		}

		[HarmonyPatch(typeof(CombatDirector), "SetNextSpawnAsBoss")]
		[HarmonyPrefix]
		private static void SetBoss(CombatDirector __instance)
		{
			Override.Set(__instance, Type.Boss);
		}

		[HarmonyPatch(typeof(CombatDirector), "CombatShrineActivation")]
		[HarmonyPostfix]
		private static void CombatShrine(CombatDirector __instance)
		{
			Override.Set(__instance, Type.Shrine);
		}

		[HarmonyPatch(typeof(CombatDirector), "OverrideCurrentMonsterCard")]
		[HarmonyPrefix]
		private static void OverrideMonsterCard(CombatDirector __instance)
		{
			Override.Set(__instance, Type.Card);
		}
	}
	[BepInPlugin("local.enemy.variety", "EnemyVariety", "0.3.0")]
	internal class Plugin : BaseUnityPlugin
	{
		public const string version = "0.3.0";

		public const string identifier = "local.enemy.variety";

		private static ConfigEntry<bool> scene;

		private static ConfigEntry<bool> boss;

		private static ConfigEntry<bool> combat;

		private static ConfigEntry<float> horde;

		protected async void Awake()
		{
			boss = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Apply to Teleporter Boss", true, "If enabled, multiple types of bosses may appear for the teleporter event.");
			horde = ((BaseUnityPlugin)this).Config.Bind<float>("General", "Horde of Many", 5f, new ConfigDescription("Percent chance for a different type of monster to be chosen instead.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 100f), Array.Empty<object>()));
			scene = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Scene Director", true, "This determines if expensive enemies are favored during initialization.");
			combat = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Shrine of Combat", true, "Whether those summoned by this interactable should be affected.");
			Harmony.CreateAndPatchAll(typeof(Plugin), (string)null);
			Harmony.CreateAndPatchAll(typeof(Hook), (string)null);
			GameObject val = await Addressables.LoadAssetAsync<GameObject>((object)"RoR2/DLC2/ShrineHalcyonite.prefab").Task;
			if (Object.op_Implicit((Object)(object)val))
			{
				CombatDirector[] componentsInChildren = val.GetComponentsInChildren<CombatDirector>();
				for (int i = 0; i < componentsInChildren.Length; i++)
				{
					((Behaviour)componentsInChildren[i]).enabled = false;
				}
			}
		}

		[HarmonyPatch(typeof(CombatDirector), "AttemptSpawnOnTarget")]
		[HarmonyPrefix]
		private static void ResetMonsterCard(CombatDirector __instance)
		{
			//IL_0163: Unknown result type (might be due to invalid IL or missing references)
			//IL_0168: Unknown result type (might be due to invalid IL or missing references)
			//IL_016a: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d1: Unknown result type (might be due to invalid IL or missing references)
			WeightedSelection<DirectorCard> finalMonsterCardsSelection = __instance.finalMonsterCardsSelection;
			if (finalMonsterCardsSelection == null || !__instance.resetMonsterCardIfFailed)
			{
				return;
			}
			DirectorCard currentMonsterCard = __instance.currentMonsterCard;
			Xoroshiro128Plus rng = __instance.rng;
			bool? flag = null;
			switch (Override.Get(__instance))
			{
			case Type.Scene:
			{
				SceneDef currentSceneDef = SceneCatalog.currentSceneDef;
				if (!scene.Value || !(currentSceneDef?.stageOrder <= Run.stagesPerLoop))
				{
					return;
				}
				break;
			}
			case Type.Boss:
				if (!boss.Value)
				{
					return;
				}
				if (__instance.hasStartedWave)
				{
					flag = false;
					if (currentMonsterCard == null || !currentMonsterCard.IsBoss())
					{
						return;
					}
				}
				else
				{
					flag = rng.nextNormalizedFloat < horde.Value / 100f;
					if (currentMonsterCard != null && currentMonsterCard.IsBoss() != flag)
					{
						return;
					}
				}
				break;
			case Type.Shrine:
				if (!combat.Value)
				{
					return;
				}
				break;
			case Type.Card:
				return;
			}
			int spawnCountInCurrentWave = __instance.spawnCountInCurrentWave;
			int num = 0;
			if (currentMonsterCard != null)
			{
				num = currentMonsterCard.cost;
			}
			else if (!flag.HasValue)
			{
				return;
			}
			WeightedSelection<DirectorCard> val = new WeightedSelection<DirectorCard>(finalMonsterCardsSelection.Count);
			float monsterCredit = __instance.monsterCredit;
			float val2 = Math.Min(800f, monsterCredit);
			for (int i = 0; i < finalMonsterCardsSelection.Count; i++)
			{
				ChoiceInfo<DirectorCard> choice = finalMonsterCardsSelection.GetChoice(i);
				currentMonsterCard = choice.value;
				if ((currentMonsterCard.cost <= num || !((float)currentMonsterCard.cost > monsterCredit)) && currentMonsterCard.IsAvailable())
				{
					if (!flag.HasValue)
					{
						choice.weight *= Math.Min(currentMonsterCard.cost, val2);
					}
					else if (currentMonsterCard.IsBoss() == flag)
					{
						continue;
					}
					val.AddChoice(choice);
				}
			}
			if (val.Count > 0)
			{
				currentMonsterCard = val.Evaluate(rng.nextNormalizedFloat);
				__instance.PrepareNewMonsterWave(currentMonsterCard);
				__instance.spawnCountInCurrentWave = spawnCountInCurrentWave;
			}
		}

		[HarmonyPrefix]
		[HarmonyPatch(typeof(Chat), "SendBroadcastChat", new System.Type[] { typeof(ChatMessageBase) })]
		private static void ChangeMessage(ChatMessageBase message)
		{
			SubjectFormatChatMessage val = (SubjectFormatChatMessage)(object)((message is SubjectFormatChatMessage) ? message : null);
			if (val != null)
			{
				bool? flag = val.paramTokens?.Any();
				if (flag.HasValue && flag.GetValueOrDefault() && combat.Value && ((SubjectChatMessage)val).baseToken == "SHRINE_COMBAT_USE_MESSAGE")
				{
					val.paramTokens[0] = Language.GetString("LOGBOOK_CATEGORY_MONSTER").ToLower();
				}
			}
		}

		[HarmonyPatch(typeof(BossGroup), "UpdateBossMemories")]
		[HarmonyPostfix]
		private static void UpdateTitle(BossGroup __instance)
		{
			if (!boss.Value)
			{
				return;
			}
			Dictionary<(string, string), float> dictionary = new Dictionary<(string, string), float>();
			float num = 0f;
			for (int i = 0; i < __instance.bossMemoryCount; i++)
			{
				CharacterBody cachedBody = __instance.bossMemories[i].cachedBody;
				if (!Object.op_Implicit((Object)(object)cachedBody))
				{
					continue;
				}
				HealthComponent healthComponent = cachedBody.healthComponent;
				if (!(((healthComponent != null) ? new bool?(healthComponent.alive) : null) ?? true))
				{
					continue;
				}
				string bestBodyName = Util.GetBestBodyName(((Component)cachedBody).gameObject);
				string text = cachedBody.GetSubtitle();
				(string, string) key = (bestBodyName, text);
				if (!dictionary.ContainsKey(key))
				{
					dictionary[key] = 0f;
				}
				dictionary[key] += healthComponent.combinedHealth + healthComponent.missingCombinedHealth * 4f;
				if (dictionary[key] > num)
				{
					num = dictionary[key];
					if (string.IsNullOrEmpty(text))
					{
						text = Language.GetString("NULL_SUBTITLE");
					}
					__instance.bestObservedName = bestBodyName;
					__instance.bestObservedSubtitle = "<sprite name=\"CloudLeft\" tint=1> " + text + " <sprite name=\"CloudRight\" tint=1>";
				}
			}
		}

		[HarmonyPatch(typeof(CombatDirector), "SpendAllCreditsOnMapSpawns")]
		[HarmonyILManipulator]
		private static void SkipReroll(ILContext context)
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Expected O, but got Unknown
			//IL_00ad: Unknown result type (might be due to invalid IL or missing references)
			ILCursor val = new ILCursor(context);
			MethodInfo method = typeof(CombatDirector).GetMethod("PrepareNewMonsterWave", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			if (val.TryGotoNext((MoveType)2, new Func<Instruction, bool>[1]
			{
				(Instruction i) => ILPatternMatchingExt.MatchCall(i, (MethodBase)method)
			}))
			{
				ILLabel val2 = val.MarkLabel();
				ILLabel val3 = default(ILLabel);
				if (val.TryGotoPrev((MoveType)2, new Func<Instruction, bool>[1]
				{
					(Instruction i) => ILPatternMatchingExt.MatchBr(i, ref val3)
				}))
				{
					val.MoveAfterLabels();
					val.EmitDelegate<Func<bool>>((Func<bool>)(() => scene.Value));
					val.Emit(OpCodes.Brtrue, (object)val2);
					return;
				}
			}
			Console.WriteLine("Failed to patch scene combat director.");
		}
	}
	internal static class Extension
	{
		internal static bool IsBoss(this DirectorCard card)
		{
			SpawnCard spawnCard = card.spawnCard;
			CharacterSpawnCard val = (CharacterSpawnCard)(object)((spawnCard is CharacterSpawnCard) ? spawnCard : null);
			if (val == null || val.forbiddenAsBoss)
			{
				return false;
			}
			return ((SpawnCard)val).prefab.GetComponent<CharacterMaster>().bodyPrefab.GetComponent<CharacterBody>().isChampion;
		}
	}
}