Decompiled source of EndlessEnemy v4.0.0

EndlessEnemy.dll

Decompiled 2 days ago
using System;
using System.Collections.Generic;
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.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: IgnoresAccessChecksTo("")]
[assembly: AssemblyCompany("REPOJP")]
[assembly: AssemblyConfiguration("Debug")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("zabuMod")]
[assembly: AssemblyTitle("zabuMod")]
[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 EndlessEnemy
{
	[BepInPlugin("REPO_JP.EndlessEnemy", "EndlessEnemy", "4.0.0")]
	public sealed class EndlessEnemyPlugin : BaseUnityPlugin
	{
		private const string PluginGuid = "REPO_JP.EndlessEnemy";

		private const string PluginName = "EndlessEnemy";

		private const string PluginVersion = "4.0.0";

		internal static ManualLogSource Log;

		internal static ConfigEntry<bool> Enabled;

		internal static ConfigEntry<float> CheckIntervalSeconds;

		internal static ConfigEntry<float> AfterTriggerCooldownSeconds;

		internal static ConfigEntry<int> RespawnCount;

		internal static ConfigEntry<float> RespawnTimerSeconds;

		internal static ConfigEntry<bool> IgnoreAfterExtractionCompleted;

		internal static ConfigEntry<bool> DebugLogEnabled;

		private Harmony harmony;

		private void Awake()
		{
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			BindConfig();
			harmony = new Harmony("REPO_JP.EndlessEnemy");
			harmony.PatchAll();
			Log.LogInfo((object)"EndlessEnemy v4.0.0 loaded");
		}

		private void OnDestroy()
		{
			Harmony obj = harmony;
			if (obj != null)
			{
				obj.UnpatchSelf();
			}
			harmony = null;
		}

		private void BindConfig()
		{
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: Expected O, but got Unknown
			//IL_008c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0096: Expected O, but got Unknown
			//IL_00be: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c8: Expected O, but got Unknown
			//IL_00fb: Unknown result type (might be due to invalid IL or missing references)
			//IL_0105: Expected O, but got Unknown
			Enabled = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enabled", true, "Enable this mod.このMODを有効にします");
			CheckIntervalSeconds = ((BaseUnityPlugin)this).Config.Bind<float>("General", "CheckIntervalSeconds", 5f, new ConfigDescription("Interval in seconds for checking whether alive enemies are zero.生きている敵が0体か確認する間隔", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.5f, 60f), Array.Empty<object>()));
			AfterTriggerCooldownSeconds = ((BaseUnityPlugin)this).Config.Bind<float>("General", "AfterTriggerCooldownSeconds", 20f, new ConfigDescription("Delay in seconds before the next zero-enemy check after respawn timers are shortened.リスポーンタイマー短縮後に次の敵0体確認まで待つ秒数", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 300f), Array.Empty<object>()));
			RespawnCount = ((BaseUnityPlugin)this).Config.Bind<int>("General", "RespawnCount", 1, new ConfigDescription("Number of despawned enemies whose respawn timer will be shortened.リスポーンタイマーを短縮するデスポーン中の敵数", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 20), Array.Empty<object>()));
			RespawnTimerSeconds = ((BaseUnityPlugin)this).Config.Bind<float>("General", "RespawnTimerSeconds", 5f, new ConfigDescription("Respawn timer value in seconds applied to selected enemies.選択された敵に設定するリスポーンタイマー秒数", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 60f), Array.Empty<object>()));
			IgnoreAfterExtractionCompleted = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "IgnoreAfterExtractionCompleted", false, "Do not shorten enemy respawn timers after all extraction points are completed.すべての抽出ポイント完了後は敵のリスポーンタイマーを短縮しません");
			DebugLogEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "DebugLogEnabled", false, "Log enemy names when their respawn timer is shortened.リスポーンタイマーを短縮した敵名をログ出力します");
		}
	}
	[HarmonyPatch(typeof(EnemyDirector), "Update")]
	internal static class EnemyDirectorUpdatePatch
	{
		private static float nextCheckTime;

		private static int lastLevelGeneratorInstanceId;

		private static void Postfix(EnemyDirector __instance)
		{
			EnemyRespawnController.Tick(__instance, ref nextCheckTime, ref lastLevelGeneratorInstanceId);
		}
	}
	internal static class EnemyRespawnController
	{
		private static readonly FieldRef<EnemyParent, bool> SpawnedRef = AccessTools.FieldRefAccess<EnemyParent, bool>("Spawned");

		internal static void Tick(EnemyDirector director, ref float nextCheckTime, ref int lastLevelGeneratorInstanceId)
		{
			try
			{
				if (Time.time < nextCheckTime)
				{
					return;
				}
				float num = Mathf.Max(0.5f, EndlessEnemyPlugin.CheckIntervalSeconds.Value);
				float num2 = Mathf.Max(0f, EndlessEnemyPlugin.AfterTriggerCooldownSeconds.Value);
				int targetCount = Mathf.Clamp(EndlessEnemyPlugin.RespawnCount.Value, 1, 20);
				float targetTimer = Mathf.Clamp(EndlessEnemyPlugin.RespawnTimerSeconds.Value, 1f, 60f);
				if (!EndlessEnemyPlugin.Enabled.Value)
				{
					nextCheckTime = Time.time + num;
					return;
				}
				nextCheckTime = Time.time + num;
				if (!SemiFunc.IsMasterClientOrSingleplayer() || (Object)(object)director == (Object)null || director.enemiesSpawned == null || (Object)(object)LevelGenerator.Instance == (Object)null || !LevelGenerator.Instance.Generated)
				{
					return;
				}
				int instanceID = ((Object)LevelGenerator.Instance).GetInstanceID();
				if (instanceID != lastLevelGeneratorInstanceId)
				{
					lastLevelGeneratorInstanceId = instanceID;
					nextCheckTime = Time.time + num;
				}
				else
				{
					if ((EndlessEnemyPlugin.IgnoreAfterExtractionCompleted.Value && (Object)(object)RoundDirector.instance != (Object)null && RoundDirector.instance.allExtractionPointsCompleted) || HasAnySpawnedEnemy(director.enemiesSpawned))
					{
						return;
					}
					List<EnemyParent> list = BuildRespawnCandidates(director.enemiesSpawned, targetTimer);
					if (list.Count != 0)
					{
						int num3 = ShortenShortestTimers(list, targetCount, targetTimer);
						if (num3 > 0)
						{
							nextCheckTime = Time.time + num2;
						}
					}
				}
			}
			catch (Exception arg)
			{
				EndlessEnemyPlugin.Log.LogError((object)$"EndlessEnemy error: {arg}");
				nextCheckTime = Time.time + 5f;
			}
		}

		private static bool HasAnySpawnedEnemy(List<EnemyParent> enemies)
		{
			for (int i = 0; i < enemies.Count; i++)
			{
				EnemyParent val = enemies[i];
				if (!((Object)(object)val == (Object)null) && IsSpawned(val))
				{
					return true;
				}
			}
			return false;
		}

		private static List<EnemyParent> BuildRespawnCandidates(List<EnemyParent> enemies, float targetTimer)
		{
			List<EnemyParent> list = new List<EnemyParent>();
			for (int i = 0; i < enemies.Count; i++)
			{
				EnemyParent val = enemies[i];
				if (!((Object)(object)val == (Object)null) && !IsSpawned(val) && val.DespawnedTimer > targetTimer)
				{
					list.Add(val);
				}
			}
			return list;
		}

		private static int ShortenShortestTimers(List<EnemyParent> candidates, int targetCount, float targetTimer)
		{
			int num = 0;
			for (int i = 0; i < targetCount; i++)
			{
				int num2 = -1;
				float num3 = float.MaxValue;
				for (int j = 0; j < candidates.Count; j++)
				{
					EnemyParent val = candidates[j];
					if (!((Object)(object)val == (Object)null) && !IsSpawned(val) && !(val.DespawnedTimer <= targetTimer) && val.DespawnedTimer < num3)
					{
						num3 = val.DespawnedTimer;
						num2 = j;
					}
				}
				if (num2 < 0)
				{
					break;
				}
				EnemyParent val2 = candidates[num2];
				candidates.RemoveAt(num2);
				float despawnedTimer = val2.DespawnedTimer;
				val2.DespawnedTimerSet(targetTimer, false);
				num++;
				if (EndlessEnemyPlugin.DebugLogEnabled.Value)
				{
					EndlessEnemyPlugin.Log.LogInfo((object)$"Shortened enemy respawn timer: {GetEnemyName(val2)} {despawnedTimer:F1}s -> {targetTimer:F1}s");
				}
			}
			return num;
		}

		private static bool IsSpawned(EnemyParent enemy)
		{
			return SpawnedRef.Invoke(enemy);
		}

		private static string GetEnemyName(EnemyParent enemy)
		{
			if ((Object)(object)enemy == (Object)null)
			{
				return "Unknown Enemy";
			}
			if (!string.IsNullOrWhiteSpace(enemy.enemyName))
			{
				return enemy.enemyName;
			}
			return ((Object)enemy).name;
		}
	}
}