Decompiled source of Enemies Anywhere v1.2.0

AnyRoomEnemies.dll

Decompiled 2 weeks 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;
using System.Security.Permissions;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using UnityEngine;

[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("Omniscye")]
[assembly: AssemblyConfiguration("Debug")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+c574138cfea486cfa7104eae1a6f135c166daf8b")]
[assembly: AssemblyProduct("AnyRoomEnemies")]
[assembly: AssemblyTitle("AnyRoomEnemies")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.0.0")]
[module: UnverifiableCode]
[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 Empress.InitialSpawnDistributor
{
	[BepInPlugin("dev.empress.initialspawndistributor", "Empress Initial Spawn Distributor", "1.1.4")]
	public sealed class Plugin : BaseUnityPlugin
	{
		public enum Distribution
		{
			BalancedRooms,
			UniformPoints
		}

		public enum SeedMode
		{
			UnityRandom,
			DeterministicLevelSeed
		}

		internal static ConfigEntry<bool> IncludeStartRoom = null;

		internal static ConfigEntry<Distribution> DistributionMode = null;

		internal static ConfigEntry<SeedMode> RandomSeedMode = null;

		internal static ConfigEntry<bool> ForceSeparateGroupRooms = null;

		internal static ManualLogSource Log = null;

		internal Harmony? _harmony;

		internal static bool GroupContextActive;

		internal static HashSet<int> GroupUsedRooms = new HashSet<int>();

		internal static HashSet<LevelPoint> UsedFirstSpawnPoints = new HashSet<LevelPoint>();

		private void Awake()
		{
			//IL_0092: Unknown result type (might be due to invalid IL or missing references)
			//IL_009c: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			IncludeStartRoom = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "IncludeStartRoom", false, "");
			DistributionMode = ((BaseUnityPlugin)this).Config.Bind<Distribution>("General", "DistributionMode", Distribution.BalancedRooms, "");
			RandomSeedMode = ((BaseUnityPlugin)this).Config.Bind<SeedMode>("General", "RandomSeedMode", SeedMode.UnityRandom, "");
			ForceSeparateGroupRooms = ((BaseUnityPlugin)this).Config.Bind<bool>("Groups", "ForceSeparateGroupRooms", true, "");
			_harmony = new Harmony("dev.empress.initialspawndistributor");
			_harmony.PatchAll(typeof(FirstSpawnPatch));
			_harmony.PatchAll(typeof(EnemySpawnGroupScopePatch));
			Log.LogInfo((object)"[Empress] Initial Spawn Distributor 1.1.4 loaded. Host-only install is enough.");
		}

		private void OnDestroy()
		{
			try
			{
				Harmony? harmony = _harmony;
				if (harmony != null)
				{
					harmony.UnpatchSelf();
				}
			}
			catch
			{
			}
		}

		internal static Random GetRng()
		{
			int seed = Mathf.RoundToInt(Random.value * 2.1474836E+09f);
			return new Random(seed);
		}

		internal static int RoomKey(RoomVolume rv)
		{
			return Object.op_Implicit((Object)(object)rv) ? ((Object)rv).GetInstanceID() : int.MinValue;
		}
	}
	[HarmonyPatch(typeof(LevelGenerator), "EnemySpawn", new Type[]
	{
		typeof(EnemySetup),
		typeof(Vector3)
	})]
	internal static class EnemySpawnGroupScopePatch
	{
		private static void Prefix()
		{
			if (Plugin.ForceSeparateGroupRooms.Value)
			{
				Plugin.GroupContextActive = true;
				Plugin.GroupUsedRooms.Clear();
			}
		}

		private static void Finalizer(Exception __exception)
		{
			Plugin.GroupContextActive = false;
			Plugin.GroupUsedRooms.Clear();
		}
	}
	[HarmonyPatch(typeof(EnemyDirector), "FirstSpawnPointAdd")]
	internal static class FirstSpawnPatch
	{
		private static bool Prefix(EnemyDirector __instance, EnemyParent _enemyParent)
		{
			try
			{
				if (!SemiFunc.IsMasterClientOrSingleplayer())
				{
					return true;
				}
				List<LevelPoint> list = SemiFunc.LevelPointsGetAll();
				if (list == null || list.Count == 0)
				{
					return true;
				}
				List<LevelPoint> list2 = new List<LevelPoint>();
				foreach (LevelPoint item2 in list)
				{
					if (Object.op_Implicit((Object)(object)item2) && !item2.Truck)
					{
						list2.Add(item2);
					}
				}
				if (list2.Count == 0)
				{
					return true;
				}
				List<LevelPoint> list3 = list2.Where((LevelPoint lp) => !Plugin.UsedFirstSpawnPoints.Contains(lp)).ToList();
				if (list3.Count == 0)
				{
					Plugin.UsedFirstSpawnPoints.Clear();
					list3 = list2;
				}
				Random rng = Plugin.GetRng();
				LevelPoint val;
				if (Plugin.DistributionMode.Value == Plugin.Distribution.BalancedRooms)
				{
					Dictionary<int, List<LevelPoint>> groups = new Dictionary<int, List<LevelPoint>>();
					foreach (LevelPoint item3 in list3)
					{
						int key = Plugin.RoomKey(item3.Room);
						if (!groups.TryGetValue(key, out List<LevelPoint> value))
						{
							value = new List<LevelPoint>();
							groups[key] = value;
						}
						value.Add(item3);
					}
					Dictionary<int, int> usedPerRoom = new Dictionary<int, int>();
					foreach (LevelPoint usedFirstSpawnPoint in Plugin.UsedFirstSpawnPoints)
					{
						int key2 = Plugin.RoomKey(usedFirstSpawnPoint.Room);
						usedPerRoom[key2] = ((!usedPerRoom.TryGetValue(key2, out var value2)) ? 1 : (value2 + 1));
					}
					List<int> source = groups.Keys.ToList();
					if (Plugin.GroupContextActive && Plugin.ForceSeparateGroupRooms.Value)
					{
						List<int> list4 = source.Where((int k) => !Plugin.GroupUsedRooms.Contains(k)).ToList();
						if (list4.Count > 0)
						{
							source = list4;
						}
					}
					int value4;
					int minUse = source.Select((int k) => usedPerRoom.TryGetValue(k, out value4) ? value4 : 0).DefaultIfEmpty(0).Min();
					int value3;
					List<int> source2 = source.Where((int k) => (usedPerRoom.TryGetValue(k, out value3) ? value3 : 0) == minUse).ToList();
					int maxAvail = source2.Select((int k) => groups[k].Count).Max();
					List<int> list5 = source2.Where((int k) => groups[k].Count == maxAvail).ToList();
					int num = list5[rng.Next(list5.Count)];
					List<LevelPoint> list6 = groups[num];
					val = list6[rng.Next(list6.Count)];
					if (Plugin.GroupContextActive && Plugin.ForceSeparateGroupRooms.Value)
					{
						Plugin.GroupUsedRooms.Add(num);
					}
				}
				else
				{
					val = list3[rng.Next(list3.Count)];
					if (Plugin.GroupContextActive && Plugin.ForceSeparateGroupRooms.Value)
					{
						int item = Plugin.RoomKey(val.Room);
						if (Plugin.GroupUsedRooms.Contains(item))
						{
							LevelPoint val2 = ((IEnumerable<LevelPoint>)list3).FirstOrDefault((Func<LevelPoint, bool>)((LevelPoint lp) => !Plugin.GroupUsedRooms.Contains(Plugin.RoomKey(lp.Room))));
							if (Object.op_Implicit((Object)(object)val2))
							{
								val = val2;
							}
							Plugin.GroupUsedRooms.Add(Plugin.RoomKey(val.Room));
						}
						else
						{
							Plugin.GroupUsedRooms.Add(item);
						}
					}
				}
				FieldInfo field = typeof(EnemyParent).GetField("firstSpawnPoint", BindingFlags.Instance | BindingFlags.NonPublic);
				if (field == null)
				{
					return true;
				}
				field.SetValue(_enemyParent, val);
				Plugin.UsedFirstSpawnPoints.Add(val);
				if (Plugin.UsedFirstSpawnPoints.Count >= list2.Count)
				{
					Plugin.UsedFirstSpawnPoints.Clear();
				}
				return false;
			}
			catch (Exception arg)
			{
				Plugin.Log.LogError((object)$"[Empress] FirstSpawnPointAdd patch failed, falling back to vanilla: {arg}");
				return true;
			}
		}
	}
}