Decompiled source of PersistentRaids v1.0.6

Cjayride.PersistentRaids.dll

Decompiled 10 hours 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.IgnoreSymbolStoreSequencePoints)]
[assembly: IgnoresAccessChecksTo("assembly_valheim")]
[assembly: TargetFramework(".NETFramework,Version=v4.8.1", FrameworkDisplayName = ".NET Framework 4.8.1")]
[assembly: AssemblyCompany("Cjayride.PersistentRaids")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.6.0")]
[assembly: AssemblyInformationalVersion("1.0.6")]
[assembly: AssemblyProduct("Cjayride.PersistentRaids")]
[assembly: AssemblyTitle("Cjayride.PersistentRaids")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.6.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 Cjayride.PersistentRaids
{
	internal static class CapService
	{
		internal static void OnCreatureSpawned()
		{
			if (ModConfig.EnableMonsterCap.Value)
			{
				EnforceCap();
			}
		}

		internal static void EnforceCap()
		{
			if (!ModConfig.EnableMonsterCap.Value || (Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer())
			{
				return;
			}
			List<Character> allCharacters = Character.GetAllCharacters();
			List<Character> list = new List<Character>();
			for (int i = 0; i < allCharacters.Count; i++)
			{
				Character val = allCharacters[i];
				if (CreatureRules.IsEnemy(val) && CreatureRules.PassesFilter(val))
				{
					list.Add(val);
				}
			}
			int value = ModConfig.MaxActiveMonsters.Value;
			if (list.Count <= value)
			{
				return;
			}
			if (ModConfig.LogCapEvents.Value)
			{
				Plugin.Log.LogInfo((object)("Monster cap " + value + " hit, living=" + list.Count));
			}
			int value2 = ModConfig.CapBehavior.Value;
			if (value2 == 0)
			{
				return;
			}
			int num = list.Count - value;
			if (value2 == 2)
			{
				list.Sort((Character a, Character b) => DistanceToNearestPlayer(b).CompareTo(DistanceToNearestPlayer(a)));
			}
			int num2 = 0;
			for (int num3 = 0; num3 < list.Count; num3++)
			{
				if (num2 >= num)
				{
					break;
				}
				Character val2 = list[num3];
				if (!val2.IsBoss() || !ModConfig.NeverDespawnBosses.Value)
				{
					ZNetView component = ((Component)val2).GetComponent<ZNetView>();
					if ((Object)(object)component != (Object)null && component.IsValid())
					{
						component.Destroy();
						num2++;
					}
				}
			}
		}

		internal static int Clear(bool eventOnly, bool includeBosses, string prefabFilter)
		{
			int num = 0;
			List<Character> list = new List<Character>(Character.GetAllCharacters());
			for (int i = 0; i < list.Count; i++)
			{
				Character val = list[i];
				if (!CreatureRules.IsEnemy(val) || (!includeBosses && val.IsBoss()) || (!string.IsNullOrEmpty(prefabFilter) && !((Object)((Component)val).gameObject).name.Replace("(Clone)", string.Empty).Equals(prefabFilter, StringComparison.OrdinalIgnoreCase)))
				{
					continue;
				}
				if (eventOnly)
				{
					MonsterAI component = ((Component)val).GetComponent<MonsterAI>();
					if ((Object)(object)component == (Object)null || !component.IsEventCreature())
					{
						continue;
					}
				}
				ZNetView component2 = ((Component)val).GetComponent<ZNetView>();
				if ((Object)(object)component2 != (Object)null && component2.IsValid())
				{
					component2.Destroy();
					num++;
				}
			}
			return num;
		}

		private static float DistanceToNearestPlayer(Character character)
		{
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			float num = float.MaxValue;
			foreach (Player allPlayer in Player.GetAllPlayers())
			{
				if (!((Object)(object)allPlayer == (Object)null))
				{
					float num2 = Vector3.Distance(((Component)character).transform.position, ((Component)allPlayer).transform.position);
					if (num2 < num)
					{
						num = num2;
					}
				}
			}
			return num;
		}
	}
	public class ConfigurationManagerAttributes
	{
		public bool? IsAdminOnly;
	}
	internal static class CreatureRules
	{
		internal sealed class RaidChaseApplied : MonoBehaviour
		{
		}

		internal static bool PlayersOnline()
		{
			if ((Object)(object)ZNet.instance == (Object)null)
			{
				return false;
			}
			if (ZNet.instance.IsServer())
			{
				if (ZNet.instance.GetPeers().Count <= 0)
				{
					return (Object)(object)Player.m_localPlayer != (Object)null;
				}
				return true;
			}
			return (Object)(object)Player.m_localPlayer != (Object)null;
		}

		internal static bool RulesActive()
		{
			if (ModConfig.Enabled == null || !ModConfig.Enabled.Value)
			{
				return false;
			}
			if (ModConfig.OnlyApplyWhilePlayersOnline.Value && !PlayersOnline())
			{
				return false;
			}
			return true;
		}

		internal static bool PassesFilter(Character character)
		{
			if ((Object)(object)character == (Object)null || character.IsPlayer())
			{
				return false;
			}
			string text = ((Object)((Component)character).gameObject).name.Replace("(Clone)", string.Empty);
			string value = ModConfig.CreatureBlacklist.Value;
			string[] array;
			if (!string.IsNullOrWhiteSpace(value))
			{
				array = value.Split(new char[1] { ',' });
				foreach (string text2 in array)
				{
					if (text.Equals(text2.Trim(), StringComparison.OrdinalIgnoreCase))
					{
						return false;
					}
				}
			}
			string value2 = ModConfig.CreatureWhitelist.Value;
			if (string.IsNullOrWhiteSpace(value2))
			{
				return true;
			}
			array = value2.Split(new char[1] { ',' });
			foreach (string text3 in array)
			{
				if (text.Equals(text3.Trim(), StringComparison.OrdinalIgnoreCase))
				{
					return true;
				}
			}
			return false;
		}

		internal static bool IsEnemy(Character character)
		{
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Invalid comparison between Unknown and I4
			if ((Object)(object)character == (Object)null || character.IsPlayer())
			{
				return false;
			}
			Faction faction = character.GetFaction();
			if ((int)faction != 0)
			{
				return (int)faction != 10;
			}
			return false;
		}

		internal static bool ShouldPersist(Character character, MonsterAI monster)
		{
			if (!RulesActive() || !PassesFilter(character) || !IsEnemy(character))
			{
				return false;
			}
			if (character.IsBoss())
			{
				return ModConfig.NeverDespawnBosses.Value;
			}
			if (ModConfig.NeverDespawnMonsters.Value)
			{
				return IsRaidCreature(monster);
			}
			return false;
		}

		internal static void ApplyPersistence(MonsterAI monster)
		{
			if ((Object)(object)monster == (Object)null)
			{
				return;
			}
			Character character = ((BaseAI)monster).m_character;
			if ((Object)(object)character == (Object)null || !ShouldPersist(character, monster))
			{
				return;
			}
			ZNetView nview = ((BaseAI)monster).m_nview;
			if (!((Object)(object)nview == (Object)null) && nview.IsValid())
			{
				ZDO zDO = nview.GetZDO();
				if (zDO != null && !zDO.Persistent)
				{
					zDO.Persistent = true;
				}
			}
		}

		internal static bool IsRaidCreature(MonsterAI monster)
		{
			if ((Object)(object)monster != (Object)null)
			{
				return monster.IsEventCreature();
			}
			return false;
		}

		internal static bool ShouldKeepHunt(BaseAI ai)
		{
			if (!RulesActive() || (Object)(object)ai == (Object)null || (Object)(object)ai.m_character == (Object)null || !PassesFilter(ai.m_character))
			{
				return false;
			}
			if (!IsRaidCreature((MonsterAI)(object)((ai is MonsterAI) ? ai : null)))
			{
				return false;
			}
			if (!ModConfig.EnablePersistentChase.Value)
			{
				return ModConfig.EventMonstersSurviveRaidEnd.Value;
			}
			return true;
		}

		internal static void ApplyRaidChase(MonsterAI monster)
		{
			if ((Object)(object)monster == (Object)null || !RulesActive() || !IsRaidCreature(monster) || !PassesFilter(((BaseAI)monster).m_character) || (!ModConfig.EnablePersistentChase.Value && !ModConfig.EventMonstersSurviveRaidEnd.Value))
			{
				return;
			}
			EnforceFightAi(monster);
			if (ModConfig.EnablePersistentChase.Value && (Object)(object)((Component)monster).GetComponent<RaidChaseApplied>() == (Object)null)
			{
				((Component)monster).gameObject.AddComponent<RaidChaseApplied>();
				float num = Mathf.Max(1f, ModConfig.ChaseRangeMultiplier.Value);
				monster.m_maxChaseDistance *= num;
				((BaseAI)monster).m_viewRange = ((BaseAI)monster).m_viewRange * num;
				monster.m_alertRange *= num;
				float value = ModConfig.MaxChaseDistance.Value;
				if (value > 0f)
				{
					monster.m_maxChaseDistance = Mathf.Min(monster.m_maxChaseDistance, value);
				}
			}
		}

		internal static void EnforceFightAi(MonsterAI monster)
		{
			if (!((Object)(object)monster == (Object)null))
			{
				monster.m_fleeIfNotAlerted = false;
				monster.m_fleeIfHurtWhenTargetCantBeReached = false;
				monster.m_fleeIfLowHealth = 0f;
				monster.m_enableHuntPlayer = true;
				if (!((BaseAI)monster).m_huntPlayer)
				{
					((BaseAI)monster).SetHuntPlayer(true);
				}
				if (!((BaseAI)monster).IsAlerted())
				{
					((BaseAI)monster).SetAlerted(true);
				}
			}
		}

		internal static void DebugLog(string message)
		{
			if (ModConfig.EnableDebugLogging != null && ModConfig.EnableDebugLogging.Value)
			{
				Plugin.Log.LogInfo((object)message);
			}
		}
	}
	[HarmonyPatch(typeof(BaseAI), "MoveAwayAndDespawn", new Type[]
	{
		typeof(float),
		typeof(bool)
	})]
	internal static class PatchMoveAwayAndDespawn
	{
		private static bool Prefix(BaseAI __instance)
		{
			if ((Object)(object)__instance.m_character != (Object)null && CreatureRules.ShouldPersist(__instance.m_character, (MonsterAI)(object)((__instance is MonsterAI) ? __instance : null)))
			{
				CreatureRules.DebugLog("Blocked MoveAwayAndDespawn " + ((Object)__instance.m_character).name);
				return false;
			}
			return true;
		}
	}
	[HarmonyPatch(typeof(MonsterAI), "HuntPlayer")]
	internal static class PatchMonsterHuntPlayer
	{
		private static bool Prefix(MonsterAI __instance, ref bool __result)
		{
			if (!CreatureRules.ShouldKeepHunt((BaseAI)(object)__instance))
			{
				return true;
			}
			if (__instance.DespawnInDay() && EnvMan.IsDay() && !ModConfig.NeverDespawnNightMonsters.Value)
			{
				return true;
			}
			__result = true;
			return false;
		}
	}
	[HarmonyPatch(typeof(BaseAI), "SetAlerted")]
	internal static class PatchSetAlerted
	{
		private static bool Prefix(BaseAI __instance, bool alert)
		{
			if (!alert && CreatureRules.ShouldKeepHunt(__instance))
			{
				return false;
			}
			return true;
		}
	}
	[HarmonyPatch(typeof(BaseAI), "SetHuntPlayer")]
	internal static class PatchSetHuntPlayer
	{
		private static bool Prefix(BaseAI __instance, bool hunt)
		{
			if (!hunt && CreatureRules.ShouldKeepHunt(__instance))
			{
				return false;
			}
			return true;
		}
	}
	[HarmonyPatch(typeof(BaseAI), "Flee", new Type[]
	{
		typeof(float),
		typeof(Vector3)
	})]
	internal static class PatchFlee
	{
		private static bool Prefix(BaseAI __instance, ref bool __result)
		{
			if (CreatureRules.ShouldKeepHunt(__instance))
			{
				__result = false;
				return false;
			}
			return true;
		}
	}
	[HarmonyPatch(typeof(MonsterAI), "UpdateAI")]
	internal static class PatchMonsterUpdateAI
	{
		private static void Postfix(MonsterAI __instance)
		{
			if (CreatureRules.ShouldKeepHunt((BaseAI)(object)__instance))
			{
				CreatureRules.EnforceFightAi(__instance);
			}
		}
	}
	[HarmonyPatch(typeof(MonsterAI), "Awake")]
	internal static class PatchMonsterAwake
	{
		private static void Postfix(MonsterAI __instance)
		{
			CreatureRules.ApplyRaidChase(__instance);
			CreatureRules.ApplyPersistence(__instance);
		}
	}
	[HarmonyPatch(typeof(MonsterAI), "SetEventCreature")]
	internal static class PatchSetEventCreature
	{
		private static void Postfix(MonsterAI __instance)
		{
			CreatureRules.ApplyRaidChase(__instance);
			CreatureRules.ApplyPersistence(__instance);
		}
	}
	[HarmonyPatch(typeof(MonsterAI), "SetDespawnInDay")]
	internal static class PatchSetDespawnInDay
	{
		private static bool Prefix(MonsterAI __instance, ref bool despawn)
		{
			if (despawn && CreatureRules.RulesActive() && ModConfig.NeverDespawnNightMonsters.Value && CreatureRules.IsRaidCreature(__instance) && CreatureRules.PassesFilter(((BaseAI)__instance).m_character))
			{
				despawn = false;
			}
			return true;
		}
	}
	internal static class ModConfig
	{
		internal static ConfigEntry<bool> Enabled;

		internal static ConfigEntry<bool> EnablePersistentChase;

		internal static ConfigEntry<float> ChaseRangeMultiplier;

		internal static ConfigEntry<float> MaxChaseDistance;

		internal static ConfigEntry<bool> NeverDespawnMonsters;

		internal static ConfigEntry<bool> NeverDespawnBosses;

		internal static ConfigEntry<bool> EventMonstersSurviveRaidEnd;

		internal static ConfigEntry<bool> NeverDespawnNightMonsters;

		internal static ConfigEntry<bool> AllowMultipleRaids;

		internal static ConfigEntry<bool> ContinueSpawningWithoutPlayers;

		internal static ConfigEntry<float> SpawnWithoutPlayersDuration;

		internal static ConfigEntry<bool> OverrideRaidDurations;

		internal static ConfigEntry<float> OverrideDurationSeconds;

		internal static ConfigEntry<bool> EnableMonsterCap;

		internal static ConfigEntry<int> MaxActiveMonsters;

		internal static ConfigEntry<int> CapBehavior;

		internal static ConfigEntry<bool> OnlyApplyWhilePlayersOnline;

		internal static ConfigEntry<float> AutoCleanupInterval;

		internal static ConfigEntry<string> CreatureWhitelist;

		internal static ConfigEntry<string> CreatureBlacklist;

		internal static ConfigEntry<bool> EnableDebugLogging;

		internal static ConfigEntry<bool> LogCapEvents;

		private static ConfigDescription Admin(string desc)
		{
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Expected O, but got Unknown
			return new ConfigDescription(desc, (AcceptableValueBase)null, new object[1]
			{
				new ConfigurationManagerAttributes
				{
					IsAdminOnly = true
				}
			});
		}

		internal static void Bind(ConfigFile config)
		{
			Enabled = config.Bind<bool>("General", "Enabled", true, Admin("Master switch."));
			EnablePersistentChase = config.Bind<bool>("RaidChase", "EnablePersistentChase", true, Admin("ALL RandomEvent/raid creatures hunt and fight (no shy flee). Open-world mobs stay vanilla."));
			ChaseRangeMultiplier = config.Bind<float>("RaidChase", "ChaseRangeMultiplier", 3f, Admin("Multiplies view/alert/chase range for raid creatures only. 1 = vanilla."));
			MaxChaseDistance = config.Bind<float>("RaidChase", "MaxChaseDistance", 200f, Admin("Absolute chase cap in meters. 0 = multiplier only."));
			NeverDespawnMonsters = config.Bind<bool>("Despawn", "NeverDespawnMonsters", true, Admin("Block far-despawn and mark ZDOs persistent for RAID/event creatures only. Open-world wildlife culls like vanilla so it does not pile up around bases."));
			NeverDespawnBosses = config.Bind<bool>("Despawn", "NeverDespawnBosses", true, Admin("Bosses persist / skip far-despawn. Hunt range is still raid-only."));
			EventMonstersSurviveRaidEnd = config.Bind<bool>("Despawn", "EventMonstersSurviveRaidEnd", true, Admin("Event creatures keep hunting after the raid timer ends."));
			NeverDespawnNightMonsters = config.Bind<bool>("Despawn", "NeverDespawnNightMonsters", true, Admin("Ignore despawn-in-day on night-spawned event creatures."));
			AllowMultipleRaids = config.Bind<bool>("MultipleRaids", "AllowMultipleRaids", false, Admin("OFF by default. Custom Raids will not start a new raid while any event is still current. Leave false."));
			ContinueSpawningWithoutPlayers = config.Bind<bool>("MultipleRaids", "ContinueSpawningWithoutPlayers", false, Admin("OFF by default. When on, keeps the raid timer (and spawn checks) running with nobody in the circle. Leave off so each raid's PauseIfNoPlayerInArea and Duration work as configured."));
			SpawnWithoutPlayersDuration = config.Bind<float>("MultipleRaids", "SpawnWithoutPlayersDuration", 600f, Admin("Unused (kept so old configs still load)."));
			OverrideRaidDurations = config.Bind<bool>("MultipleRaids", "OverrideRaidDurations", false, Admin("OFF by default. When on, replaces every random-event Duration with OverrideDurationSeconds. Leave off to use each Custom Raids Duration."));
			OverrideDurationSeconds = config.Bind<float>("MultipleRaids", "OverrideDurationSeconds", 60f, Admin("Only used when OverrideRaidDurations is on. Seconds."));
			EnableMonsterCap = config.Bind<bool>("Safety", "EnableMonsterCap", true, Admin("Hard cap on living non-player characters."));
			MaxActiveMonsters = config.Bind<int>("Safety", "MaxActiveMonsters", 220, Admin("Global living monster cap."));
			CapBehavior = config.Bind<int>("Safety", "CapBehavior", 1, Admin("0 = stop persisting new ones. 1 = despawn oldest. 2 = despawn furthest from any player."));
			OnlyApplyWhilePlayersOnline = config.Bind<bool>("Safety", "OnlyApplyWhilePlayersOnline", true, Admin("When no players are online, never-despawn rules are off so the server can unload."));
			AutoCleanupInterval = config.Bind<float>("Safety", "AutoCleanupInterval", 15f, Admin("Minutes between cap sweeps. 0 = only on spawn."));
			CreatureWhitelist = config.Bind<string>("Filters", "CreatureWhitelist", "", Admin("Comma prefab names. Empty = all enemies."));
			CreatureBlacklist = config.Bind<string>("Filters", "CreatureBlacklist", "", Admin("Comma prefab names to ignore."));
			EnableDebugLogging = config.Bind<bool>("Logging", "EnableDebugLogging", false, "Log blocked despawns and extra raids.");
			LogCapEvents = config.Bind<bool>("Logging", "LogCapEvents", true, "Log when the monster cap is hit.");
		}
	}
	[BepInPlugin("cjayride.persistentraids", "PersistentRaids", "1.0.6")]
	public class Plugin : BaseUnityPlugin
	{
		[Serializable]
		[CompilerGenerated]
		private sealed class <>c
		{
			public static readonly <>c <>9 = new <>c();

			public static ConsoleEvent <>9__7_0;

			internal void <Awake>b__7_0(ConsoleEventArgs args)
			{
				if (!Object.op_Implicit((Object)(object)ZNet.instance) || !ZNet.instance.IsServer())
				{
					args.Context.AddString("PersistentRaids: server only");
					return;
				}
				if (args.Length < 2)
				{
					args.Context.AddString("persistraid clear | event | stop | cap | boss <prefab>");
					return;
				}
				switch (args[1].ToLowerInvariant())
				{
				case "clear":
				{
					int num = CapService.Clear(eventOnly: false, includeBosses: false, null);
					args.Context.AddString("Cleared " + num + " non-boss monsters");
					break;
				}
				case "event":
				{
					int num = CapService.Clear(eventOnly: true, includeBosses: false, null);
					args.Context.AddString("Cleared " + num + " event monsters");
					break;
				}
				case "stop":
					ExtraRaids.StopAll(RandEventSystem.instance);
					args.Context.AddString("Cleared current/stashed random events");
					break;
				case "cap":
					CapService.EnforceCap();
					args.Context.AddString("Cap enforced");
					break;
				case "boss":
				{
					if (args.Length < 3)
					{
						args.Context.AddString("persistraid boss <prefab>");
						break;
					}
					int num = CapService.Clear(eventOnly: false, includeBosses: true, args[2]);
					args.Context.AddString("Cleared " + num + " matching bosses");
					break;
				}
				default:
					args.Context.AddString("persistraid clear | event | stop | cap | boss <prefab>");
					break;
				}
			}
		}

		public const string PluginGUID = "cjayride.persistentraids";

		public const string PluginName = "PersistentRaids";

		public const string PluginVersion = "1.0.6";

		internal static Plugin Instance;

		internal static Harmony Harmony;

		internal static ManualLogSource Log => ((BaseUnityPlugin)Instance).Logger;

		private void Awake()
		{
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Expected O, but got Unknown
			//IL_0062: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: Expected O, but got Unknown
			Instance = this;
			ModConfig.Bind(((BaseUnityPlugin)this).Config);
			Harmony = new Harmony("cjayride.persistentraids");
			Harmony.PatchAll(Assembly.GetExecutingAssembly());
			object obj = <>c.<>9__7_0;
			if (obj == null)
			{
				ConsoleEvent val = delegate(ConsoleEventArgs args)
				{
					if (!Object.op_Implicit((Object)(object)ZNet.instance) || !ZNet.instance.IsServer())
					{
						args.Context.AddString("PersistentRaids: server only");
					}
					else if (args.Length < 2)
					{
						args.Context.AddString("persistraid clear | event | stop | cap | boss <prefab>");
					}
					else
					{
						switch (args[1].ToLowerInvariant())
						{
						case "clear":
						{
							int num = CapService.Clear(eventOnly: false, includeBosses: false, null);
							args.Context.AddString("Cleared " + num + " non-boss monsters");
							break;
						}
						case "event":
						{
							int num = CapService.Clear(eventOnly: true, includeBosses: false, null);
							args.Context.AddString("Cleared " + num + " event monsters");
							break;
						}
						case "stop":
							ExtraRaids.StopAll(RandEventSystem.instance);
							args.Context.AddString("Cleared current/stashed random events");
							break;
						case "cap":
							CapService.EnforceCap();
							args.Context.AddString("Cap enforced");
							break;
						case "boss":
							if (args.Length < 3)
							{
								args.Context.AddString("persistraid boss <prefab>");
							}
							else
							{
								int num = CapService.Clear(eventOnly: false, includeBosses: true, args[2]);
								args.Context.AddString("Cleared " + num + " matching bosses");
							}
							break;
						default:
							args.Context.AddString("persistraid clear | event | stop | cap | boss <prefab>");
							break;
						}
					}
				};
				<>c.<>9__7_0 = val;
				obj = (object)val;
			}
			new ConsoleCommand("persistraid", "clear | event | stop | cap | boss <prefab> — PersistentRaids cleanup", (ConsoleEvent)obj, false, false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false);
			if (ModConfig.AutoCleanupInterval.Value > 0f)
			{
				((MonoBehaviour)this).InvokeRepeating("TickCap", 60f, ModConfig.AutoCleanupInterval.Value * 60f);
			}
			((BaseUnityPlugin)this).Logger.LogInfo((object)"PersistentRaids 1.0.6 loaded. Custom Raids owns raid start. This mod only persists raid creatures.");
		}

		private void TickCap()
		{
			CapService.EnforceCap();
		}
	}
	internal static class ExtraRaids
	{
		internal static readonly List<RandomEvent> Stashed = new List<RandomEvent>();

		internal static void StashCurrent(RandEventSystem system)
		{
			if (system.m_randomEvent != null)
			{
				Stashed.Add(system.m_randomEvent);
				CreatureRules.DebugLog("Stashed raid " + system.m_randomEvent.m_name + " extras=" + Stashed.Count);
				system.m_randomEvent = null;
			}
		}

		internal static void StopAll(RandEventSystem system)
		{
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			Stashed.Clear();
			if ((Object)(object)system != (Object)null)
			{
				system.SetRandomEvent((RandomEvent)null, Vector3.zero);
			}
		}

		internal static void ApplyDurationOverride(RandomEvent ev)
		{
			if (ev != null && ModConfig.OverrideRaidDurations.Value)
			{
				float duration = Mathf.Max(1f, ModConfig.OverrideDurationSeconds.Value);
				CreatureRules.DebugLog("Duration override " + ev.m_name + " " + ev.m_duration + " -> " + duration);
				ev.m_duration = duration;
			}
		}

		internal static void Tick(RandEventSystem system, float dt)
		{
			bool flag = ModConfig.ContinueSpawningWithoutPlayers.Value && CreatureRules.RulesActive();
			for (int num = Stashed.Count - 1; num >= 0; num--)
			{
				RandomEvent val = Stashed[num];
				if (val == null)
				{
					Stashed.RemoveAt(num);
				}
				else
				{
					bool flag2 = system.IsAnyPlayerInEventArea(val) || flag;
					if (val.Update(true, true, flag2, dt))
					{
						val.OnStop();
						Stashed.RemoveAt(num);
						CreatureRules.DebugLog("Extra raid ended " + val.m_name);
					}
				}
			}
		}
	}
	[HarmonyPatch(typeof(RandEventSystem), "StartRandomEvent")]
	internal static class PatchStartRandomEvent
	{
		private static void Prefix(RandEventSystem __instance)
		{
			if (CreatureRules.RulesActive() && ModConfig.AllowMultipleRaids.Value && __instance.m_randomEvent != null)
			{
				ExtraRaids.StashCurrent(__instance);
			}
		}
	}
	[HarmonyPatch(typeof(RandEventSystem), "SetRandomEvent")]
	internal static class PatchSetRandomEvent
	{
		private static void Postfix(RandEventSystem __instance)
		{
			if (CreatureRules.RulesActive())
			{
				ExtraRaids.ApplyDurationOverride(__instance.m_randomEvent);
			}
		}
	}
	[HarmonyPatch(typeof(RandEventSystem), "FixedUpdate")]
	internal static class PatchEventFixedUpdate
	{
		private static void Postfix(RandEventSystem __instance)
		{
			if (Object.op_Implicit((Object)(object)ZNet.instance) && ZNet.instance.IsServer() && ModConfig.AllowMultipleRaids.Value && ExtraRaids.Stashed.Count != 0)
			{
				ExtraRaids.Tick(__instance, Time.fixedDeltaTime);
			}
		}
	}
	[HarmonyPatch(typeof(RandomEvent), "Update", new Type[]
	{
		typeof(bool),
		typeof(bool),
		typeof(bool),
		typeof(float)
	})]
	internal static class PatchRandomEventUpdate
	{
		private static void Prefix(ref bool playerInArea)
		{
			if (CreatureRules.RulesActive() && ModConfig.ContinueSpawningWithoutPlayers.Value)
			{
				playerInArea = true;
			}
		}
	}
}
namespace System.Runtime.CompilerServices
{
	[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
	internal sealed class IgnoresAccessChecksToAttribute : Attribute
	{
		public IgnoresAccessChecksToAttribute(string assemblyName)
		{
		}
	}
}