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;
}
}
}
}