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 Enabled; internal static ConfigEntry CheckIntervalSeconds; internal static ConfigEntry AfterTriggerCooldownSeconds; internal static ConfigEntry RespawnCount; internal static ConfigEntry RespawnTimerSeconds; internal static ConfigEntry IgnoreAfterExtractionCompleted; internal static ConfigEntry 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("General", "Enabled", true, "Enable this mod.このMODを有効にします"); CheckIntervalSeconds = ((BaseUnityPlugin)this).Config.Bind("General", "CheckIntervalSeconds", 5f, new ConfigDescription("Interval in seconds for checking whether alive enemies are zero.生きている敵が0体か確認する間隔", (AcceptableValueBase)(object)new AcceptableValueRange(0.5f, 60f), Array.Empty())); AfterTriggerCooldownSeconds = ((BaseUnityPlugin)this).Config.Bind("General", "AfterTriggerCooldownSeconds", 20f, new ConfigDescription("Delay in seconds before the next zero-enemy check after respawn timers are shortened.リスポーンタイマー短縮後に次の敵0体確認まで待つ秒数", (AcceptableValueBase)(object)new AcceptableValueRange(0f, 300f), Array.Empty())); RespawnCount = ((BaseUnityPlugin)this).Config.Bind("General", "RespawnCount", 1, new ConfigDescription("Number of despawned enemies whose respawn timer will be shortened.リスポーンタイマーを短縮するデスポーン中の敵数", (AcceptableValueBase)(object)new AcceptableValueRange(1, 20), Array.Empty())); RespawnTimerSeconds = ((BaseUnityPlugin)this).Config.Bind("General", "RespawnTimerSeconds", 5f, new ConfigDescription("Respawn timer value in seconds applied to selected enemies.選択された敵に設定するリスポーンタイマー秒数", (AcceptableValueBase)(object)new AcceptableValueRange(1f, 60f), Array.Empty())); IgnoreAfterExtractionCompleted = ((BaseUnityPlugin)this).Config.Bind("General", "IgnoreAfterExtractionCompleted", false, "Do not shorten enemy respawn timers after all extraction points are completed.すべての抽出ポイント完了後は敵のリスポーンタイマーを短縮しません"); DebugLogEnabled = ((BaseUnityPlugin)this).Config.Bind("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 SpawnedRef = AccessTools.FieldRefAccess("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 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 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 BuildRespawnCandidates(List enemies, float targetTimer) { List list = new List(); 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 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; } } }