using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Newtonsoft.Json;
using REPOLib;
using SpawnConfig.ExtendedClasses;
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("Index154.SpawnConfig")]
[assembly: AssemblyConfiguration("Debug")]
[assembly: AssemblyFileVersion("1.2.3.0")]
[assembly: AssemblyInformationalVersion("1.2.3+d59ff0167d97ebe7640b908172d59d6c10cf77e5")]
[assembly: AssemblyProduct("SpawnConfig")]
[assembly: AssemblyTitle("Index154.SpawnConfig")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.2.3.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 SpawnConfig
{
public class ConfigManager
{
internal ConfigEntry<bool> preventSpawns = null;
internal ConfigEntry<bool> addMissingGroups = null;
internal ConfigEntry<double> repeatMultiplier = null;
internal ConfigEntry<bool> ignoreInvalidGroups = null;
internal ConfigEntry<int> groupCountMultiplier = null;
internal void Setup(ConfigFile configFile)
{
//IL_0019: Unknown result type (might be due to invalid IL or missing references)
//IL_0023: Expected O, but got Unknown
//IL_0040: Unknown result type (might be due to invalid IL or missing references)
//IL_004a: Expected O, but got Unknown
//IL_006f: Unknown result type (might be due to invalid IL or missing references)
//IL_0079: Expected O, but got Unknown
//IL_0096: Unknown result type (might be due to invalid IL or missing references)
//IL_00a0: Expected O, but got Unknown
//IL_00c4: Unknown result type (might be due to invalid IL or missing references)
//IL_00ce: Expected O, but got Unknown
preventSpawns = configFile.Bind<bool>("General", "Prevent enemy spawning", false, new ConfigDescription("Prevent enemy spawning entirely, turning the game into a no-stakes gathering simulator or for when you want to test something in peace", (AcceptableValueBase)null, Array.Empty<object>()));
addMissingGroups = configFile.Bind<bool>("General", "Re-add missing groups", false, new ConfigDescription("Whether the mod should update your custom SpawnGroups config at launch by adding all loaded enemy groups that are missing from it", (AcceptableValueBase)null, Array.Empty<object>()));
repeatMultiplier = configFile.Bind<double>("General", "Repeat spawn weight multiplier", 0.5, new ConfigDescription("All three weights of an enemy group will be multiplied by this value for the current level after having been selected once. Reduces the chance of encountering multiple copies of a group in the same level. Set to 1.0 to \"disable\"", (AcceptableValueBase)null, Array.Empty<object>()));
ignoreInvalidGroups = configFile.Bind<bool>("General", "Ignore groups with invalid spawnObjects", true, new ConfigDescription("If set to true, any group containing a single invalid spawn object will be ignored completely. If set to false, only the individual spawn object will be ignored and the group can still spawn as long as it contains at least one valid enemy", (AcceptableValueBase)null, Array.Empty<object>()));
groupCountMultiplier = configFile.Bind<int>("General", "Group count multiplier", 1, new ConfigDescription("The amount of enemy groups spawned each level is multiplied by this number. This will apply on top of what you configure in `GroupsPerLevel.json`", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 10), Array.Empty<object>()));
}
}
public class JsonManager
{
public static List<ExtendedGroupCounts> GetEGCListFromJSON(string path)
{
List<ExtendedGroupCounts> result = new List<ExtendedGroupCounts>();
if (File.Exists(path))
{
string text = File.ReadAllText(path);
if (text != null && text != "")
{
result = JsonConvert.DeserializeObject<List<ExtendedGroupCounts>>(text);
}
}
return result;
}
public static List<ExtendedEnemySetup> GetEESListFromJSON(string path)
{
List<ExtendedEnemySetup> result = new List<ExtendedEnemySetup>();
if (File.Exists(path))
{
string text = File.ReadAllText(path);
if (text != null && text != "")
{
result = JsonConvert.DeserializeObject<List<ExtendedEnemySetup>>(text);
if (!text.Contains("allowDuplicates"))
{
SpawnConfig.missingProperties = true;
}
}
}
return result;
}
public static string EESToJSON(List<ExtendedEnemySetup> eesList)
{
//IL_000f: Unknown result type (might be due to invalid IL or missing references)
//IL_0015: Expected O, but got Unknown
StringBuilder stringBuilder = new StringBuilder();
StringWriter stringWriter = new StringWriter(stringBuilder);
JsonWriter val = (JsonWriter)new JsonTextWriter((TextWriter)stringWriter);
try
{
val.Formatting = (Formatting)1;
val.WriteStartArray();
foreach (ExtendedEnemySetup ees in eesList)
{
val.WriteStartObject();
val.WritePropertyName("name");
val.WriteValue(ees.name);
val.WritePropertyName("levelRangeCondition");
val.WriteValue(ees.levelRangeCondition);
val.WritePropertyName("minLevel");
val.WriteValue(ees.minLevel);
val.WritePropertyName("maxLevel");
val.WriteValue(ees.maxLevel);
val.WritePropertyName("runsPlayed");
val.WriteValue(ees.runsPlayed);
val.WritePropertyName("spawnObjects");
val.WriteStartArray();
foreach (string spawnObject in ees.spawnObjects)
{
val.WriteValue(spawnObject);
}
val.WriteEndArray();
val.WritePropertyName("difficulty1Weight");
val.WriteValue(ees.difficulty1Weight);
val.WritePropertyName("difficulty2Weight");
val.WriteValue(ees.difficulty2Weight);
val.WritePropertyName("difficulty3Weight");
val.WriteValue(ees.difficulty3Weight);
val.WritePropertyName("thisGroupOnly");
val.WriteValue(ees.thisGroupOnly);
val.WritePropertyName("allowDuplicates");
val.WriteValue(ees.allowDuplicates);
val.WritePropertyName("alterAmountChance");
val.WriteValue(ees.alterAmountChance);
val.WritePropertyName("alterAmountMin");
val.WriteValue(ees.alterAmountMin);
val.WritePropertyName("alterAmountMax");
val.WriteValue(ees.alterAmountMax);
val.WriteEndObject();
}
val.WriteEndArray();
}
finally
{
((IDisposable)val)?.Dispose();
}
return stringBuilder.ToString();
}
public static string GroupCountsToJSON(List<ExtendedGroupCounts> gcList)
{
//IL_000f: Unknown result type (might be due to invalid IL or missing references)
//IL_0015: Expected O, but got Unknown
StringBuilder stringBuilder = new StringBuilder();
StringWriter stringWriter = new StringWriter(stringBuilder);
JsonWriter val = (JsonWriter)new JsonTextWriter((TextWriter)stringWriter);
try
{
val.Formatting = (Formatting)1;
val.WriteStartArray();
foreach (ExtendedGroupCounts gc in gcList)
{
val.Formatting = (Formatting)1;
val.WriteStartObject();
val.WritePropertyName("level");
val.WriteValue(gc.level);
val.WritePropertyName("possibleGroupCounts");
val.WriteStartArray();
foreach (GroupCountEntry possibleGroupCount in gc.possibleGroupCounts)
{
val.WriteStartObject();
val.Formatting = (Formatting)0;
val.WritePropertyName("counts");
val.WriteStartArray();
val.WriteValue(possibleGroupCount.counts[0]);
val.WriteValue(possibleGroupCount.counts[1]);
val.WriteValue(possibleGroupCount.counts[2]);
val.WriteEndArray();
val.WritePropertyName("weight");
val.WriteValue(possibleGroupCount.weight);
val.WriteEndObject();
}
val.Formatting = (Formatting)1;
val.WriteEndArray();
val.WriteEndObject();
}
val.WriteEndArray();
}
finally
{
((IDisposable)val)?.Dispose();
}
return stringBuilder.ToString();
}
}
public class ListManager
{
public static Dictionary<string, EnemySetup> enemySetupsDict = new Dictionary<string, EnemySetup>();
public static Dictionary<string, GameObject> spawnObjectsDict = new Dictionary<string, GameObject>();
public static Dictionary<string, ExtendedEnemySetup> extendedSetups = new Dictionary<string, ExtendedEnemySetup>();
public static Dictionary<string, ExtendedSpawnObject> extendedSpawnObjects = new Dictionary<string, ExtendedSpawnObject>();
public static Dictionary<int, ExtendedGroupCounts> extendedGroupCounts = new Dictionary<int, ExtendedGroupCounts>();
public static List<int> difficulty1Counts = new List<int>();
public static List<int> difficulty2Counts = new List<int>();
public static List<int> difficulty3Counts = new List<int>();
public static List<int> levelNumbers = new List<int>();
}
[BepInPlugin("Index154.SpawnConfig", "SpawnConfig", "1.2.3")]
[BepInDependency(/*Could not decode attribute arguments.*/)]
public class SpawnConfig : BaseUnityPlugin
{
internal static ConfigManager configManager = null;
internal static ConfigFile Conf = null;
internal static readonly string configVersion = "1.0";
internal static readonly string exportPath = Path.Combine(Paths.ConfigPath, "SpawnConfig");
internal static readonly string spawnGroupsPath = Path.Combine(exportPath, "SpawnGroups.json");
internal static readonly string defaultSpawnGroupsPath = Path.Combine(exportPath, "Defaults", "SpawnGroups-Readonly.json");
internal static readonly string groupsPerLevelPath = Path.Combine(exportPath, "GroupsPerLevel.json");
internal static readonly string defaultGroupsPerLevelPath = Path.Combine(exportPath, "Defaults", "GroupsPerLevel-Readonly.json");
internal static readonly string groupsPerLevelExplainedPath = Path.Combine(exportPath, "GroupsPerLevel-Explained.json");
internal static readonly string spawnGroupsExplainedPath = Path.Combine(exportPath, "SpawnGroups-Explained.json");
public static bool missingProperties = false;
public static SpawnConfig Instance { get; private set; } = null;
internal static ManualLogSource Logger { get; private set; } = null;
internal static Harmony? Harmony { get; set; }
private void Awake()
{
Logger = ((BaseUnityPlugin)this).Logger;
Instance = this;
configManager = new ConfigManager();
Conf = ((BaseUnityPlugin)this).Config;
configManager.Setup(((BaseUnityPlugin)this).Config);
Directory.CreateDirectory(exportPath);
Directory.CreateDirectory(Path.Combine(exportPath, "Defaults"));
Patch();
Logger.LogInfo((object)"Index154.SpawnConfig has loaded!");
}
internal static void Patch()
{
//IL_000d: Unknown result type (might be due to invalid IL or missing references)
//IL_0012: Unknown result type (might be due to invalid IL or missing references)
//IL_0018: Expected O, but got Unknown
if (Harmony == null)
{
Harmony = new Harmony("Index154.SpawnConfig");
}
Logger.LogDebug((object)"Patching...");
Harmony.PatchAll();
Logger.LogDebug((object)"Finished patching!");
}
internal static void Unpatch()
{
Logger.LogDebug((object)"Unpatching...");
Harmony? harmony = Harmony;
if (harmony != null)
{
harmony.UnpatchSelf();
}
Logger.LogDebug((object)"Finished unpatching!");
}
public static void ReadAndUpdateJSON()
{
List<ExtendedEnemySetup> list = JsonManager.GetEESListFromJSON(spawnGroupsPath);
List<ExtendedGroupCounts> list2 = JsonManager.GetEGCListFromJSON(groupsPerLevelPath);
List<ExtendedGroupCounts> list3 = ListManager.extendedGroupCounts.Select<KeyValuePair<int, ExtendedGroupCounts>, ExtendedGroupCounts>((KeyValuePair<int, ExtendedGroupCounts> obj) => obj.Value).ToList();
File.WriteAllText(defaultGroupsPerLevelPath, JsonManager.GroupCountsToJSON(list3));
if (list2.Count < 1)
{
Logger.LogInfo((object)"No custom group count config found! Creating default file");
File.WriteAllText(groupsPerLevelPath, JsonManager.GroupCountsToJSON(list3));
list2 = list3;
}
if (list2[0].level != 1)
{
Logger.LogError((object)"Your custom group count config must contain at least a valid level 1 entry!");
list2 = list3;
}
List<ExtendedEnemySetup> list4 = ListManager.extendedSetups.Select<KeyValuePair<string, ExtendedEnemySetup>, ExtendedEnemySetup>((KeyValuePair<string, ExtendedEnemySetup> obj) => obj.Value).ToList();
File.WriteAllText(defaultSpawnGroupsPath, JsonManager.EESToJSON(list4));
if (list.Count < 1)
{
Logger.LogInfo((object)"No custom spawn groups config found! Creating default file");
File.WriteAllText(spawnGroupsPath, JsonManager.EESToJSON(list4));
list = list4;
}
bool flag = false;
foreach (ExtendedEnemySetup item in list)
{
if (item.Update())
{
flag = true;
}
}
Dictionary<string, ExtendedEnemySetup> dictionary = list.ToDictionary((ExtendedEnemySetup obj) => obj.name);
foreach (KeyValuePair<string, ExtendedEnemySetup> extendedSetup in ListManager.extendedSetups)
{
if (!dictionary.ContainsKey(extendedSetup.Value.name) && configManager.addMissingGroups.Value)
{
Logger.LogInfo((object)("Adding missing entry to custom config: " + extendedSetup.Value.name));
dictionary.Add(extendedSetup.Value.name, extendedSetup.Value);
flag = true;
}
}
list = dictionary.Values.ToList();
if (flag || missingProperties)
{
File.WriteAllText(spawnGroupsPath, JsonManager.EESToJSON(list));
}
ListManager.extendedSetups = list.ToDictionary((ExtendedEnemySetup obj) => obj.name);
ListManager.extendedGroupCounts = list2.ToDictionary((ExtendedGroupCounts obj) => obj.level);
File.Delete(spawnGroupsExplainedPath);
File.Delete(groupsPerLevelExplainedPath);
}
}
public static class MyPluginInfo
{
public const string PLUGIN_GUID = "Index154.SpawnConfig";
public const string PLUGIN_NAME = "SpawnConfig";
public const string PLUGIN_VERSION = "1.2.3";
}
}
namespace SpawnConfig.Patches
{
[HarmonyPatch(typeof(EnemyDirector))]
public class EnemyDirectorPatch
{
public static bool setupDone = false;
public static int currentDifficultyPick = 3;
public static bool onlyOneSetup = false;
public static List<string> enemyList = new List<string>();
public static List<string> enemiesSpawned = new List<string>();
public static List<string> enemiesSpawnedToDelete = new List<string>();
public static Dictionary<string, int> enemyCounts = new Dictionary<string, int>();
public static int enemyListIndex = 0;
public static void EnemySpawnSimulation(List<EnemySetup>[] enemiesDifficulties)
{
SpawnConfig.Logger.LogInfo((object)"Consolidated spawn distribution (100 runs, 15 levels each, 3 enemies per tier per level):");
Dictionary<string, int> dictionary = new Dictionary<string, int>();
Dictionary<string, float> dictionary2 = new Dictionary<string, float>();
for (int i = 0; i < 100; i++)
{
enemyList.Clear();
enemiesSpawned.Clear();
enemiesSpawnedToDelete.Clear();
enemyListIndex = 0;
for (int j = 0; j < 15; j++)
{
enemiesSpawnedToDelete.Clear();
foreach (string item in enemiesSpawned)
{
bool flag = false;
foreach (string item2 in enemiesSpawnedToDelete)
{
if (item == item2)
{
flag = true;
break;
}
}
if (!flag)
{
enemiesSpawnedToDelete.Add(item);
}
}
foreach (List<EnemySetup> enemiesList in enemiesDifficulties)
{
for (int l = 0; l < 3; l++)
{
string key = PickEnemySimulation(enemiesList);
if (!dictionary.ContainsKey(key))
{
dictionary.Add(key, 1);
}
else
{
dictionary[key]++;
}
}
}
for (int m = 0; m < 9; m++)
{
string text = enemyList[enemyListIndex];
enemyListIndex++;
int num = 0;
foreach (string item3 in enemiesSpawned)
{
if (item3 == text)
{
num++;
}
}
int num2 = 2;
while (num < 4 && num2 > 0)
{
enemiesSpawned.Add(text);
num++;
num2--;
}
}
foreach (string item4 in enemiesSpawnedToDelete)
{
enemiesSpawned.Remove(item4);
}
}
}
foreach (List<EnemySetup> list in enemiesDifficulties)
{
foreach (EnemySetup item5 in list)
{
if (dictionary.ContainsKey(((Object)item5).name))
{
if (Object.op_Implicit((Object)(object)item5.rarityPreset))
{
dictionary2.Add(((Object)item5).name, item5.rarityPreset.chance);
}
else
{
dictionary2.Add(((Object)item5).name, 100f);
}
}
}
}
foreach (KeyValuePair<string, int> item6 in dictionary)
{
SpawnConfig.Logger.LogInfo((object)(item6.Key + " = " + item6.Value + " (" + dictionary2[item6.Key] + ")"));
}
foreach (List<EnemySetup> list2 in enemiesDifficulties)
{
foreach (EnemySetup item7 in list2)
{
if (!dictionary.ContainsKey(((Object)item7).name))
{
SpawnConfig.Logger.LogInfo((object)(((Object)item7).name + " = 0 (" + item7.rarityPreset.chance + ")"));
}
}
}
}
public static string PickEnemySimulation(List<EnemySetup> _enemiesList)
{
ListExtension.Shuffle<EnemySetup>((IList<EnemySetup>)_enemiesList);
EnemySetup val = null;
float num = -1f;
foreach (EnemySetup _enemies in _enemiesList)
{
int num2 = 0;
foreach (string item in enemiesSpawned)
{
if (item == ((Object)_enemies).name)
{
num2++;
}
}
int num3 = 0;
foreach (string enemy in enemyList)
{
if (enemy == ((Object)_enemies).name)
{
num3++;
}
}
float num4 = 100f;
if (Object.op_Implicit((Object)(object)_enemies.rarityPreset))
{
num4 = _enemies.rarityPreset.chance;
}
float num5 = Mathf.Max(1f, num4 - 30f * (float)num2 - 10f * (float)num3);
float num6 = Random.Range(0f, num5);
if (num6 > num)
{
val = _enemies;
num = num6;
}
}
enemyList.Add(((Object)val).name);
return ((Object)val).name;
}
[HarmonyPatch("Start")]
[HarmonyPostfix]
public static void SetupAfterBundles(EnemyDirector __instance)
{
EnemyDirector __instance2 = __instance;
BundleLoader.OnAllBundlesLoaded += delegate
{
SpawnConfig.Logger.LogInfo((object)"All bundles have been loaded! Running setup...");
SetupOnStart(__instance2);
};
}
public static void SetupOnStart(EnemyDirector __instance)
{
if (setupDone)
{
return;
}
List<EnemySetup>[] array = new List<EnemySetup>[3] { __instance.enemiesDifficulty3, __instance.enemiesDifficulty2, __instance.enemiesDifficulty1 };
int num = 3;
List<EnemySetup>[] array2 = array;
foreach (List<EnemySetup> list in array2)
{
foreach (EnemySetup item in list)
{
foreach (GameObject spawnObject in item.spawnObjects)
{
((Object)spawnObject).name = ((Object)spawnObject).name;
ExtendedSpawnObject extendedSpawnObject = new ExtendedSpawnObject(spawnObject);
if (!ListManager.spawnObjectsDict.ContainsKey(((Object)spawnObject).name))
{
ListManager.spawnObjectsDict.Add(((Object)spawnObject).name, spawnObject);
}
}
ExtendedEnemySetup extendedEnemySetup = new ExtendedEnemySetup(item, num);
if (!ListManager.extendedSetups.ContainsKey(((Object)item).name))
{
ListManager.extendedSetups.Add(extendedEnemySetup.name, extendedEnemySetup);
}
}
num--;
}
SpawnConfig.Logger.LogInfo((object)"Found the following enemy spawnObjects:");
foreach (KeyValuePair<string, GameObject> item2 in ListManager.spawnObjectsDict)
{
if (!item2.Key.Contains("Director"))
{
SpawnConfig.Logger.LogInfo((object)item2.Key);
}
}
int num2 = -1;
for (int j = 0; j < 21; j++)
{
float num3 = Mathf.Clamp01((float)j / 9f);
float num4 = Mathf.Clamp01((float)(j - 9) / 10f);
int num5;
int num6;
int num7;
if (num4 > 0f)
{
num5 = (int)__instance.amountCurve3_2.Evaluate(num4);
num6 = (int)__instance.amountCurve2_2.Evaluate(num4);
num7 = (int)__instance.amountCurve1_2.Evaluate(num4);
}
else
{
num5 = (int)__instance.amountCurve3_1.Evaluate(num3);
num6 = (int)__instance.amountCurve2_1.Evaluate(num3);
num7 = (int)__instance.amountCurve1_1.Evaluate(num3);
}
if (j == 0 || num5 != ListManager.difficulty3Counts[num2] || num6 != ListManager.difficulty2Counts[num2] || num7 != ListManager.difficulty1Counts[num2])
{
ListManager.levelNumbers.Add(j + 1);
ListManager.difficulty3Counts.Add(num5);
ListManager.difficulty2Counts.Add(num6);
ListManager.difficulty1Counts.Add(num7);
num2++;
}
}
for (int k = 0; k < ListManager.difficulty1Counts.Count; k++)
{
ExtendedGroupCounts extendedGroupCounts = new ExtendedGroupCounts(k);
ListManager.extendedGroupCounts.Add(extendedGroupCounts.level, extendedGroupCounts);
}
SpawnConfig.ReadAndUpdateJSON();
List<string> list2 = new List<string>();
foreach (KeyValuePair<string, ExtendedEnemySetup> extendedSetup in ListManager.extendedSetups)
{
bool flag = false;
int num8 = 0;
List<int> list3 = new List<int>();
foreach (string spawnObject2 in extendedSetup.Value.spawnObjects)
{
if (!ListManager.spawnObjectsDict.ContainsKey(spawnObject2))
{
if (SpawnConfig.configManager.ignoreInvalidGroups.Value)
{
SpawnConfig.Logger.LogError((object)("Unable to resolve enemy name \"" + spawnObject2 + "\" in group \"" + extendedSetup.Value.name + "\"! This group will be ignored"));
flag = true;
}
else
{
SpawnConfig.Logger.LogError((object)("Unable to resolve enemy name \"" + spawnObject2 + "\" in group \"" + extendedSetup.Value.name + "\"! This enemy will be removed but the group can still spawn"));
list3.Add(num8);
}
}
num8++;
}
for (int num9 = list3.Count - 1; num9 > -1; num9--)
{
extendedSetup.Value.spawnObjects.RemoveAt(list3[num9]);
}
if (extendedSetup.Value.spawnObjects.Count < 1 && !flag)
{
flag = true;
SpawnConfig.Logger.LogError((object)("The group \"" + extendedSetup.Value.name + "\" contains no valid enemies! This group will be ignored"));
}
if (flag)
{
list2.Add(extendedSetup.Key);
}
}
foreach (string item3 in list2)
{
ListManager.extendedSetups.Remove(item3);
}
setupDone = true;
}
[HarmonyPatch("AmountSetup")]
[HarmonyPrefix]
public static bool AmountSetupOverride(EnemyDirector __instance)
{
if (!SemiFunc.IsMasterClientOrSingleplayer())
{
return false;
}
if (!setupDone)
{
SpawnConfig.Logger.LogWarning((object)"Setup function hasn't run yet! Trying again now...");
SetupOnStart(__instance);
}
__instance.enemyListCurrent.Clear();
__instance.enemyList.Clear();
__instance.enemiesDifficulty1.Clear();
__instance.enemiesDifficulty2.Clear();
__instance.enemiesDifficulty3.Clear();
onlyOneSetup = false;
foreach (KeyValuePair<string, ExtendedEnemySetup> extendedSetup in ListManager.extendedSetups)
{
if (extendedSetup.Value.difficulty1Weight > 0f)
{
__instance.enemiesDifficulty1.Add(extendedSetup.Value.GetEnemySetup());
}
if (extendedSetup.Value.difficulty2Weight > 0f)
{
__instance.enemiesDifficulty2.Add(extendedSetup.Value.GetEnemySetup());
}
if (extendedSetup.Value.difficulty3Weight > 0f)
{
__instance.enemiesDifficulty3.Add(extendedSetup.Value.GetEnemySetup());
}
}
EnemySetup val = ScriptableObject.CreateInstance<EnemySetup>();
((Object)val).name = "Fallback";
val.spawnObjects = new List<GameObject>();
if (__instance.enemiesDifficulty1.Count < 1)
{
__instance.enemiesDifficulty1.Add(val);
}
if (__instance.enemiesDifficulty2.Count < 1)
{
__instance.enemiesDifficulty2.Add(val);
}
if (__instance.enemiesDifficulty3.Count < 1)
{
__instance.enemiesDifficulty3.Add(val);
}
int num = 0;
int num2 = 0;
int num3 = 0;
int num4 = RunManager.instance.levelsCompleted + 1;
int num5 = 0;
int num6 = num4;
while (num5 == 0)
{
if (ListManager.extendedGroupCounts.ContainsKey(num6))
{
num5 = num6;
SpawnConfig.Logger.LogInfo((object)("Using group count configs from [Level " + num6 + "]!"));
}
else
{
SpawnConfig.Logger.LogDebug((object)("Found no config for [Level " + num6 + "]..."));
num6--;
}
if (num6 < 1)
{
SpawnConfig.Logger.LogError((object)"No config entry for level 1 found in GroupsPerLevel.json! Enemy spawning will break!");
return false;
}
}
int num7 = 0;
foreach (GroupCountEntry possibleGroupCount in ListManager.extendedGroupCounts[num5].possibleGroupCounts)
{
num7 += possibleGroupCount.weight;
}
int num8 = Random.Range(1, num7 + 1);
SpawnConfig.Logger.LogInfo((object)("Selecting group counts based on random number " + num8 + "..."));
foreach (GroupCountEntry possibleGroupCount2 in ListManager.extendedGroupCounts[num5].possibleGroupCounts)
{
int weight = possibleGroupCount2.weight;
if (possibleGroupCount2.counts.Count < 3)
{
SpawnConfig.Logger.LogError((object)"Found possibleGroupCounts entry with less than 3 numbers in the \"counts\" value! Missing group counts will lead to 0 groups spawning for the corresponding difficulty tier!");
while (possibleGroupCount2.counts.Count < 3)
{
possibleGroupCount2.counts.Add(0);
}
}
SpawnConfig.Logger.LogDebug((object)("=> [" + possibleGroupCount2.counts[0] + "," + possibleGroupCount2.counts[1] + "," + possibleGroupCount2.counts[2] + "] = " + weight + " / " + num8));
if (weight >= num8)
{
num = possibleGroupCount2.counts[2];
num2 = possibleGroupCount2.counts[1];
num3 = possibleGroupCount2.counts[0];
if (SpawnConfig.configManager.groupCountMultiplier.Value != 1)
{
SpawnConfig.Logger.LogInfo((object)("Applying global group count multiplier: " + SpawnConfig.configManager.groupCountMultiplier.Value));
num *= SpawnConfig.configManager.groupCountMultiplier.Value;
num2 *= SpawnConfig.configManager.groupCountMultiplier.Value;
num3 *= SpawnConfig.configManager.groupCountMultiplier.Value;
}
SpawnConfig.Logger.LogInfo((object)("Selected group counts: [" + num3 + "," + num2 + "," + num + "]"));
break;
}
num8 -= weight;
}
for (int i = 0; i < num; i++)
{
PickEnemiesCustom(__instance.enemiesDifficulty3, __instance);
}
for (int j = 0; j < num2; j++)
{
PickEnemiesCustom(__instance.enemiesDifficulty2, __instance);
}
for (int k = 0; k < num3; k++)
{
PickEnemiesCustom(__instance.enemiesDifficulty1, __instance);
}
__instance.totalAmount = num3 + num2 + num;
return false;
}
public static void PickEnemiesCustom(List<EnemySetup> _enemiesList, EnemyDirector __instance)
{
if (!SemiFunc.IsMasterClientOrSingleplayer())
{
return;
}
if (_enemiesList == __instance.enemiesDifficulty1)
{
currentDifficultyPick = 1;
}
if (_enemiesList == __instance.enemiesDifficulty2)
{
currentDifficultyPick = 2;
}
if (_enemiesList == __instance.enemiesDifficulty3)
{
currentDifficultyPick = 3;
}
SpawnConfig.Logger.LogInfo((object)("Picking difficulty " + currentDifficultyPick + " setup..."));
SpawnConfig.Logger.LogInfo((object)"Enemy group weights:");
int num = DataDirector.instance.SettingValueFetch((Setting)31);
List<EnemySetup> list = new List<EnemySetup>();
float num2 = 0f;
foreach (EnemySetup _enemies in _enemiesList)
{
if ((!_enemies.levelsCompletedCondition || (RunManager.instance.levelsCompleted >= _enemies.levelsCompletedMin && RunManager.instance.levelsCompleted <= _enemies.levelsCompletedMax)) && num >= _enemies.runsPlayed)
{
float num3 = 1f;
if (ListManager.extendedSetups.ContainsKey(((Object)_enemies).name))
{
num3 = ListManager.extendedSetups[((Object)_enemies).name].GetWeight(currentDifficultyPick, __instance.enemyList);
}
if (!(num3 < 1f))
{
num2 += num3;
list.Add(_enemies);
SpawnConfig.Logger.LogInfo((object)(((Object)_enemies).name + " = " + num3));
}
}
}
EnemySetup val = null;
float num4 = Random.Range(1f, num2);
SpawnConfig.Logger.LogInfo((object)("Selecting a group based on random number " + num4 + "..."));
foreach (EnemySetup item in list)
{
float num5 = 1f;
if (ListManager.extendedSetups.ContainsKey(((Object)item).name))
{
num5 = ListManager.extendedSetups[((Object)item).name].GetWeight(currentDifficultyPick, __instance.enemyList);
}
SpawnConfig.Logger.LogDebug((object)("=> " + ((Object)item).name + " = " + num5 + " / " + num4));
if (num5 >= num4)
{
SpawnConfig.Logger.LogInfo((object)("Selected: " + ((Object)item).name));
if (onlyOneSetup)
{
val = ScriptableObject.CreateInstance<EnemySetup>();
((Object)val).name = ((Object)item).name;
val.spawnObjects = new List<GameObject>();
}
else
{
val = item;
}
break;
}
num4 -= num5;
}
if (ListManager.extendedSetups.ContainsKey(((Object)val).name) && ListManager.extendedSetups[((Object)val).name].thisGroupOnly && !onlyOneSetup)
{
List<string> list2 = new List<string>();
int count = __instance.enemyList.Count;
foreach (EnemySetup enemy in __instance.enemyList)
{
list2.Add(((Object)enemy).name);
}
__instance.enemyList.Clear();
__instance.enemyList.Add(val);
onlyOneSetup = true;
for (int i = 0; i < count; i++)
{
EnemySetup val2 = ScriptableObject.CreateInstance<EnemySetup>();
((Object)val2).name = list2[i];
val2.spawnObjects = new List<GameObject>();
__instance.enemyList.Add(val2);
}
}
else
{
__instance.enemyList.Add(val);
}
}
}
[HarmonyPatch(typeof(LevelGenerator))]
public class LevelGeneratorPatch
{
public static int PickNonDirector(EnemySetup enemySetup)
{
int num = -1;
int num2 = 0;
int num3 = enemySetup.spawnObjects.Count;
while (num == -1)
{
int num4 = Random.Range(num2, num3);
if (!((Object)enemySetup.spawnObjects[num4]).name.Contains("Director"))
{
num = num4;
}
else if (num2 == num3)
{
num = num2;
}
else if (num4 == num2)
{
num2++;
}
else if (num4 == num3)
{
num3--;
}
}
return num;
}
[HarmonyPatch("EnemySpawn")]
[HarmonyPrefix]
public static void LogAndModifySpawns(EnemySetup enemySetup, Vector3 position, LevelGenerator __instance)
{
bool flag = false;
bool flag2 = false;
int num = 0;
foreach (GameObject spawnObject in enemySetup.spawnObjects)
{
if (((Object)spawnObject).name.Contains("Gnome") && !flag)
{
flag = true;
num++;
}
else if (((Object)spawnObject).name.Contains("Bang") && !flag2)
{
flag2 = true;
num++;
}
}
if (flag)
{
enemySetup.spawnObjects.Insert(0, ListManager.spawnObjectsDict["Enemy - Gnome Director"]);
}
if (flag2)
{
enemySetup.spawnObjects.Insert(0, ListManager.spawnObjectsDict["Enemy - Bang Director"]);
}
if (ListManager.extendedSetups.ContainsKey(((Object)enemySetup).name))
{
int num2 = (int)Math.Round(1.0 / ListManager.extendedSetups[((Object)enemySetup).name].alterAmountChance);
if (num2 < 1)
{
num2 = 1;
}
int num3 = Random.Range(1, num2 + 1);
if (num3 <= 1 && enemySetup.spawnObjects.Count > 0)
{
int i = Random.Range(ListManager.extendedSetups[((Object)enemySetup).name].alterAmountMin, ListManager.extendedSetups[((Object)enemySetup).name].alterAmountMax + 1);
if (i > 0)
{
while (i > 0)
{
enemySetup.spawnObjects.Add(enemySetup.spawnObjects[PickNonDirector(enemySetup)]);
i--;
}
}
else if (i < 0)
{
for (; i < 0; i++)
{
if (enemySetup.spawnObjects.Count <= 0)
{
break;
}
enemySetup.spawnObjects.RemoveAt(PickNonDirector(enemySetup));
}
}
}
}
Dictionary<string, int> dictionary = new Dictionary<string, int>();
foreach (GameObject spawnObject2 in enemySetup.spawnObjects)
{
if (dictionary.ContainsKey(((Object)spawnObject2).name))
{
dictionary[((Object)spawnObject2).name] = dictionary[((Object)spawnObject2).name] + 1;
}
else
{
dictionary.Add(((Object)spawnObject2).name, 1);
}
}
string text = "";
foreach (KeyValuePair<string, int> item in dictionary)
{
if (!item.Key.Contains("Director"))
{
if (text != "")
{
text += ", ";
}
text = text + item.Key + " x " + item.Value;
}
}
if (text == "")
{
text = "No spawn objects found in group...";
}
if (SpawnConfig.configManager.preventSpawns.Value)
{
enemySetup.spawnObjects.Clear();
SpawnConfig.Logger.LogInfo((object)"Forcibly prevented enemy spawn!");
return;
}
SpawnConfig.Logger.LogInfo((object)("Spawning [" + ((Object)enemySetup).name + "] (" + text.Replace("Enemy - ", "") + ")"));
}
}
}
namespace SpawnConfig.ExtendedClasses
{
public class ExtendedEnemySetup
{
public string name = "Nameless";
public bool levelsCompletedCondition = false;
public int levelsCompletedMax = 10;
public int levelsCompletedMin = 0;
public bool levelRangeCondition = false;
public int minLevel = 0;
public int maxLevel = 0;
public int runsPlayed = 0;
public List<string> spawnObjects = new List<string>();
public float difficulty1Weight = 0f;
public float difficulty2Weight = 0f;
public float difficulty3Weight = 0f;
public bool thisGroupOnly = false;
public bool allowDuplicates = true;
public double alterAmountChance = 0.0;
public int alterAmountMin = 0;
public int alterAmountMax = 0;
public ExtendedEnemySetup()
{
}
public ExtendedEnemySetup(EnemySetup enemySetup, int difficulty)
{
name = ((Object)enemySetup).name;
if (enemySetup.levelsCompletedCondition)
{
levelRangeCondition = true;
minLevel = enemySetup.levelsCompletedMin + 1;
maxLevel = enemySetup.levelsCompletedMax + 1;
}
runsPlayed = enemySetup.runsPlayed;
spawnObjects = (from obj in enemySetup.spawnObjects
where !((Object)obj).name.Contains("Director")
select ((Object)obj).name).ToList();
float num = 100f;
if (Object.op_Implicit((Object)(object)enemySetup.rarityPreset))
{
num = (float)Math.Round(-2.08f * (100f - enemySetup.rarityPreset.chance) + 98.45f);
if (num > 100f)
{
num = 100f;
}
if (num == 98f)
{
num = 100f;
}
if (num <= 15f && num > 2f)
{
num = 5f;
}
if (num < 2f)
{
num = 2f;
}
SpawnConfig.Logger.LogDebug((object)(name + " (rarity) = " + enemySetup.rarityPreset.chance));
}
difficulty1Weight = ((difficulty == 1) ? num : 0f);
difficulty2Weight = ((difficulty == 2) ? num : 0f);
difficulty3Weight = ((difficulty == 3) ? num : 0f);
}
public EnemySetup GetEnemySetup()
{
EnemySetup val = ScriptableObject.CreateInstance<EnemySetup>();
((Object)val).name = name;
val.spawnObjects = new List<GameObject>();
val.levelsCompletedCondition = levelRangeCondition;
val.levelsCompletedMin = minLevel - 1;
val.levelsCompletedMax = maxLevel - 1;
val.runsPlayed = runsPlayed;
foreach (string spawnObject in spawnObjects)
{
val.spawnObjects.Add(ListManager.spawnObjectsDict[spawnObject]);
}
return val;
}
public float GetWeight(int difficulty, List<EnemySetup> enemyList)
{
float num = difficulty1Weight;
switch (difficulty)
{
case 2:
num = difficulty2Weight;
break;
case 3:
num = difficulty3Weight;
break;
}
if (enemyList.Select((EnemySetup obj) => ((Object)obj).name).ToList().Contains(name))
{
num = (float)((double)num * SpawnConfig.configManager.repeatMultiplier.Value);
if (!allowDuplicates)
{
num = 0f;
}
}
if (num < 0f)
{
num = 0f;
}
return num;
}
public void UpdateWithDefaults(ExtendedEnemySetup defaultSetup)
{
PropertyInfo[] properties = defaultSetup.GetType().GetProperties();
PropertyInfo[] array = properties;
foreach (PropertyInfo propertyInfo in array)
{
object value = propertyInfo.GetValue(defaultSetup);
object value2 = propertyInfo.GetValue(this);
object value3 = propertyInfo.GetValue(ListManager.extendedSetups[defaultSetup.name]);
if (value == value2 && value3 != value)
{
SpawnConfig.Logger.LogInfo((object)("Updating unmodified property " + propertyInfo?.ToString() + ": " + value?.ToString() + " => " + value3));
propertyInfo.SetValue(this, value3);
}
}
}
public bool Update()
{
bool result = false;
if (levelsCompletedCondition)
{
levelRangeCondition = true;
minLevel = levelsCompletedMin;
maxLevel = levelsCompletedMax;
levelsCompletedCondition = false;
result = true;
}
if (!levelRangeCondition && maxLevel == 10)
{
maxLevel = 0;
result = true;
}
return result;
}
}
public class ExtendedGroupCounts
{
public int level = 1;
public List<GroupCountEntry> possibleGroupCounts = new List<GroupCountEntry>();
public ExtendedGroupCounts(int i)
{
level = ListManager.levelNumbers[i];
possibleGroupCounts.Add(new GroupCountEntry(i));
}
}
public class GroupCountEntry
{
public List<int> counts = new List<int>();
public int weight = 1;
public GroupCountEntry(int i)
{
counts = new List<int>(3)
{
ListManager.difficulty1Counts[i],
ListManager.difficulty2Counts[i],
ListManager.difficulty3Counts[i]
};
}
}
public class ExtendedSpawnObject
{
public string name = "Nameless";
public bool disabled = false;
public int biggerGroupChance = 0;
public int groupIncreaseAmount = 0;
[JsonIgnore]
public bool alteredGroupSize = false;
public ExtendedSpawnObject(GameObject spawnObject)
{
name = ((Object)spawnObject).name;
}
public GameObject GetSpawnObject()
{
return ListManager.spawnObjectsDict[name];
}
}
}