Decompiled source of PlayerThreatLevel v1.2.1

PlayerThreatLevel.dll

Decompiled 11 hours ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Threading;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using ConditionalConfigSync;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: AssemblyCompany("PlayerThreatLevel")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.2.1.0")]
[assembly: AssemblyInformationalVersion("1.2.1")]
[assembly: AssemblyProduct("PlayerThreatLevel")]
[assembly: AssemblyTitle("PlayerThreatLevel")]
[assembly: AssemblyVersion("1.2.1.0")]
[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 PlayerThreatLevel
{
	internal sealed class BossInfo
	{
		public readonly Biome Biome;

		public readonly string BiomeName;

		public readonly string BossName;

		public BossInfo(Biome biome, string biomeName, string bossName)
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			Biome = biome;
			BiomeName = biomeName;
			BossName = bossName;
		}
	}
	[BepInPlugin("com.dreich.playerthreatlevel", "Player Threat Level", "1.2.1")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public class PlayerThreatLevel : BaseUnityPlugin
	{
		public const string Guid = "com.dreich.playerthreatlevel";

		public const string Name = "Player Threat Level";

		public const string Version = "1.2.1";

		internal const string ConfigSyncGuid = "_shudnal.ConditionalConfigSync";

		internal static PlayerThreatLevel Instance;

		internal static ManualLogSource Log;

		internal static bool ConfigSyncAvailable;

		internal static readonly Dictionary<string, BossInfo> Bosses = new Dictionary<string, BossInfo>
		{
			{
				"defeated_eikthyr",
				new BossInfo((Biome)1, "Meadows", "Eikthyr")
			},
			{
				"defeated_gdking",
				new BossInfo((Biome)8, "Black Forest", "The Elder")
			},
			{
				"defeated_bonemass",
				new BossInfo((Biome)2, "Swamp", "Bonemass")
			},
			{
				"defeated_dragon",
				new BossInfo((Biome)4, "Mountain", "Moder")
			},
			{
				"defeated_goblinking",
				new BossInfo((Biome)16, "Plains", "Yagluth")
			},
			{
				"defeated_queen",
				new BossInfo((Biome)512, "Mistlands", "The Queen")
			},
			{
				"defeated_fader",
				new BossInfo((Biome)32, "Ashlands", "Fader")
			},
			{
				"defeated_frozenking",
				new BossInfo((Biome)64, "Deep North", "Kall Fimbulbringer")
			}
		};

		private static ConfigEntry<bool> ConfigLockedEntry;

		private static ConfigEntry<float> BaseFearRadiusEntry;

		private static ConfigEntry<float> FearRadiusPerLevelEntry;

		private static ConfigEntry<float> FearDurationEntry;

		private static ConfigEntry<bool> BossesImmuneEntry;

		private static ConfigEntry<bool> StarredImmuneEntry;

		private static ConfigEntry<bool> SeedFromDefeatedBossesEntry;

		private static ConfigEntry<bool> HostileIfAttackedEntry;

		private static ConfigEntry<float> HostileDurationEntry;

		private static ConfigEntry<float> FearImmunityAfterAttackEntry;

		private static ConfigEntry<bool> AlertWhenFleeingEntry;

		private static ConfigEntry<float> AllyCourageRadiusEntry;

		internal static ConfigEntry<bool> ShowMessages;

		internal static ConfigEntry<MessageType> MessageLocation;

		internal static ConfigEntry<string> LevelUpMessage;

		internal static ConfigEntry<string> MaxLevelMessage;

		private FileSystemWatcher watcher;

		private volatile bool reloadPending;

		private long lastChangeTicks;

		private long ignoreUntilTicks;

		private static readonly SortedDictionary<string, ConfigEntry<bool>> CreatureToggles = new SortedDictionary<string, ConfigEntry<bool>>(StringComparer.Ordinal);

		internal static float BaseFearRadius => BaseFearRadiusEntry.Value;

		internal static float FearRadiusPerLevel => FearRadiusPerLevelEntry.Value;

		internal static float FearDuration => FearDurationEntry.Value;

		internal static bool BossesImmune => BossesImmuneEntry.Value;

		internal static bool StarredImmune => StarredImmuneEntry.Value;

		internal static bool SeedFromDefeatedBosses => SeedFromDefeatedBossesEntry.Value;

		internal static bool HostileIfAttacked => HostileIfAttackedEntry.Value;

		internal static float HostileDuration => HostileDurationEntry.Value;

		internal static float FearImmunityAfterAttack => FearImmunityAfterAttackEntry.Value;

		internal static bool AlertWhenFleeing => AlertWhenFleeingEntry.Value;

		internal static float AllyCourageRadius => AllyCourageRadiusEntry.Value;

		private void Awake()
		{
			//IL_0242: Unknown result type (might be due to invalid IL or missing references)
			Instance = this;
			Log = ((BaseUnityPlugin)this).Logger;
			ConfigSyncAvailable = Chainloader.PluginInfos.ContainsKey("_shudnal.ConditionalConfigSync");
			if (ConfigSyncAvailable)
			{
				ConfigSyncCompat.Initialize("com.dreich.playerthreatlevel", "Player Threat Level", "1.2.1");
			}
			else
			{
				Log.LogInfo((object)"ConditionalConfigSync not found; configuration will not be synced from the server.");
			}
			ConfigLockedEntry = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Lock Configuration", true, "If on, the configuration is locked and can be changed by server admins only. Requires ConditionalConfigSync.");
			if (ConfigSyncAvailable)
			{
				ConfigSyncCompat.AddLockingConfigEntry(ConfigLockedEntry);
			}
			BaseFearRadiusEntry = BindShared("Fear", "BaseFearRadius", 12f, "Distance (m) at which monsters start fleeing at threat level 1.");
			FearRadiusPerLevelEntry = BindShared("Fear", "FearRadiusPerLevel", 4f, "Extra fear radius (m) for each threat level after the first.");
			FearDurationEntry = BindShared("Fear", "FearDuration", 6f, "Seconds a monster keeps running after it last sensed you.");
			BossesImmuneEntry = BindShared("Fear", "BossesImmune", defaultValue: true, "Bosses are never scared.");
			StarredImmuneEntry = BindShared("Fear", "StarredImmune", defaultValue: true, "Creatures with one or more stars are never scared.");
			HostileIfAttackedEntry = BindShared("Fear", "HostileIfAttacked", defaultValue: true, "If you attack a monster that fears you, it stops fleeing and fights back.");
			HostileDurationEntry = BindShared("Fear", "HostileDuration", 30f, "Seconds a monster stays unafraid of the player who last hit it. 0 means it never fears that player again.");
			FearImmunityAfterAttackEntry = BindShared("Fear", "FearImmunityAfterAttack", 20f, "Seconds a monster cannot become afraid of any player after any player hits it, even if a different player comes near. 0 disables.");
			AlertWhenFleeingEntry = BindShared("Fear", "AlertWhenFleeing", defaultValue: true, "Fleeing monsters show the red exclamation mark and play their alert sound, like deer do. Runs on the peer that owns the monster, so it is server-controlled.");
			AllyCourageRadiusEntry = BindShared("Fear", "AllyCourageRadius", 10f, "Distance (m) a frightened monster checks around itself for other living monsters of its own faction. If it finds any, it is no longer afraid. 0 disables the check.");
			SeedFromDefeatedBossesEntry = BindShared("Progress", "CheckDefeatedBosses", defaultValue: true, "On spawn, count bosses already defeated in this world toward your threat level.");
			ShowMessages = BindLocal("Messages", "ShowMessages", defaultValue: true, "Show a message when your threat level rises.");
			MessageLocation = BindLocal<MessageType>("Messages", "MessageLocation", (MessageType)2, "Where the message appears: Center or TopLeft.");
			LevelUpMessage = BindLocal("Messages", "LevelUpMessage", "Threat Level {level}\\nThe {biome} fears you", "Message shown when a boss kill raises your threat level. Placeholders: {level}, {max}, {biome}, {boss}. Use \\n for a line break. Leave empty to show nothing.");
			MaxLevelMessage = BindLocal("Messages", "MaxLevelMessage", "Threat Level {level}\\nAll of Valheim fears you", "Message shown instead when you reach the maximum threat level. Same placeholders. Leave empty to use LevelUpMessage.");
			SetupWatcher();
			new Harmony("com.dreich.playerthreatlevel").PatchAll();
		}

		internal static bool IsCreatureEnabled(string prefabName)
		{
			if (!string.IsNullOrEmpty(prefabName) && CreatureToggles.TryGetValue(prefabName, out var value))
			{
				return value.Value;
			}
			return true;
		}

		internal static IEnumerable<string> DisabledCreatures()
		{
			foreach (KeyValuePair<string, ConfigEntry<bool>> creatureToggle in CreatureToggles)
			{
				if (!creatureToggle.Value.Value)
				{
					yield return creatureToggle.Key;
				}
			}
		}

		internal static void RegisterCreatures(IEnumerable<GameObject> prefabs)
		{
			if ((Object)(object)Instance == (Object)null || prefabs == null)
			{
				return;
			}
			SortedDictionary<string, bool> sortedDictionary = new SortedDictionary<string, bool>(StringComparer.Ordinal);
			foreach (GameObject prefab in prefabs)
			{
				if (!((Object)(object)prefab == (Object)null) && !((Object)(object)prefab.GetComponent<MonsterAI>() == (Object)null) && !((Object)(object)prefab.GetComponent<Character>() == (Object)null) && !((Object)(object)prefab.GetComponent<Player>() != (Object)null))
				{
					sortedDictionary[((Object)prefab).name] = true;
				}
			}
			ConfigFile config = ((BaseUnityPlugin)Instance).Config;
			bool saveOnConfigSet = config.SaveOnConfigSet;
			config.SaveOnConfigSet = false;
			int num = 0;
			try
			{
				foreach (string key in sortedDictionary.Keys)
				{
					if (!CreatureToggles.ContainsKey(key))
					{
						CreatureToggles[key] = Instance.BindShared("Creatures", key, defaultValue: true, "Creatures with this prefab name flee from you when your threat level is high enough. Server-controlled.");
						num++;
					}
				}
			}
			finally
			{
				config.SaveOnConfigSet = saveOnConfigSet;
			}
			if (num > 0)
			{
				config.Save();
				Log.LogInfo((object)("Registered " + num + " creature toggles."));
			}
		}

		private ConfigEntry<T> BindShared<T>(string section, string key, T defaultValue, string description)
		{
			ConfigEntry<T> val = ((BaseUnityPlugin)this).Config.Bind<T>(section, key, defaultValue, description);
			if (ConfigSyncAvailable)
			{
				ConfigSyncCompat.AddConfigEntry<T>(val, serverControlled: true);
			}
			return val;
		}

		private ConfigEntry<T> BindLocal<T>(string section, string key, T defaultValue, string description)
		{
			ConfigEntry<T> val = ((BaseUnityPlugin)this).Config.Bind<T>(section, key, defaultValue, description);
			if (ConfigSyncAvailable)
			{
				ConfigSyncCompat.AddConfigEntry<T>(val, serverControlled: false);
			}
			return val;
		}

		internal static bool IsSyncedFromServer()
		{
			if (ConfigSyncAvailable)
			{
				return ConfigSyncCompat.InitialSyncDone();
			}
			return false;
		}

		private void SetupWatcher()
		{
			try
			{
				string configFilePath = ((BaseUnityPlugin)this).Config.ConfigFilePath;
				watcher = new FileSystemWatcher(Path.GetDirectoryName(configFilePath), Path.GetFileName(configFilePath));
				watcher.NotifyFilter = NotifyFilters.Size | NotifyFilters.LastWrite;
				watcher.Changed += OnConfigFileChanged;
				watcher.EnableRaisingEvents = true;
			}
			catch (Exception ex)
			{
				Log.LogWarning((object)("Config hot reload unavailable: " + ex.Message));
			}
		}

		private void OnConfigFileChanged(object sender, FileSystemEventArgs e)
		{
			long ticks = DateTime.UtcNow.Ticks;
			if (ticks >= Interlocked.Read(in ignoreUntilTicks))
			{
				Interlocked.Exchange(ref lastChangeTicks, ticks);
				reloadPending = true;
			}
		}

		private void Update()
		{
			BossKeyWatcher.Check();
			if (reloadPending && DateTime.UtcNow.Ticks - Interlocked.Read(in lastChangeTicks) > 5000000)
			{
				reloadPending = false;
				Interlocked.Exchange(ref ignoreUntilTicks, DateTime.UtcNow.Ticks + 20000000);
				try
				{
					((BaseUnityPlugin)this).Config.Reload();
					Log.LogInfo((object)"Config reloaded.");
				}
				catch (Exception ex)
				{
					Log.LogWarning((object)("Config reload failed: " + ex.Message));
				}
			}
		}

		private void OnDestroy()
		{
			if (watcher != null)
			{
				watcher.EnableRaisingEvents = false;
				watcher.Dispose();
			}
		}
	}
	internal static class ConfigSyncCompat
	{
		private static object configSync;

		[MethodImpl(MethodImplOptions.NoInlining)]
		internal static void Initialize(string guid, string name, string version)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: Expected O, but got Unknown
			configSync = (object)new ConfigSync(guid)
			{
				DisplayName = name,
				CurrentVersion = version,
				MinimumRequiredVersion = version,
				ModRequired = false,
				ModRequirementMode = (ModRequirementMode)1
			};
		}

		[MethodImpl(MethodImplOptions.NoInlining)]
		internal static void AddLockingConfigEntry(ConfigEntry<bool> entry)
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			((ConditionalConfigSync)(ConfigSync)configSync).AddLockingConfigEntry<bool>(entry);
		}

		[MethodImpl(MethodImplOptions.NoInlining)]
		internal static void AddConfigEntry<T>(ConfigEntry<T> entry, bool serverControlled)
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			((ConditionalConfigSync)(ConfigSync)configSync).AddConfigEntry<T>(entry, (ConfigSyncMode)((!serverControlled) ? 2 : 0));
		}

		[MethodImpl(MethodImplOptions.NoInlining)]
		internal static bool InitialSyncDone()
		{
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			if (configSync != null)
			{
				return ((ConditionalConfigSync)(ConfigSync)configSync).InitialSyncDone;
			}
			return false;
		}
	}
	internal static class BossKeyWatcher
	{
		private const float CheckInterval = 1f;

		private static readonly HashSet<string> SeenKeys = new HashSet<string>();

		private static Player trackedPlayer;

		private static float nextCheck;

		public static void Check()
		{
			float unscaledTime = Time.unscaledTime;
			if (unscaledTime < nextCheck)
			{
				return;
			}
			nextCheck = unscaledTime + 1f;
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null || (Object)(object)ZoneSystem.instance == (Object)null)
			{
				trackedPlayer = null;
				return;
			}
			if ((Object)(object)localPlayer != (Object)(object)trackedPlayer)
			{
				trackedPlayer = localPlayer;
				SeenKeys.Clear();
				{
					foreach (string key in PlayerThreatLevel.Bosses.Keys)
					{
						if (ZoneSystem.instance.GetGlobalKey(key))
						{
							SeenKeys.Add(key);
						}
					}
					return;
				}
			}
			foreach (KeyValuePair<string, BossInfo> boss in PlayerThreatLevel.Bosses)
			{
				if (!SeenKeys.Contains(boss.Key) && ZoneSystem.instance.GetGlobalKey(boss.Key))
				{
					SeenKeys.Add(boss.Key);
					if (!((Character)localPlayer).IsDead())
					{
						Threat.AddBiome(localPlayer, boss.Value);
					}
				}
			}
		}
	}
	internal static class Threat
	{
		private const string SaveKey = "ThreatLevel_BiomeMask";

		private static readonly int ZdoKey = StringExtensionMethods.GetStableHashCode("ThreatLevel_BiomeMask");

		private static int AllBossBits()
		{
			//IL_001e: 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_0025: Expected I4, but got Unknown
			int num = 0;
			foreach (BossInfo value in PlayerThreatLevel.Bosses.Values)
			{
				num |= value.Biome;
			}
			return num;
		}

		public static int GetMask(Player p)
		{
			if ((Object)(object)p == (Object)null)
			{
				return 0;
			}
			ZNetView component = ((Component)p).GetComponent<ZNetView>();
			if ((Object)(object)component == (Object)null || !component.IsValid())
			{
				return 0;
			}
			return component.GetZDO().GetInt(ZdoKey, 0);
		}

		public static void SetMask(Player p, int mask)
		{
			ZNetView component = ((Component)p).GetComponent<ZNetView>();
			if (!((Object)(object)component == (Object)null) && component.IsValid() && component.IsOwner())
			{
				component.GetZDO().Set(ZdoKey, mask, false);
				p.m_customData["ThreatLevel_BiomeMask"] = mask.ToString();
			}
		}

		public static int Level(int mask)
		{
			mask &= AllBossBits();
			int num = 0;
			while (mask != 0)
			{
				num += mask & 1;
				mask >>= 1;
			}
			return num;
		}

		public static int Level(Player p)
		{
			return Level(GetMask(p));
		}

		public static BossInfo InfoFor(Biome biome)
		{
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			foreach (BossInfo value in PlayerThreatLevel.Bosses.Values)
			{
				if (value.Biome == biome)
				{
					return value;
				}
			}
			return null;
		}

		public static string FormatMessage(string template, int level, BossInfo info)
		{
			return template.Replace("\\n", "\n").Replace("{level}", level.ToString()).Replace("{max}", PlayerThreatLevel.Bosses.Count.ToString())
				.Replace("{biome}", info.BiomeName)
				.Replace("{boss}", info.BossName);
		}

		public static void AddBiome(Player p, BossInfo info, bool announce = true)
		{
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Expected I4, but got Unknown
			//IL_0076: Unknown result type (might be due to invalid IL or missing references)
			int mask = GetMask(p);
			int num = mask | info.Biome;
			if (num == mask)
			{
				return;
			}
			SetMask(p, num);
			if (announce && PlayerThreatLevel.ShowMessages.Value)
			{
				int num2 = Level(num);
				string value = PlayerThreatLevel.LevelUpMessage.Value;
				if (num2 >= PlayerThreatLevel.Bosses.Count && !string.IsNullOrEmpty(PlayerThreatLevel.MaxLevelMessage.Value))
				{
					value = PlayerThreatLevel.MaxLevelMessage.Value;
				}
				if (!string.IsNullOrEmpty(value))
				{
					((Character)p).Message(PlayerThreatLevel.MessageLocation.Value, FormatMessage(value, num2, info), 0, (Sprite)null, false);
				}
			}
		}

		public static void LoadFromSave(Player p)
		{
			//IL_006e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0073: Unknown result type (might be due to invalid IL or missing references)
			//IL_0075: Expected I4, but got Unknown
			int num = GetMask(p);
			if (p.m_customData.TryGetValue("ThreatLevel_BiomeMask", out var value) && int.TryParse(value, out var result))
			{
				num |= result;
			}
			if (PlayerThreatLevel.SeedFromDefeatedBosses && (Object)(object)ZoneSystem.instance != (Object)null)
			{
				foreach (KeyValuePair<string, BossInfo> boss in PlayerThreatLevel.Bosses)
				{
					if (ZoneSystem.instance.GetGlobalKey(boss.Key))
					{
						num |= boss.Value.Biome;
					}
				}
			}
			SetMask(p, num);
		}

		public static string BiomeList(int mask)
		{
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			List<string> list = new List<string>();
			foreach (BossInfo value in PlayerThreatLevel.Bosses.Values)
			{
				if ((mask & value.Biome) != 0)
				{
					list.Add(value.BiomeName);
				}
			}
			if (list.Count != 0)
			{
				return string.Join(", ", list.ToArray());
			}
			return "none";
		}
	}
	[HarmonyPatch(typeof(Player), "OnSpawned")]
	internal static class PlayerSpawnedPatch
	{
		private static void Postfix(Player __instance)
		{
			if ((Object)(object)__instance == (Object)(object)Player.m_localPlayer)
			{
				Threat.LoadFromSave(__instance);
			}
		}
	}
	[HarmonyPatch(typeof(ZNetScene), "Awake")]
	internal static class CreatureRegisterPatch
	{
		private static void Postfix(ZNetScene __instance)
		{
			PlayerThreatLevel.RegisterCreatures(__instance.m_prefabs);
		}
	}
	[HarmonyPatch(typeof(MonsterAI), "UpdateAI")]
	internal static class MonsterFearPatch
	{
		private class State
		{
			public float NextCheck;

			public float AfraidUntil;

			public Player ScaredOf;

			public Character Char;

			public ZNetView NView;

			public string PrefabName;
		}

		private static readonly int ProvokedByKey = StringExtensionMethods.GetStableHashCode("ThreatLevel_ProvokedBy");

		private static readonly int ProvokedUntilKey = StringExtensionMethods.GetStableHashCode("ThreatLevel_ProvokedUntil");

		private static readonly int FearlessUntilKey = StringExtensionMethods.GetStableHashCode("ThreatLevel_FearlessUntil");

		private static readonly ConditionalWeakTable<MonsterAI, State> States = new ConditionalWeakTable<MonsterAI, State>();

		private static readonly Func<BaseAI, float, Vector3, bool> Flee = MakeFlee();

		private static readonly Action<BaseAI, bool> SetAlerted = MakeSetAlerted();

		private static long NowTicks()
		{
			if (!((Object)(object)ZNet.instance != (Object)null))
			{
				return DateTime.UtcNow.Ticks;
			}
			return ZNet.instance.GetTime().Ticks;
		}

		private static long SecondsToTicks(float seconds)
		{
			return (long)(seconds * 10000000f);
		}

		internal static void OnAttacked(MonsterAI ai, Player attacker)
		{
			ZNetView component = ((Component)ai).GetComponent<ZNetView>();
			if ((Object)(object)component == (Object)null || !component.IsValid() || !component.IsOwner())
			{
				return;
			}
			ZDO zDO = component.GetZDO();
			long num = NowTicks();
			bool flag = false;
			if (PlayerThreatLevel.HostileIfAttacked)
			{
				float hostileDuration = PlayerThreatLevel.HostileDuration;
				zDO.Set(ProvokedByKey, attacker.GetPlayerID());
				zDO.Set(ProvokedUntilKey, (hostileDuration <= 0f) ? long.MaxValue : (num + SecondsToTicks(hostileDuration)));
				flag = true;
			}
			float fearImmunityAfterAttack = PlayerThreatLevel.FearImmunityAfterAttack;
			if (fearImmunityAfterAttack > 0f)
			{
				long num2 = num + SecondsToTicks(fearImmunityAfterAttack);
				if (num2 > zDO.GetLong(FearlessUntilKey, 0L))
				{
					zDO.Set(FearlessUntilKey, num2);
				}
				flag = true;
			}
			if (flag)
			{
				State orCreateValue = States.GetOrCreateValue(ai);
				orCreateValue.AfraidUntil = 0f;
				orCreateValue.ScaredOf = null;
				orCreateValue.NextCheck = 0f;
			}
		}

		private static Func<BaseAI, float, Vector3, bool> MakeFlee()
		{
			try
			{
				MethodInfo method = typeof(BaseAI).GetMethod("Flee", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[2]
				{
					typeof(float),
					typeof(Vector3)
				}, null);
				return (Func<BaseAI, float, Vector3, bool>)Delegate.CreateDelegate(typeof(Func<BaseAI, float, Vector3, bool>), method);
			}
			catch (Exception ex)
			{
				PlayerThreatLevel.Log.LogError((object)("Could not bind BaseAI.Flee - fear disabled: " + ex.Message));
				return null;
			}
		}

		private static Action<BaseAI, bool> MakeSetAlerted()
		{
			try
			{
				MethodInfo method = typeof(BaseAI).GetMethod("SetAlerted", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[1] { typeof(bool) }, null);
				return (Action<BaseAI, bool>)Delegate.CreateDelegate(typeof(Action<BaseAI, bool>), method);
			}
			catch (Exception ex)
			{
				PlayerThreatLevel.Log.LogWarning((object)("Could not bind BaseAI.SetAlerted - no exclamation mark on fleeing monsters: " + ex.Message));
				return null;
			}
		}

		private static bool HasAlliesNearby(Character self, float radius)
		{
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0064: Unknown result type (might be due to invalid IL or missing references)
			//IL_006e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0073: 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)
			//IL_0079: Unknown result type (might be due to invalid IL or missing references)
			if (radius <= 0f)
			{
				return false;
			}
			Vector3 position = ((Component)self).transform.position;
			float num = radius * radius;
			Faction faction = self.GetFaction();
			foreach (Character allCharacter in Character.GetAllCharacters())
			{
				if (!((Object)(object)allCharacter == (Object)null) && !((Object)(object)allCharacter == (Object)(object)self) && !(allCharacter is Player) && !allCharacter.IsDead() && allCharacter.GetFaction() == faction)
				{
					Vector3 val = ((Component)allCharacter).transform.position - position;
					if (!(((Vector3)(ref val)).sqrMagnitude > num))
					{
						return true;
					}
				}
			}
			return false;
		}

		private static Player FindScaryPlayer(Vector3 pos, long ignoredPlayerId)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0070: Unknown result type (might be due to invalid IL or missing references)
			//IL_0078: Unknown result type (might be due to invalid IL or missing references)
			//IL_009d: Unknown result type (might be due to invalid IL or missing references)
			//IL_009e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0092: Unknown result type (might be due to invalid IL or missing references)
			//IL_0093: Unknown result type (might be due to invalid IL or missing references)
			//IL_0098: Unknown result type (might be due to invalid IL or missing references)
			Biome val = (Biome)0;
			bool flag = false;
			Player result = null;
			float num = float.MaxValue;
			foreach (Player allPlayer in Player.GetAllPlayers())
			{
				if ((Object)(object)allPlayer == (Object)null || ((Character)allPlayer).IsDead() || (ignoredPlayerId != 0L && allPlayer.GetPlayerID() == ignoredPlayerId))
				{
					continue;
				}
				int mask = Threat.GetMask(allPlayer);
				int num2 = Threat.Level(mask);
				if (num2 <= 0)
				{
					continue;
				}
				float num3 = PlayerThreatLevel.BaseFearRadius + PlayerThreatLevel.FearRadiusPerLevel * (float)(num2 - 1);
				float num4 = Vector3.Distance(pos, ((Component)allPlayer).transform.position);
				if (!(num4 > num3) && !(num4 >= num))
				{
					if (!flag)
					{
						val = Heightmap.FindBiome(pos);
						flag = true;
					}
					if ((mask & val) != 0)
					{
						result = allPlayer;
						num = num4;
					}
				}
			}
			return result;
		}

		private static bool Prefix(MonsterAI __instance, float dt, ref bool __result)
		{
			//IL_0210: Unknown result type (might be due to invalid IL or missing references)
			//IL_016d: Unknown result type (might be due to invalid IL or missing references)
			if (Flee == null)
			{
				return true;
			}
			State orCreateValue = States.GetOrCreateValue(__instance);
			if ((Object)(object)orCreateValue.Char == (Object)null)
			{
				orCreateValue.Char = ((Component)__instance).GetComponent<Character>();
				orCreateValue.NView = ((Component)__instance).GetComponent<ZNetView>();
				orCreateValue.PrefabName = Utils.GetPrefabName(((Component)__instance).gameObject);
			}
			if ((Object)(object)orCreateValue.Char == (Object)null || (Object)(object)orCreateValue.NView == (Object)null || !orCreateValue.NView.IsValid() || !orCreateValue.NView.IsOwner())
			{
				return true;
			}
			if (orCreateValue.Char.IsDead() || orCreateValue.Char.IsTamed())
			{
				return true;
			}
			if (PlayerThreatLevel.BossesImmune && orCreateValue.Char.IsBoss())
			{
				return true;
			}
			if (PlayerThreatLevel.StarredImmune && orCreateValue.Char.GetLevel() >= 2)
			{
				return true;
			}
			if (!PlayerThreatLevel.IsCreatureEnabled(orCreateValue.PrefabName))
			{
				return true;
			}
			float time = Time.time;
			if (time >= orCreateValue.NextCheck)
			{
				orCreateValue.NextCheck = time + 0.25f;
				ZDO zDO = orCreateValue.NView.GetZDO();
				long num = NowTicks();
				if (zDO.GetLong(FearlessUntilKey, 0L) > num)
				{
					orCreateValue.AfraidUntil = 0f;
					orCreateValue.ScaredOf = null;
				}
				else
				{
					long ignoredPlayerId = ((zDO.GetLong(ProvokedUntilKey, 0L) > num) ? zDO.GetLong(ProvokedByKey, 0L) : 0);
					bool flag = time < orCreateValue.AfraidUntil;
					Player val = FindScaryPlayer(((Component)orCreateValue.Char).transform.position, ignoredPlayerId);
					if ((Object)(object)val != (Object)null || flag)
					{
						if (HasAlliesNearby(orCreateValue.Char, PlayerThreatLevel.AllyCourageRadius))
						{
							orCreateValue.AfraidUntil = 0f;
							orCreateValue.ScaredOf = null;
						}
						else if ((Object)(object)val != (Object)null)
						{
							orCreateValue.ScaredOf = val;
							orCreateValue.AfraidUntil = time + PlayerThreatLevel.FearDuration;
						}
					}
				}
			}
			if (time < orCreateValue.AfraidUntil && (Object)(object)orCreateValue.ScaredOf != (Object)null)
			{
				if (SetAlerted != null && PlayerThreatLevel.AlertWhenFleeing)
				{
					SetAlerted((BaseAI)(object)__instance, arg2: true);
				}
				Flee((BaseAI)(object)__instance, dt, ((Component)orCreateValue.ScaredOf).transform.position);
				__result = true;
				return false;
			}
			return true;
		}
	}
	[HarmonyPatch(typeof(MonsterAI), "OnDamaged")]
	internal static class MonsterDamagedPatch
	{
		private static void Postfix(MonsterAI __instance, Character attacker)
		{
			Player val = (Player)(object)((attacker is Player) ? attacker : null);
			if (!((Object)(object)val == (Object)null))
			{
				MonsterFearPatch.OnAttacked(__instance, val);
			}
		}
	}
	[HarmonyPatch(typeof(Terminal), "InitTerminal")]
	internal static class ThreatCommandPatch
	{
		[Serializable]
		[CompilerGenerated]
		private sealed class <>c
		{
			public static readonly <>c <>9 = new <>c();

			public static ConsoleEvent <>9__0_0;

			internal void <Postfix>b__0_0(ConsoleEventArgs args)
			{
				//IL_011d: Unknown result type (might be due to invalid IL or missing references)
				//IL_0122: Unknown result type (might be due to invalid IL or missing references)
				//IL_0148: Unknown result type (might be due to invalid IL or missing references)
				Player localPlayer = Player.m_localPlayer;
				if ((Object)(object)localPlayer == (Object)null)
				{
					args.Context.AddString("No local player.");
					return;
				}
				if (args.Length >= 2)
				{
					switch (args[1].ToLowerInvariant())
					{
					case "reset":
						Threat.SetMask(localPlayer, 0);
						break;
					case "creatures":
					{
						string text = string.Join(", ", new List<string>(PlayerThreatLevel.DisabledCreatures()).ToArray());
						args.Context.AddString("Creatures that ignore threat: " + ((text.Length == 0) ? "none" : text));
						return;
					}
					case "all":
						foreach (BossInfo value in PlayerThreatLevel.Bosses.Values)
						{
							Threat.AddBiome(localPlayer, value, announce: false);
						}
						break;
					case "add":
						if (args.Length >= 3)
						{
							Biome biome;
							try
							{
								biome = (Biome)Enum.Parse(typeof(Biome), args[2], ignoreCase: true);
							}
							catch
							{
								args.Context.AddString("Unknown biome: " + args[2]);
								return;
							}
							BossInfo bossInfo = Threat.InfoFor(biome);
							if (bossInfo == null)
							{
								args.Context.AddString("No boss is tied to " + args[2]);
							}
							else
							{
								Threat.AddBiome(localPlayer, bossInfo);
							}
						}
						break;
					}
				}
				int mask = Threat.GetMask(localPlayer);
				string text2 = (PlayerThreatLevel.IsSyncedFromServer() ? " (settings synced from server)" : "");
				args.Context.AddString("Threat level " + Threat.Level(mask) + "/" + PlayerThreatLevel.Bosses.Count + " - feared in: " + Threat.BiomeList(mask) + text2);
			}
		}

		private static void Postfix()
		{
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: 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_0029: Expected O, but got Unknown
			object obj = <>c.<>9__0_0;
			if (obj == null)
			{
				ConsoleEvent val = delegate(ConsoleEventArgs args)
				{
					//IL_011d: Unknown result type (might be due to invalid IL or missing references)
					//IL_0122: Unknown result type (might be due to invalid IL or missing references)
					//IL_0148: Unknown result type (might be due to invalid IL or missing references)
					Player localPlayer = Player.m_localPlayer;
					if ((Object)(object)localPlayer == (Object)null)
					{
						args.Context.AddString("No local player.");
					}
					else
					{
						if (args.Length >= 2)
						{
							switch (args[1].ToLowerInvariant())
							{
							case "reset":
								Threat.SetMask(localPlayer, 0);
								break;
							case "creatures":
							{
								string text = string.Join(", ", new List<string>(PlayerThreatLevel.DisabledCreatures()).ToArray());
								args.Context.AddString("Creatures that ignore threat: " + ((text.Length == 0) ? "none" : text));
								return;
							}
							case "all":
								foreach (BossInfo value in PlayerThreatLevel.Bosses.Values)
								{
									Threat.AddBiome(localPlayer, value, announce: false);
								}
								break;
							case "add":
								if (args.Length >= 3)
								{
									Biome biome;
									try
									{
										biome = (Biome)Enum.Parse(typeof(Biome), args[2], ignoreCase: true);
									}
									catch
									{
										args.Context.AddString("Unknown biome: " + args[2]);
										return;
									}
									BossInfo bossInfo = Threat.InfoFor(biome);
									if (bossInfo == null)
									{
										args.Context.AddString("No boss is tied to " + args[2]);
									}
									else
									{
										Threat.AddBiome(localPlayer, bossInfo);
									}
								}
								break;
							}
						}
						int mask = Threat.GetMask(localPlayer);
						string text2 = (PlayerThreatLevel.IsSyncedFromServer() ? " (settings synced from server)" : "");
						args.Context.AddString("Threat level " + Threat.Level(mask) + "/" + PlayerThreatLevel.Bosses.Count + " - feared in: " + Threat.BiomeList(mask) + text2);
					}
				};
				<>c.<>9__0_0 = val;
				obj = (object)val;
			}
			new ConsoleCommand("threat", "threat [reset | all | add <biome> | creatures] - view or edit your threat level", (ConsoleEvent)obj, true, false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false);
		}
	}
}