Decompiled source of PersistentRaids v1.0.10

Cjayride.PersistentRaids.dll

Decompiled 2 weeks 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.10.0")]
[assembly: AssemblyInformationalVersion("1.0.10+326a9db0625175261219567e320b756790f4e2d4")]
[assembly: AssemblyProduct("Cjayride.PersistentRaids")]
[assembly: AssemblyTitle("Cjayride.PersistentRaids")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.10.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
	{
		private static readonly Dictionary<int, bool> CountablePrefabs = new Dictionary<int, bool>();

		private static readonly HashSet<ZDOID> Tracked = new HashSet<ZDOID>();

		private static readonly List<ZDO> RaidBuffer = new List<ZDO>();

		private static readonly List<ZDO> NearBuffer = new List<ZDO>();

		private static readonly List<ZDOID> DeadIds = new List<ZDOID>();

		private static readonly List<Vector3> PlayerBuffer = new List<Vector3>();

		private static string _filterKey;

		private static bool _didWorldScan;

		internal static void Track(ZDO zdo)
		{
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			if (zdo != null)
			{
				Tracked.Add(zdo.m_uid);
			}
		}

		internal static void EnforceCap()
		{
			//IL_009a: Unknown result type (might be due to invalid IL or missing references)
			//IL_009f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cc: Unknown result type (might be due to invalid IL or missing references)
			if (!ModConfig.EnableMonsterCap.Value || (Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer() || ZDOMan.instance == null)
			{
				return;
			}
			List<Vector3> players = PlayerPositions();
			List<ZDO> list = RaidZdos();
			ModConfig.CapScopeMode value = ModConfig.CapScope.Value;
			if (value != ModConfig.CapScopeMode.ServerWide)
			{
				float num = Mathf.Max(1f, ModConfig.PlayerAreaRadius.Value);
				float num2 = num * num;
				int num3 = Mathf.Max(0, ModConfig.MaxPerPlayerArea.Value);
				for (int i = 0; i < players.Count; i++)
				{
					Vector3 player = players[i];
					NearBuffer.Clear();
					for (int j = 0; j < list.Count; j++)
					{
						Vector3 val = list[j].GetPosition() - player;
						if (((Vector3)(ref val)).sqrMagnitude <= num2)
						{
							NearBuffer.Add(list[j]);
						}
					}
					if (NearBuffer.Count > num3)
					{
						NearBuffer.Sort(delegate(ZDO a, ZDO b)
						{
							//IL_0001: Unknown result type (might be due to invalid IL or missing references)
							//IL_0007: Unknown result type (might be due to invalid IL or missing references)
							//IL_000c: Unknown result type (might be due to invalid IL or missing references)
							//IL_0011: 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_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)
							//IL_002d: Unknown result type (might be due to invalid IL or missing references)
							Vector3 val2 = b.GetPosition() - player;
							float sqrMagnitude = ((Vector3)(ref val2)).sqrMagnitude;
							val2 = a.GetPosition() - player;
							return sqrMagnitude.CompareTo(((Vector3)(ref val2)).sqrMagnitude);
						});
						RemoveExtras(NearBuffer, NearBuffer.Count - num3, list, "within " + num + " m of a player (cap " + num3 + ")");
					}
				}
			}
			if (value == ModConfig.CapScopeMode.PerPlayerArea)
			{
				return;
			}
			int num4 = Mathf.Max(0, ModConfig.MaxActiveMonsters.Value);
			if (list.Count > num4)
			{
				list.Sort((ZDO a, ZDO b) => NearestPlayer(b.GetPosition(), players).CompareTo(NearestPlayer(a.GetPosition(), players)));
				RemoveExtras(list, list.Count - num4, list, "on the server (cap " + num4 + ")");
			}
		}

		private static void RemoveExtras(List<ZDO> ordered, int extra, List<ZDO> raid, string where)
		{
			//IL_007f: Unknown result type (might be due to invalid IL or missing references)
			if (ModConfig.LogCapEvents.Value)
			{
				Plugin.Log.LogInfo((object)("Raid creature cap hit: " + ordered.Count + " raid creatures " + where + "."));
			}
			if (ModConfig.CapBehavior.Value != 0)
			{
				long sessionID = ZDOMan.GetSessionID();
				int num = 0;
				for (int i = 0; i < extra && i < ordered.Count; i++)
				{
					ZDO val = ordered[i];
					val.SetOwner(sessionID);
					Tracked.Remove(val.m_uid);
					ZDOMan.instance.DestroyZDO(val);
					raid.Remove(val);
					num++;
				}
				if (ModConfig.LogCapEvents.Value)
				{
					Plugin.Log.LogInfo((object)("Removed " + num + " raid creatures, furthest first."));
				}
			}
		}

		private static List<ZDO> RaidZdos()
		{
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			//IL_009c: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d7: Unknown result type (might be due to invalid IL or missing references)
			RefreshFilterCache();
			RaidBuffer.Clear();
			DeadIds.Clear();
			if (!_didWorldScan || Tracked.Count == 0)
			{
				ScanWorld();
				_didWorldScan = true;
				return RaidBuffer;
			}
			foreach (ZDOID item in Tracked)
			{
				ZDO zDO = ZDOMan.instance.GetZDO(item);
				if (zDO == null)
				{
					DeadIds.Add(item);
				}
				else if (!IsCountable(zDO.GetPrefab()) || !zDO.GetBool(ZDOVars.s_eventCreature, false) || zDO.GetBool(ZDOVars.s_tamed, false))
				{
					DeadIds.Add(item);
				}
				else
				{
					RaidBuffer.Add(zDO);
				}
			}
			for (int i = 0; i < DeadIds.Count; i++)
			{
				Tracked.Remove(DeadIds[i]);
			}
			return RaidBuffer;
		}

		private static void ScanWorld()
		{
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			Tracked.Clear();
			foreach (ZDO value in ZDOMan.instance.m_objectsByID.Values)
			{
				if (value != null && IsCountable(value.GetPrefab()) && value.GetBool(ZDOVars.s_eventCreature, false) && !value.GetBool(ZDOVars.s_tamed, false))
				{
					Tracked.Add(value.m_uid);
					RaidBuffer.Add(value);
				}
			}
		}

		private static bool IsCountable(int prefabHash)
		{
			if (CountablePrefabs.TryGetValue(prefabHash, out var value))
			{
				return value;
			}
			value = false;
			GameObject val = (((Object)(object)ZNetScene.instance != (Object)null) ? ZNetScene.instance.GetPrefab(prefabHash) : null);
			if ((Object)(object)val != (Object)null && (Object)(object)val.GetComponent<MonsterAI>() != (Object)null)
			{
				Character component = val.GetComponent<Character>();
				value = (Object)(object)component != (Object)null && !component.m_boss && CreatureRules.PassesFilter(component);
			}
			CountablePrefabs[prefabHash] = value;
			return value;
		}

		private static void RefreshFilterCache()
		{
			string text = ModConfig.CreatureWhitelist.Value + "|" + ModConfig.CreatureBlacklist.Value;
			if (text != _filterKey)
			{
				_filterKey = text;
				CountablePrefabs.Clear();
			}
		}

		private static List<Vector3> PlayerPositions()
		{
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_0074: Unknown result type (might be due to invalid IL or missing references)
			PlayerBuffer.Clear();
			foreach (ZNetPeer peer in ZNet.instance.GetPeers())
			{
				if (peer != null && peer.IsReady())
				{
					PlayerBuffer.Add(peer.m_refPos);
				}
			}
			if ((Object)(object)Player.m_localPlayer != (Object)null)
			{
				PlayerBuffer.Add(((Component)Player.m_localPlayer).transform.position);
			}
			return PlayerBuffer;
		}

		private static float NearestPlayer(Vector3 position, List<Vector3> players)
		{
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: 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_0017: Unknown result type (might be due to invalid IL or missing references)
			float num = float.MaxValue;
			for (int i = 0; i < players.Count; i++)
			{
				Vector3 val = players[i] - position;
				float sqrMagnitude = ((Vector3)(ref val)).sqrMagnitude;
				if (sqrMagnitude < num)
				{
					num = sqrMagnitude;
				}
			}
			return num;
		}

		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) || val.IsTamed() || (!includeBosses && val.IsBoss()) || (!string.IsNullOrEmpty(prefabFilter) && !CreatureRules.PrefabName(val).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;
		}
	}
	public class ConfigurationManagerAttributes
	{
		public bool? IsAdminOnly;
	}
	internal static class CreatureRules
	{
		internal sealed class RaidChaseApplied : MonoBehaviour
		{
		}

		internal sealed class RaidState : MonoBehaviour
		{
			internal int filterRevision = -1;

			internal bool keepHunt;
		}

		private static int _rulesFrame = -1;

		private static bool _rulesCache;

		private static string _whiteRaw;

		private static string _blackRaw;

		private static HashSet<string> _white = new HashSet<string>(StringComparer.OrdinalIgnoreCase);

		private static HashSet<string> _black = new HashSet<string>(StringComparer.OrdinalIgnoreCase);

		private static int _filterRevision;

		internal static int FilterRevision => _filterRevision;

		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()
		{
			int frameCount = Time.frameCount;
			if (frameCount == _rulesFrame)
			{
				return _rulesCache;
			}
			_rulesFrame = frameCount;
			if (ModConfig.Enabled == null || !ModConfig.Enabled.Value)
			{
				_rulesCache = false;
				return false;
			}
			if (ModConfig.OnlyApplyWhilePlayersOnline.Value && !PlayersOnline())
			{
				_rulesCache = false;
				return false;
			}
			_rulesCache = true;
			return true;
		}

		internal static void RefreshFilters()
		{
			string value = ModConfig.CreatureWhitelist.Value;
			string value2 = ModConfig.CreatureBlacklist.Value;
			if (!(value == _whiteRaw) || !(value2 == _blackRaw))
			{
				_whiteRaw = value;
				_blackRaw = value2;
				_filterRevision++;
				FillSet(_white, value);
				FillSet(_black, value2);
			}
		}

		private static void FillSet(HashSet<string> set, string raw)
		{
			set.Clear();
			if (string.IsNullOrWhiteSpace(raw))
			{
				return;
			}
			string[] array = raw.Split(new char[1] { ',' });
			for (int i = 0; i < array.Length; i++)
			{
				string text = array[i].Trim();
				if (text.Length > 0)
				{
					set.Add(text);
				}
			}
		}

		internal static string PrefabName(Character character)
		{
			string name = ((Object)((Component)character).gameObject).name;
			int num = name.IndexOf("(Clone)", StringComparison.Ordinal);
			if (num <= 0)
			{
				return name;
			}
			return name.Substring(0, num);
		}

		internal static bool PassesFilter(Character character)
		{
			if ((Object)(object)character == (Object)null || character.IsPlayer())
			{
				return false;
			}
			RefreshFilters();
			string item = PrefabName(character);
			if (_black.Count > 0 && _black.Contains(item))
			{
				return false;
			}
			if (_white.Count == 0)
			{
				return true;
			}
			return _white.Contains(item);
		}

		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;
				}
				CapService.Track(zDO);
			}
		}

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

		internal static bool ShouldKeepHunt(BaseAI ai)
		{
			MonsterAI val = (MonsterAI)(object)((ai is MonsterAI) ? ai : null);
			if ((Object)(object)val == (Object)null || !val.IsEventCreature())
			{
				return false;
			}
			RaidState raidState = ((Component)val).GetComponent<RaidState>();
			if ((Object)(object)raidState != (Object)null && raidState.filterRevision == _filterRevision)
			{
				return raidState.keepHunt;
			}
			bool flag = RulesActive() && PassesFilter(ai.m_character) && (ModConfig.EnablePersistentChase.Value || ModConfig.EventMonstersSurviveRaidEnd.Value);
			if ((Object)(object)raidState == (Object)null)
			{
				raidState = ((Component)val).gameObject.AddComponent<RaidState>();
			}
			raidState.filterRevision = _filterRevision;
			raidState.keepHunt = flag;
			return flag;
		}

		internal static void ApplyRaidChase(MonsterAI monster)
		{
			if ((Object)(object)monster == (Object)null || !IsRaidCreature(monster) || !RulesActive() || !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) && (!((BaseAI)monster).m_huntPlayer || !((BaseAI)monster).IsAlerted() || !monster.m_enableHuntPlayer || monster.m_fleeIfNotAlerted || monster.m_fleeIfHurtWhenTargetCantBeReached || !(monster.m_fleeIfLowHealth <= 0f)))
			{
				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)
		{
			MonsterAI val = (MonsterAI)(object)((__instance is MonsterAI) ? __instance : null);
			if ((Object)(object)val != (Object)null && !val.m_eventCreature && ((Object)(object)__instance.m_character == (Object)null || !__instance.m_character.IsBoss()))
			{
				return true;
			}
			if ((Object)(object)__instance.m_character != (Object)null && CreatureRules.ShouldPersist(__instance.m_character, val))
			{
				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 (!__instance.m_eventCreature || !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)
			{
				MonsterAI val = (MonsterAI)(object)((__instance is MonsterAI) ? __instance : null);
				if (val != null && val.m_eventCreature && 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)
			{
				MonsterAI val = (MonsterAI)(object)((__instance is MonsterAI) ? __instance : null);
				if (val != null && val.m_eventCreature && 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)
		{
			MonsterAI val = (MonsterAI)(object)((__instance is MonsterAI) ? __instance : null);
			if (val != null && val.m_eventCreature && CreatureRules.ShouldKeepHunt(__instance))
			{
				__result = false;
				return false;
			}
			return true;
		}
	}
	[HarmonyPatch(typeof(MonsterAI), "UpdateAI")]
	internal static class PatchMonsterUpdateAI
	{
		private static void Postfix(MonsterAI __instance)
		{
			if (__instance.m_eventCreature && CreatureRules.ShouldKeepHunt((BaseAI)(object)__instance))
			{
				CreatureRules.EnforceFightAi(__instance);
			}
		}
	}
	[HarmonyPatch(typeof(MonsterAI), "Awake")]
	internal static class PatchMonsterAwake
	{
		private static void Postfix(MonsterAI __instance)
		{
			if ((Object)(object)((BaseAI)__instance).m_character != (Object)null && ((BaseAI)__instance).m_character.IsBoss())
			{
				CreatureRules.ApplyPersistence(__instance);
			}
			else if (__instance.IsEventCreature())
			{
				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);
			if ((Object)(object)((BaseAI)__instance).m_nview != (Object)null && ((BaseAI)__instance).m_nview.IsValid())
			{
				CapService.Track(((BaseAI)__instance).m_nview.GetZDO());
			}
		}
	}
	[HarmonyPatch(typeof(MonsterAI), "SetDespawnInDay")]
	internal static class PatchSetDespawnInDay
	{
		private static bool Prefix(MonsterAI __instance, ref bool despawn)
		{
			if (despawn && __instance.m_eventCreature && CreatureRules.RulesActive() && ModConfig.NeverDespawnNightMonsters.Value && CreatureRules.PassesFilter(((BaseAI)__instance).m_character))
			{
				despawn = false;
			}
			return true;
		}
	}
	internal static class ModConfig
	{
		internal enum CapScopeMode
		{
			ServerWide,
			PerPlayerArea,
			Both
		}

		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<float> EndEventAfterNoPlayersInAreaMinutes;

		internal static ConfigEntry<float> MaxRaidWallClockMinutes;

		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<CapScopeMode> CapScope;

		internal static ConfigEntry<int> MaxActiveMonsters;

		internal static ConfigEntry<int> MaxPerPlayerArea;

		internal static ConfigEntry<float> PlayerAreaRadius;

		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."));
			EndEventAfterNoPlayersInAreaMinutes = config.Bind<float>("RaidTimeout", "EndEventAfterNoPlayersInAreaMinutes", 25f, Admin("When no player is in the raid event circle this many real minutes, end the random event so Custom Raids can roll again. Raid creatures may remain (EventMonstersSurviveRaidEnd). 0 = vanilla pause forever."));
			MaxRaidWallClockMinutes = config.Bind<float>("RaidTimeout", "MaxRaidWallClockMinutes", 90f, Admin("Force-end the random event this many real minutes after it started, even if PauseIfNoPlayerInArea froze the timer. Safety net for stuck events. 0 = off."));
			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("Cap how many raid creatures can exist on the server at once. Only raid creatures count. Tames, wildlife, normal spawns, and bosses are never counted or removed."));
			CapScope = config.Bind<CapScopeMode>("Safety", "CapScope", CapScopeMode.ServerWide, Admin("ServerWide = MaxActiveMonsters limits raid creatures on the whole server. PerPlayerArea = MaxPerPlayerArea limits raid creatures within PlayerAreaRadius of each player, which is what affects lag in a fight. Both = apply both limits."));
			MaxActiveMonsters = config.Bind<int>("Safety", "MaxActiveMonsters", 60, Admin("Most raid creatures allowed on the whole server, including ones from earlier raids that are still alive. Used by ServerWide and Both."));
			MaxPerPlayerArea = config.Bind<int>("Safety", "MaxPerPlayerArea", 40, Admin("Most raid creatures allowed within PlayerAreaRadius of any one player. Extras furthest from that player are removed first. Used by PerPlayerArea and Both."));
			PlayerAreaRadius = config.Bind<float>("Safety", "PlayerAreaRadius", 200f, Admin("Radius in meters around each player for MaxPerPlayerArea. About 200 covers the area a player has loaded."));
			CapBehavior = config.Bind<int>("Safety", "CapBehavior", 2, Admin("0 = only log when the cap is passed. 1 or 2 = remove the raid creatures furthest from any player until back at the cap."));
			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", 2f, Admin("Minutes between cap checks on the server. 0 turns the automatic check off (persistraid cap still works)."));
			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.10")]
	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.10";

		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.10 loaded. Custom Raids owns raid start. This mod only persists raid creatures.");
		}

		private void TickCap()
		{
			CapService.EnforceCap();
		}
	}
	internal static class RaidEventTimeout
	{
		private static float _raidStartedAt;

		private static float _lastPlayerInAreaAt;

		private static float _nextAreaCheck;

		private static string _trackedName;

		internal static void Reset()
		{
			_trackedName = null;
			_nextAreaCheck = 0f;
		}

		internal static void OnEventSet(RandomEvent ev)
		{
			if (ev == null)
			{
				Reset();
				return;
			}
			_raidStartedAt = Time.time;
			_lastPlayerInAreaAt = Time.time;
			_nextAreaCheck = 0f;
			_trackedName = ev.m_name;
		}

		internal static void Tick(RandEventSystem system)
		{
			if (!CreatureRules.RulesActive() || (Object)(object)system == (Object)null)
			{
				return;
			}
			RandomEvent randomEvent = system.m_randomEvent;
			if (randomEvent == null)
			{
				Reset();
				return;
			}
			float value = ModConfig.EndEventAfterNoPlayersInAreaMinutes.Value;
			float value2 = ModConfig.MaxRaidWallClockMinutes.Value;
			if (value <= 0f && value2 <= 0f)
			{
				return;
			}
			if (_trackedName != randomEvent.m_name)
			{
				OnEventSet(randomEvent);
			}
			float time = Time.time;
			if (value2 > 0f && time - _raidStartedAt >= value2 * 60f)
			{
				EndEvent(system, randomEvent, "wall-clock max " + value2 + " min");
			}
			else if (!(time < _nextAreaCheck))
			{
				_nextAreaCheck = time + 1f;
				if (system.IsAnyPlayerInEventArea(randomEvent))
				{
					_lastPlayerInAreaAt = time;
				}
				else if (value > 0f && time - _lastPlayerInAreaAt >= value * 60f)
				{
					EndEvent(system, randomEvent, "no players in event area for " + value + " min");
				}
			}
		}

		private static void EndEvent(RandEventSystem system, RandomEvent ev, string reason)
		{
			Plugin.Log.LogInfo((object)("PersistentRaids: ending raid '" + ev.m_name + "' (" + reason + ")"));
			CreatureRules.DebugLog("Raid timeout " + ev.m_name + ": " + reason);
			Reset();
			system.ResetRandomEvent();
		}
	}
	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)
		{
			Stashed.Clear();
			RaidEventTimeout.Reset();
			if ((Object)(object)system != (Object)null)
			{
				system.ResetRandomEvent();
			}
		}

		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);
				RaidEventTimeout.OnEventSet(__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())
			{
				RaidEventTimeout.Tick(__instance);
				if (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)
		{
		}
	}
}