using System; using System.Collections.Generic; using System.Diagnostics; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: AssemblyTitle("ClassLibrary3")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("ClassLibrary3")] [assembly: AssemblyCopyright("Copyright © 2026")] [assembly: AssemblyTrademark("")] [assembly: ComVisible(false)] [assembly: Guid("b9a6c547-5fae-4c80-847d-8da4b933a5dd")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyVersion("1.0.0.0")] namespace COTLMultiplayerEnemySpawnScaling; internal static class EnemySpawnPatches { private sealed class SpawnScaleCloneMarker : MonoBehaviour { public int RoomToken; } private const string DifficultyPlayerPrefsKey = "COTLMP.8PDifficulty"; private static bool _spawningScaledCopy = false; private static float _extraSpawnCarry = 0f; private static float _nextSkipLogAt = 0f; private static float _nextHookLogAt = 0f; private static float _nextRejectLogAt = 0f; private static int _roomToken = 0; private static readonly HashSet _scaledEncounterInstanceIds = new HashSet(); private static readonly HashSet _scaledSpawnInstanceIds = new HashSet(); private static readonly HashSet _scaledBossHealthIds = new HashSet(); private static readonly Type DungeonLeaderMechanicsType = AccessTools.TypeByName("DungeonLeaderMechanics"); private static readonly Type DemonType = AccessTools.TypeByName("Demon"); private static readonly Type FriendlyEnemyType = AccessTools.TypeByName("FriendlyEnemy"); private static int GetActualPlayerCount() { int num = 0; if (PlayerFarming.players != null) { foreach (PlayerFarming player in PlayerFarming.players) { if ((Object)(object)player != (Object)null && (Object)(object)((Component)player).gameObject != (Object)null && ((Component)player).gameObject.activeInHierarchy) { num++; } } } return Mathf.Clamp(Mathf.Max(num, PlayerFarming.playersCount), 1, 8); } public static void EnemyRoundsBase_RoundStarted_Prefix(int round, int totalRounds) { //IL_0096: Unknown result type (might be due to invalid IL or missing references) bool flag = round <= 1 || _roomToken == 0; if (flag) { _roomToken++; _scaledEncounterInstanceIds.Clear(); _scaledSpawnInstanceIds.Clear(); _scaledBossHealthIds.Clear(); } _extraSpawnCarry = 0f; EnemySpawnScalingPlugin.Log.LogInfo((object)$"[SpawnScale] round start {round}/{totalRounds} token={_roomToken} newRoomToken={flag} (players={GetActualPlayerCount()}, coop={GetCoopStateForLog()}, loc={PlayerFarming.Location})"); } public static void EnemySpawner_Create_Postfix(Vector3 Position, Transform Parent, GameObject Spawn) { //IL_0014: Unknown result type (might be due to invalid IL or missing references) LogHookSeen("Create"); Debug.Log((object)"[SPAWNSCALE_HOOK_017] Create"); TrySpawnScaledCopies(Position, Parent, Spawn, "Create"); } public static void EnemySpawner_CreateWithAndInit_Postfix(Vector3 Position, Transform Parent, GameObject Spawn) { //IL_0014: Unknown result type (might be due to invalid IL or missing references) LogHookSeen("CreateWithInit"); Debug.Log((object)"[SPAWNSCALE_HOOK_017] CreateWithInit"); TrySpawnScaledCopies(Position, Parent, Spawn, "CreateWithInit"); } public static void EnemySpawner_InitAndInstantiate_Postfix(EnemySpawner __instance, GameObject __0) { LogHookSeen("InitAndInstantiate"); Debug.Log((object)"[SPAWNSCALE_HOOK_017] InitAndInstantiate"); } public static void EnemySpawner_Init_Postfix(EnemySpawner __instance, GameObject __0) { LogHookSeen("Init"); Debug.Log((object)"[SPAWNSCALE_HOOK_017] Init"); } public static void EnemyRoundsBase_AddEnemyToRound_Postfix(Health e) { if (!((Object)(object)e == (Object)null)) { LogHookSeen("AddEnemyToRound"); } } public static void EnemyEncounterChanceEvents_AssignShieldsAndGroups_Postfix(EnemyEncounterChanceEvents __instance) { //IL_02e2: Unknown result type (might be due to invalid IL or missing references) //IL_023c: Unknown result type (might be due to invalid IL or missing references) //IL_0243: Unknown result type (might be due to invalid IL or missing references) //IL_025c: Unknown result type (might be due to invalid IL or missing references) //IL_00dc: Unknown result type (might be due to invalid IL or missing references) //IL_00e2: Invalid comparison between Unknown and I4 if ((Object)(object)__instance == (Object)null) { return; } if (!ShouldScaleDungeonSpawns(out var blockedReason)) { LogSkip("encounter_gate=" + blockedReason); return; } int num = (((Object)(object)RoomManager.r != (Object)null) ? ((object)RoomManager.r).GetHashCode() : 0); int item = (((Object)((Component)__instance).gameObject).GetInstanceID() * 397) ^ num; if (!_scaledEncounterInstanceIds.Add(item)) { return; } UnitObject[] componentsInChildren; try { componentsInChildren = ((Component)__instance).GetComponentsInChildren(true); } catch { return; } if (componentsInChildren == null || componentsInChildren.Length == 0) { return; } List list = new List(); UnitObject[] array = componentsInChildren; foreach (UnitObject val in array) { if (!((Object)(object)val == (Object)null) && !((Object)(object)((Component)val).gameObject == (Object)null) && ((Component)val).gameObject.activeInHierarchy && !((Object)(object)val.health == (Object)null) && (int)val.health.team == 2 && !((Object)(object)((Component)val).GetComponent() != (Object)null) && !IsHealthScaleOnlyEnemy(((Component)val).gameObject) && !((Object)(object)((Component)val).GetComponent() != (Object)null) && !((Object)(object)((Component)val).GetComponent() != (Object)null) && !HasComponentInChildren(((Component)val).gameObject, DungeonLeaderMechanicsType) && !HasComponentInChildren(((Component)val).gameObject, DemonType) && !HasComponentInChildren(((Component)val).gameObject, FriendlyEnemyType)) { list.Add(val); } } if (list.Count == 0) { return; } int roomExtraCount = GetRoomExtraCount(list.Count); if (roomExtraCount <= 0) { return; } int num2 = 0; for (int j = 0; j < roomExtraCount; j++) { UnitObject val2 = list[j % list.Count]; if (!((Object)(object)val2 == (Object)null) && !((Object)(object)((Component)val2).gameObject == (Object)null)) { try { GameObject val3 = Object.Instantiate(((Component)val2).gameObject, ((Component)val2).transform.parent); ((Object)val3).name = ((Object)((Component)val2).gameObject).name + "_SpawnScale"; val3.AddComponent().RoomToken = _roomToken; SanitizeClone(val3); _scaledSpawnInstanceIds.Add(((Object)val3).GetInstanceID()); val3.transform.position = GetNearbySpawnPoint(((Component)val2).transform.position, j); val3.SetActive(false); EnemySpawner.CreateWithAndInitInstantiatedEnemy(val3.transform.position, val3.transform.parent, val3); num2++; } catch (Exception ex) { EnemySpawnScalingPlugin.Log.LogWarning((object)("[SpawnScale] room clone failed: " + ex.Message)); } } } EnemySpawnScalingPlugin.Log.LogInfo((object)$"[SpawnScale] room-scale eligible={list.Count} spawnedExtra={num2} players={GetActualPlayerCount()} loc={PlayerFarming.Location}"); } private static void TrySpawnScaledCopies(Vector3 position, Transform parent, GameObject spawnPrefab, string source) { //IL_0094: Unknown result type (might be due to invalid IL or missing references) //IL_0096: Unknown result type (might be due to invalid IL or missing references) if (_spawningScaledCopy || (Object)(object)spawnPrefab == (Object)null) { return; } int instanceID = ((Object)spawnPrefab).GetInstanceID(); string blockedReason; if (_scaledSpawnInstanceIds.Remove(instanceID)) { LogReject("scaled_copy_source"); } else if (IsHealthScaleOnlyEnemy(spawnPrefab)) { LogReject("boss_health_owned_by_main"); } else if ((Object)(object)EnemyRoundsBase.Instance == (Object)null) { LogSkip("static_room_owned_by_encounter"); } else if (!ShouldScaleDungeonSpawns(out blockedReason)) { LogSkip("gate=" + blockedReason); } else { if (!IsEligibleEnemyPrefab(spawnPrefab)) { return; } int extraSpawnsForThisEnemy = GetExtraSpawnsForThisEnemy(); if (extraSpawnsForThisEnemy <= 0) { return; } try { _spawningScaledCopy = true; for (int i = 0; i < extraSpawnsForThisEnemy; i++) { Vector3 nearbySpawnPoint = GetNearbySpawnPoint(position, i); GameObject val = Object.Instantiate(spawnPrefab, parent); val.AddComponent().RoomToken = _roomToken; _scaledSpawnInstanceIds.Add(((Object)val).GetInstanceID()); val.SetActive(false); EnemySpawner.CreateWithAndInitInstantiatedEnemy(nearbySpawnPoint, parent, val); } EnemySpawnScalingPlugin.Log.LogInfo((object)$"[SpawnScale] source={source} players={GetActualPlayerCount()} spawnedExtra={extraSpawnsForThisEnemy} carry={_extraSpawnCarry:0.00}"); } catch (Exception ex) { EnemySpawnScalingPlugin.Log.LogWarning((object)("[SpawnScale] Failed to spawn scaled enemy copy: " + ex.Message)); } finally { _spawningScaledCopy = false; } } } private static bool ShouldScaleDungeonSpawns(out string blockedReason) { //IL_0018: Unknown result type (might be due to invalid IL or missing references) blockedReason = string.Empty; if (GetActualPlayerCount() <= 2) { blockedReason = "players_le_2"; return false; } if (!GameManager.IsDungeon(PlayerFarming.Location)) { blockedReason = "not_dungeon"; return false; } if ((Object)(object)EnemyRoundsBase.Instance != (Object)null && EnemyRoundsBase.Instance.Completed) { blockedReason = "rounds_completed"; return false; } return true; } private static bool IsEligibleEnemyPrefab(GameObject spawnPrefab) { //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Invalid comparison between Unknown and I4 Health component = spawnPrefab.GetComponent(); if ((Object)(object)component == (Object)null) { LogReject("no_health"); return false; } if ((int)component.team != 2) { LogReject("team_" + ((object)Unsafe.As(ref component.team)/*cast due to .constrained prefix*/).ToString()); return false; } if (HasComponentInChildren(spawnPrefab, DungeonLeaderMechanicsType)) { LogReject("dungeon_leader"); return false; } if (HasComponentInChildren(spawnPrefab, DemonType)) { LogReject("demon"); return false; } if (HasComponentInChildren(spawnPrefab, FriendlyEnemyType)) { LogReject("friendly_enemy"); return false; } return true; } private static bool TryScaleBossLikeHealth(GameObject go, string source) { if ((Object)(object)go == (Object)null) { return false; } if (!IsHealthScaleOnlyEnemy(go)) { return false; } Health component = go.GetComponent(); if ((Object)(object)component == (Object)null) { return false; } int instanceID = ((Object)go).GetInstanceID(); if (!_scaledBossHealthIds.Add(instanceID)) { return true; } float num = 1f + 0.5f * (float)Mathf.Max(0, GetActualPlayerCount() - 2); if (num <= 1f) { return true; } component.totalHP *= num; component.HP *= num; EnemySpawnScalingPlugin.Log.LogInfo((object)$"[SpawnScale] boss-health source={source} enemy={GetEnemyName(go)} mult={num:0.00} players={GetActualPlayerCount()}"); return true; } private static bool IsHealthScaleOnlyEnemy(GameObject go) { //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)go == (Object)null) { return false; } if (HasComponentInChildren(go, DungeonLeaderMechanicsType)) { return true; } UnitObject component = go.GetComponent(); if ((Object)(object)component == (Object)null) { return false; } string text = ((object)component.EnemyType/*cast due to .constrained prefix*/).ToString(); if (string.IsNullOrEmpty(text)) { return false; } if ((text.IndexOf("MiniBoss", StringComparison.OrdinalIgnoreCase) >= 0 || text.IndexOf("Miniboss", StringComparison.OrdinalIgnoreCase) >= 0) && EnemySpawnScalingPlugin.DuplicateMiniBosses != null && EnemySpawnScalingPlugin.DuplicateMiniBosses.Value) { return false; } if (text.IndexOf("Boss", StringComparison.OrdinalIgnoreCase) >= 0) { return true; } return false; } private static string GetEnemyName(GameObject go) { //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) UnitObject val = (((Object)(object)go != (Object)null) ? go.GetComponent() : null); if (!((Object)(object)val != (Object)null)) { return "Unknown"; } return ((object)val.EnemyType/*cast due to .constrained prefix*/).ToString(); } private static void LogSkip(string reason) { //IL_0045: Unknown result type (might be due to invalid IL or missing references) float unscaledTime = Time.unscaledTime; if (!(unscaledTime < _nextSkipLogAt)) { _nextSkipLogAt = unscaledTime + 2f; EnemySpawnScalingPlugin.Log.LogInfo((object)$"[SpawnScale] skip {reason} (players={GetActualPlayerCount()}, coop={GetCoopStateForLog()}, loc={PlayerFarming.Location})"); } } private static void LogHookSeen(string hook) { //IL_0045: Unknown result type (might be due to invalid IL or missing references) float unscaledTime = Time.unscaledTime; if (!(unscaledTime < _nextHookLogAt)) { _nextHookLogAt = unscaledTime + 1.5f; EnemySpawnScalingPlugin.Log.LogInfo((object)$"[SpawnScale] hook={hook} seen (players={GetActualPlayerCount()}, coop={GetCoopStateForLog()}, loc={PlayerFarming.Location})"); } } private static void LogReject(string reason) { //IL_0045: Unknown result type (might be due to invalid IL or missing references) float unscaledTime = Time.unscaledTime; if (!(unscaledTime < _nextRejectLogAt)) { _nextRejectLogAt = unscaledTime + 2f; EnemySpawnScalingPlugin.Log.LogInfo((object)$"[SpawnScale] reject {reason} (players={GetActualPlayerCount()}, coop={GetCoopStateForLog()}, loc={PlayerFarming.Location})"); } } private static string GetCoopStateForLog() { if ((Object)(object)CoopManager.Instance == (Object)null) { return "null"; } if (!CoopManager.CoopActive) { return "inactive"; } return "active"; } private static bool HasComponentInChildren(GameObject go, Type componentType) { if ((Object)(object)go == (Object)null || componentType == null) { return false; } try { return (Object)(object)go.GetComponentInChildren(componentType, true) != (Object)null; } catch { return false; } } private static int GetExtraSpawnsForThisEnemy() { int actualPlayerCount = GetActualPlayerCount(); if (actualPlayerCount <= 2) { return 0; } float num = GetTargetEnemyMultiplier(actualPlayerCount) - 1f; _extraSpawnCarry += num; int num2 = Mathf.FloorToInt(_extraSpawnCarry + 0.0001f); if (num2 > 0) { _extraSpawnCarry -= num2; } return Mathf.Clamp(num2, 0, 4); } private static int GetRoomExtraCount(int baseCount) { if (baseCount <= 0) { return 0; } float targetEnemyMultiplier = GetTargetEnemyMultiplier(GetActualPlayerCount()); return Mathf.Clamp(Mathf.CeilToInt((float)baseCount * targetEnemyMultiplier) - baseCount, 0, 24); } internal static float GetTargetEnemyMultiplier(int playerCount) { if (playerCount <= 2) { return 1f; } switch (Mathf.Clamp(PlayerPrefs.GetInt("COTLMP.8PDifficulty", 1), 0, 3)) { case 0: if (playerCount <= 4) { if (playerCount != 3) { return 1.25f; } return 1.1f; } return Mathf.Lerp(1.5f, 2f, Mathf.Clamp01((float)(playerCount - 5) / 3f)); case 1: if (playerCount <= 4) { if (playerCount != 3) { return 1.6f; } return 1.25f; } return Mathf.Lerp(2f, 3f, Mathf.Clamp01((float)(playerCount - 5) / 3f)); case 2: return 1f + 0.5f * (float)(playerCount - 2); default: return 1.5f + 0.5f * (float)(playerCount - 2); } } internal static string GetDifficultyName() { return Mathf.Clamp(PlayerPrefs.GetInt("COTLMP.8PDifficulty", 1), 0, 3) switch { 0 => "EASY", 2 => "HARD", 3 => "EXTREME", _ => "NORMAL", }; } private static Vector3 GetNearbySpawnPoint(Vector3 origin, int index) { //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Unknown result type (might be due to invalid IL or missing references) float num = (float)(Time.frameCount * 37 + index * 113) % 360f * ((float)Math.PI / 180f); return origin + new Vector3(Mathf.Cos(num) * 0.75f, Mathf.Sin(num) * 0.75f, 0f); } private static void SanitizeClone(GameObject clone) { if (!((Object)(object)clone == (Object)null)) { MinionProtector component = clone.GetComponent(); if ((Object)(object)component != (Object)null) { Object.Destroy((Object)(object)component); } LineRenderer component2 = clone.GetComponent(); if ((Object)(object)component2 != (Object)null) { Object.Destroy((Object)(object)component2); } UnitObject component3 = clone.GetComponent(); if ((Object)(object)component3 != (Object)null) { component3.orderIndicator = null; component3.RemoveModifier(); component3.CanHaveModifier = false; } } } } [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInPlugin("com.cotlmp.enemyspawnscaling", "COTL Multiplayer Enemy Spawn Scaling 8P", "0.7.0")] public sealed class EnemySpawnScalingPlugin : BaseUnityPlugin { internal static ManualLogSource Log; internal static ConfigEntry DuplicateMiniBosses; internal static ConfigEntry EnemyCountMultiplier3P; internal static ConfigEntry EnemyCountMultiplier4P; internal static ConfigEntry EnemyCountMultiplier8P; private Harmony _harmony; private bool _heartbeatLogged; private void Awake() { //IL_0111: Unknown result type (might be due to invalid IL or missing references) //IL_011b: Expected O, but got Unknown Log = ((BaseUnityPlugin)this).Logger; DuplicateMiniBosses = ((BaseUnityPlugin)this).Config.Bind("Scaling", "DuplicateMiniBosses", false, "If true, mini-boss enemies can be duplicated instead of health-scaled. Can cause instability/softlocks for some encounters."); EnemyCountMultiplier3P = ((BaseUnityPlugin)this).Config.Bind("Scaling", "EnemyCountMultiplier3P", 1.15f, "Restrained total regular-enemy count with 3 players. TEST8.7 caps this at 1.15."); EnemyCountMultiplier4P = ((BaseUnityPlugin)this).Config.Bind("Scaling", "EnemyCountMultiplier4P", 1.25f, "Restrained total regular-enemy count with 4 players. TEST8.7 caps this at 1.25."); EnemyCountMultiplier8P = ((BaseUnityPlugin)this).Config.Bind("Scaling", "EnemyCountMultiplier8P", 1.75f, "Restrained total regular-enemy count with 8 players. Players 5-7 interpolate up to 1.75."); EnemyCountMultiplier3P.Value = Mathf.Clamp(EnemyCountMultiplier3P.Value, 1f, 1.15f); EnemyCountMultiplier4P.Value = Mathf.Clamp(EnemyCountMultiplier4P.Value, 1f, 1.25f); EnemyCountMultiplier8P.Value = Mathf.Clamp(EnemyCountMultiplier8P.Value, 1f, 1.75f); ((BaseUnityPlugin)this).Config.Save(); _harmony = new Harmony("com.cotlmp.enemyspawnscaling"); PatchIfFound(AccessTools.Method(typeof(EnemySpawner), "Create", new Type[3] { typeof(Vector3), typeof(Transform), typeof(GameObject) }, (Type[])null), null, "EnemySpawner_Create_Postfix", "EnemySpawner.Create(Vector3,Transform,GameObject)"); PatchIfFound(AccessTools.Method(typeof(EnemySpawner), "CreateWithAndInitInstantiatedEnemy", new Type[3] { typeof(Vector3), typeof(Transform), typeof(GameObject) }, (Type[])null), null, "EnemySpawner_CreateWithAndInit_Postfix", "EnemySpawner.CreateWithAndInitInstantiatedEnemy(Vector3,Transform,GameObject)"); PatchIfFound(AccessTools.Method(typeof(EnemySpawner), "InitAndInstantiate", new Type[1] { typeof(GameObject) }, (Type[])null), null, "EnemySpawner_InitAndInstantiate_Postfix", "EnemySpawner.InitAndInstantiate(GameObject)"); PatchIfFound(AccessTools.Method(typeof(EnemySpawner), "Init", new Type[1] { typeof(GameObject) }, (Type[])null), null, "EnemySpawner_Init_Postfix", "EnemySpawner.Init(GameObject)"); PatchIfFound(AccessTools.Method(typeof(EnemyRoundsBase), "RoundStarted", new Type[2] { typeof(int), typeof(int) }, (Type[])null), "EnemyRoundsBase_RoundStarted_Prefix", null, "EnemyRoundsBase.RoundStarted(int,int)"); PatchIfFound(AccessTools.Method(typeof(EnemyRoundsBase), "AddEnemyToRound", new Type[1] { typeof(Health) }, (Type[])null), null, "EnemyRoundsBase_AddEnemyToRound_Postfix", "EnemyRoundsBase.AddEnemyToRound(Health)"); PatchIfFound(AccessTools.Method(typeof(EnemyEncounterChanceEvents), "AssignShieldsAndGroups", new Type[0], (Type[])null), null, "EnemyEncounterChanceEvents_AssignShieldsAndGroups_Postfix", "EnemyEncounterChanceEvents.AssignShieldsAndGroups()"); Log.LogInfo((object)"Enemy spawn scaling addon loaded."); Log.LogInfo((object)"[SpawnScale] Ready v0.7.0-8P TEST8.8 DIFFICULTY SELECTOR"); Log.LogInfo((object)("[SpawnScale] Config DuplicateMiniBosses=" + DuplicateMiniBosses.Value)); Log.LogInfo((object)$"[SpawnScale] 8P Difficulty={EnemySpawnPatches.GetDifficultyName()} targets: 5P x{EnemySpawnPatches.GetTargetEnemyMultiplier(5):0.00}, 8P x{EnemySpawnPatches.GetTargetEnemyMultiplier(8):0.00}"); Log.LogInfo((object)"[SpawnScale] Vanilla enemy rounds and room completion remain authoritative."); Log.LogWarning((object)"[SPAWNSCALE_BOOT_031]"); Debug.Log((object)"[SPAWNSCALE_BOOT_031]"); } private void Update() { if (!_heartbeatLogged) { _heartbeatLogged = true; Log.LogWarning((object)"[SPAWNSCALE_HEARTBEAT_031]"); Debug.Log((object)"[SPAWNSCALE_HEARTBEAT_031]"); } } private void PatchIfFound(MethodInfo original, string prefix = null, string postfix = null, string label = null) { //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Expected O, but got Unknown //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Expected O, but got Unknown if (original == null) { Log.LogWarning((object)("[SpawnScale] Skipped patch (missing): " + (label ?? "unknown"))); return; } HarmonyMethod val = null; HarmonyMethod val2 = null; if (!string.IsNullOrEmpty(prefix)) { val = new HarmonyMethod(typeof(EnemySpawnPatches), prefix, (Type[])null); } if (!string.IsNullOrEmpty(postfix)) { val2 = new HarmonyMethod(typeof(EnemySpawnPatches), postfix, (Type[])null); } _harmony.Patch((MethodBase)original, val, val2, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); Log.LogInfo((object)("[SpawnScale] Patched: " + (label ?? original.Name))); } }