Decompiled source of TruePassiveMobs v1.2.0

TruePassiveMobs.dll

Decompiled 3 weeks 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.Security;
using System.Security.Permissions;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Jotunn.Utils;
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", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: AssemblyCompany("TruePassiveMobs")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.2.0.0")]
[assembly: AssemblyInformationalVersion("1.2.0+53f48bc56f818f83c6fabbb5e7629e9562735e07")]
[assembly: AssemblyProduct("TruePassiveMobs")]
[assembly: AssemblyTitle("TruePassiveMobs")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.2.0.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace TruePassiveMobs
{
	internal sealed class PlayerCombatProfile
	{
		private const byte FormatVersion = 1;

		public float MaxHealth;

		public float Armor;

		public float DamageTakenRate;

		public DamageModifiers Resistances;

		public DamageTypes WeaponHit;

		public static PlayerCombatProfile Compute(Player player)
		{
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			return new PlayerCombatProfile
			{
				MaxHealth = ((Character)player).GetMaxHealth(),
				Armor = ((Character)player).GetBodyArmor(),
				DamageTakenRate = Game.m_localDamgeTakenRate,
				Resistances = ((Character)player).GetDamageModifiers((WeakSpot)null),
				WeaponHit = EstimateWeaponHit(player)
			};
		}

		private static DamageTypes EstimateWeaponHit(Player player)
		{
			//IL_0015: 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_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Expected O, but got Unknown
			//IL_000c: 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_0095: Unknown result type (might be due to invalid IL or missing references)
			//IL_00db: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e8: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			ItemData currentWeapon = ((Humanoid)player).GetCurrentWeapon();
			if (currentWeapon == null)
			{
				return default(DamageTypes);
			}
			HitData val = new HitData
			{
				m_damage = currentWeapon.GetDamage()
			};
			if (!string.IsNullOrEmpty(currentWeapon.m_shared.m_ammoType))
			{
				ItemData val2 = Attack.FindAmmo((Humanoid)(object)player, currentWeapon);
				if (val2 == null)
				{
					return default(DamageTypes);
				}
				((DamageTypes)(ref val.m_damage)).Add(val2.GetDamage(), 1);
			}
			Attack attack = currentWeapon.m_shared.m_attack;
			if (attack != null)
			{
				((DamageTypes)(ref val.m_damage)).Modify(attack.m_damageMultiplier);
			}
			float num = Mathf.Lerp(0.4f, 1f, ((Character)player).GetSkillFactor(currentWeapon.m_shared.m_skillType));
			((DamageTypes)(ref val.m_damage)).Modify((Mathf.Clamp01(num - 0.15f) + Mathf.Clamp01(num + 0.15f)) * 0.5f);
			((Character)player).GetSEMan().ModifyAttack(currentWeapon.m_shared.m_skillType, ref val);
			return val.m_damage;
		}

		public byte[] Serialize()
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Expected O, but got Unknown
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_0048: Expected I4, but got Unknown
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0051: Expected I4, but got Unknown
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_005a: Expected I4, but got Unknown
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0063: Expected I4, but got Unknown
			//IL_0065: 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_006c: Expected I4, but got Unknown
			//IL_006e: Unknown result type (might be due to invalid IL or missing references)
			//IL_006f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0075: Expected I4, but got Unknown
			//IL_0077: 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_007e: Expected I4, but got Unknown
			//IL_0080: Unknown result type (might be due to invalid IL or missing references)
			//IL_0081: Unknown result type (might be due to invalid IL or missing references)
			//IL_0087: Expected I4, but got Unknown
			//IL_0089: Unknown result type (might be due to invalid IL or missing references)
			//IL_008a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0090: Expected I4, but got Unknown
			//IL_0093: Unknown result type (might be due to invalid IL or missing references)
			//IL_0094: Unknown result type (might be due to invalid IL or missing references)
			//IL_009a: Expected I4, but got Unknown
			//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_00a4: Expected I4, but got Unknown
			//IL_00ae: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b1: 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)
			//IL_00d6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00df: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fa: Unknown result type (might be due to invalid IL or missing references)
			//IL_0103: Unknown result type (might be due to invalid IL or missing references)
			//IL_010c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0115: Unknown result type (might be due to invalid IL or missing references)
			//IL_011e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0128: Unknown result type (might be due to invalid IL or missing references)
			//IL_0132: Unknown result type (might be due to invalid IL or missing references)
			//IL_013c: Unknown result type (might be due to invalid IL or missing references)
			ZPackage val = new ZPackage();
			val.Write((byte)1);
			val.Write(MaxHealth);
			val.Write(Armor);
			val.Write(DamageTakenRate);
			DamageModifiers resistances = Resistances;
			DamageModifier[] array = (DamageModifier[])(object)new DamageModifier[11]
			{
				(DamageModifier)(int)resistances.m_blunt,
				(DamageModifier)(int)resistances.m_slash,
				(DamageModifier)(int)resistances.m_pierce,
				(DamageModifier)(int)resistances.m_chop,
				(DamageModifier)(int)resistances.m_pickaxe,
				(DamageModifier)(int)resistances.m_fire,
				(DamageModifier)(int)resistances.m_frost,
				(DamageModifier)(int)resistances.m_lightning,
				(DamageModifier)(int)resistances.m_poison,
				(DamageModifier)(int)resistances.m_spirit,
				(DamageModifier)(int)resistances.m_nonPlayer
			};
			foreach (DamageModifier val2 in array)
			{
				val.Write((byte)val2);
			}
			DamageTypes weaponHit = WeaponHit;
			float[] array2 = new float[12]
			{
				weaponHit.m_damage, weaponHit.m_blunt, weaponHit.m_slash, weaponHit.m_pierce, weaponHit.m_chop, weaponHit.m_pickaxe, weaponHit.m_fire, weaponHit.m_frost, weaponHit.m_lightning, weaponHit.m_poison,
				weaponHit.m_spirit, weaponHit.m_nonPlayer
			};
			foreach (float num in array2)
			{
				val.Write(num);
			}
			return val.GetArray();
		}

		public static PlayerCombatProfile Deserialize(byte[] data)
		{
			//IL_0003: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Expected O, but got Unknown
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0067: Unknown result type (might be due to invalid IL or missing references)
			//IL_006c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0075: Unknown result type (might be due to invalid IL or missing references)
			//IL_007a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0083: Unknown result type (might be due to invalid IL or missing references)
			//IL_0088: Unknown result type (might be due to invalid IL or missing references)
			//IL_0091: Unknown result type (might be due to invalid IL or missing references)
			//IL_0096: Unknown result type (might be due to invalid IL or missing references)
			//IL_009f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ad: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b2: 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_00c0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ce: 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)
			//IL_00dc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ea: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fe: Unknown result type (might be due to invalid IL or missing references)
			//IL_0106: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e4: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e5: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				ZPackage pkg = new ZPackage(data);
				if (pkg.ReadByte() != 1)
				{
					return null;
				}
				return new PlayerCombatProfile
				{
					MaxHealth = pkg.ReadSingle(),
					Armor = pkg.ReadSingle(),
					DamageTakenRate = pkg.ReadSingle(),
					Resistances = new DamageModifiers
					{
						m_blunt = Mod(),
						m_slash = Mod(),
						m_pierce = Mod(),
						m_chop = Mod(),
						m_pickaxe = Mod(),
						m_fire = Mod(),
						m_frost = Mod(),
						m_lightning = Mod(),
						m_poison = Mod(),
						m_spirit = Mod(),
						m_nonPlayer = Mod()
					},
					WeaponHit = new DamageTypes
					{
						m_damage = pkg.ReadSingle(),
						m_blunt = pkg.ReadSingle(),
						m_slash = pkg.ReadSingle(),
						m_pierce = pkg.ReadSingle(),
						m_chop = pkg.ReadSingle(),
						m_pickaxe = pkg.ReadSingle(),
						m_fire = pkg.ReadSingle(),
						m_frost = pkg.ReadSingle(),
						m_lightning = pkg.ReadSingle(),
						m_poison = pkg.ReadSingle(),
						m_spirit = pkg.ReadSingle(),
						m_nonPlayer = pkg.ReadSingle()
					}
				};
				DamageModifier Mod()
				{
					return (DamageModifier)pkg.ReadByte();
				}
			}
			catch (Exception)
			{
				return null;
			}
		}
	}
	internal static class ProfileSync
	{
		private sealed class RemoteCache
		{
			public ZDO Zdo;

			public uint Revision;

			public PlayerCombatProfile Profile;
		}

		private static readonly int s_zdoKey = StringExtensionMethods.GetStableHashCode("TruePassiveMobs.CombatProfile");

		private const float LocalRefreshInterval = 0.5f;

		private static readonly ConditionalWeakTable<Player, RemoteCache> s_remote = new ConditionalWeakTable<Player, RemoteCache>();

		private static Player s_localPlayer;

		private static PlayerCombatProfile s_localProfile;

		private static float s_nextLocalRefresh;

		private static byte[] s_lastPublished;

		public static void Update()
		{
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer != (Object)(object)s_localPlayer)
			{
				s_localPlayer = localPlayer;
				s_localProfile = null;
				s_lastPublished = null;
				s_nextLocalRefresh = 0f;
			}
			if ((Object)(object)localPlayer == (Object)null || Time.time < s_nextLocalRefresh)
			{
				return;
			}
			s_nextLocalRefresh = Time.time + 0.5f;
			s_localProfile = PlayerCombatProfile.Compute(localPlayer);
			ZNetView nview = ((Character)localPlayer).m_nview;
			if (!((Object)(object)nview == (Object)null) && nview.IsValid() && nview.IsOwner())
			{
				byte[] array = s_localProfile.Serialize();
				if (s_lastPublished == null || !BytesEqual(array, s_lastPublished))
				{
					nview.GetZDO().Set(s_zdoKey, array);
					s_lastPublished = array;
				}
			}
		}

		public static PlayerCombatProfile Get(Player player)
		{
			if ((Object)(object)player == (Object)(object)Player.m_localPlayer)
			{
				return s_localProfile ?? (s_localProfile = PlayerCombatProfile.Compute(player));
			}
			ZDO val = (((Object)(object)((Character)player).m_nview != (Object)null && ((Character)player).m_nview.IsValid()) ? ((Character)player).m_nview.GetZDO() : null);
			if (val == null)
			{
				return null;
			}
			RemoteCache value = s_remote.GetValue(player, (Player _) => new RemoteCache());
			if (value.Zdo != val || value.Revision != val.DataRevision)
			{
				value.Zdo = val;
				value.Revision = val.DataRevision;
				byte[] byteArray = val.GetByteArray(s_zdoKey, (byte[])null);
				value.Profile = ((byteArray != null) ? PlayerCombatProfile.Deserialize(byteArray) : null);
			}
			return value.Profile;
		}

		private static bool BytesEqual(byte[] a, byte[] b)
		{
			if (a.Length != b.Length)
			{
				return false;
			}
			for (int i = 0; i < a.Length; i++)
			{
				if (a[i] != b[i])
				{
					return false;
				}
			}
			return true;
		}
	}
	internal struct Matchup
	{
		public bool HasProfile;

		public bool EnemyHitKnown;

		public float EnemyHit;

		public float EnemyHitPercent;

		public float PlayerHit;

		public int HitsToKill;

		public bool EnemyCheckPassed;

		public bool PlayerCheckPassed;

		public bool Outclassed;
	}
	internal static class CombatMath
	{
		private struct EnemyAttack
		{
			public DamageTypes Damage;

			public SkillType Skill;
		}

		private sealed class EnemyAttackCache
		{
			public float Expires;

			public readonly List<EnemyAttack> Attacks = new List<EnemyAttack>();
		}

		private const float EnemyAttackCacheSeconds = 10f;

		private const float EmptyEnemyAttackCacheSeconds = 1f;

		private static readonly ConditionalWeakTable<Character, EnemyAttackCache> s_enemyAttacks = new ConditionalWeakTable<Character, EnemyAttackCache>();

		public static Matchup Evaluate(Character enemy, Player player)
		{
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			Matchup result = new Matchup
			{
				HitsToKill = int.MaxValue
			};
			PlayerCombatProfile playerCombatProfile = ProfileSync.Get(player);
			if (playerCombatProfile == null || playerCombatProfile.MaxHealth <= 0f)
			{
				return result;
			}
			result.HasProfile = true;
			result.EnemyHitKnown = TryEstimateEnemyBestHit(enemy, playerCombatProfile, ((Component)player).transform.position, out result.EnemyHit);
			if (result.EnemyHitKnown)
			{
				result.EnemyHitPercent = result.EnemyHit / playerCombatProfile.MaxHealth * 100f;
				result.EnemyCheckPassed = result.EnemyHitPercent <= ModConfig.MaxEnemyHitPercent.Value;
			}
			result.PlayerHit = EstimatePlayerHit(enemy, playerCombatProfile);
			if (result.PlayerHit > 0.1f)
			{
				result.HitsToKill = Mathf.CeilToInt(enemy.GetMaxHealth() / result.PlayerHit);
				result.PlayerCheckPassed = result.HitsToKill <= ModConfig.MaxHitsToKill.Value;
			}
			bool value = ModConfig.UseEnemyDamageCheck.Value;
			bool value2 = ModConfig.UsePlayerDamageCheck.Value;
			if (value && value2)
			{
				result.Outclassed = ((!ModConfig.RequireBothChecks.Value) ? (result.EnemyCheckPassed || result.PlayerCheckPassed) : (result.EnemyCheckPassed && result.PlayerCheckPassed));
			}
			else
			{
				result.Outclassed = (value && result.EnemyCheckPassed) || (value2 && result.PlayerCheckPassed);
			}
			return result;
		}

		private static bool TryEstimateEnemyBestHit(Character enemy, PlayerCombatProfile target, Vector3 targetPos, out float bestHit)
		{
			//IL_0065: Unknown result type (might be due to invalid IL or missing references)
			//IL_0088: Unknown result type (might be due to invalid IL or missing references)
			//IL_008d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0090: Unknown result type (might be due to invalid IL or missing references)
			//IL_0095: Unknown result type (might be due to invalid IL or missing references)
			//IL_009c: Expected O, but got Unknown
			//IL_00b3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00df: Unknown result type (might be due to invalid IL or missing references)
			bestHit = 0f;
			EnemyAttackCache enemyAttacks = GetEnemyAttacks(enemy);
			if (enemyAttacks.Attacks.Count == 0)
			{
				return false;
			}
			float num = 1f + (float)Mathf.Max(0, enemy.GetLevel() - 1) * 0.5f;
			ZDO zDO = enemy.m_nview.GetZDO();
			float num2 = ((zDO != null) ? zDO.GetFloat(ZDOVars.s_randomSkillFactor, 1f) : 1f);
			float difficultyDamageScalePlayer = Game.instance.GetDifficultyDamageScalePlayer(targetPos);
			DamageModifier val2 = default(DamageModifier);
			foreach (EnemyAttack attack in enemyAttacks.Attacks)
			{
				HitData val = new HitData
				{
					m_damage = attack.Damage
				};
				((DamageTypes)(ref val.m_damage)).Modify(num * num2);
				enemy.GetSEMan().ModifyAttack(attack.Skill, ref val);
				val.SetAttacker(enemy);
				val.ApplyModifier(difficultyDamageScalePlayer);
				val.ApplyModifier(Game.m_enemyDamageRate);
				val.ApplyResistance(target.Resistances, ref val2);
				val.ApplyArmor(target.Armor);
				val.ApplyModifier(target.DamageTakenRate);
				bestHit = Mathf.Max(bestHit, val.GetTotalDamage());
			}
			return true;
		}

		private static float EstimatePlayerHit(Character enemy, PlayerCombatProfile attacker)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0005: 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_0012: Expected O, but got Unknown
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			HitData val = new HitData
			{
				m_damage = attacker.WeaponHit
			};
			DamageModifier val2 = default(DamageModifier);
			val.ApplyResistance(enemy.GetDamageModifiers((WeakSpot)null), ref val2);
			if (Game.m_worldLevel > 0)
			{
				val.ApplyArmor((float)(Game.m_worldLevel * Game.instance.m_worldLevelEnemyBaseAC));
			}
			val.ApplyModifier(Game.instance.GetDifficultyDamageScaleEnemy(((Component)enemy).transform.position));
			val.ApplyModifier(Game.m_playerDamageRate);
			return val.GetTotalDamage();
		}

		private static EnemyAttackCache GetEnemyAttacks(Character enemy)
		{
			EnemyAttackCache value = s_enemyAttacks.GetValue(enemy, (Character _) => new EnemyAttackCache());
			if (Time.time < value.Expires)
			{
				return value;
			}
			value.Attacks.Clear();
			Character obj = ((enemy is Humanoid) ? enemy : null);
			Inventory val = ((obj != null) ? ((Humanoid)obj).GetInventory() : null);
			if (val != null)
			{
				CollectAttacks(val, value.Attacks);
			}
			value.Expires = Time.time + ((value.Attacks.Count > 0) ? 10f : 1f);
			return value;
		}

		private static void CollectAttacks(Inventory inventory, List<EnemyAttack> attacks)
		{
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: 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_0065: Unknown result type (might be due to invalid IL or missing references)
			//IL_0067: Unknown result type (might be due to invalid IL or missing references)
			//IL_0077: Unknown result type (might be due to invalid IL or missing references)
			//IL_0084: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
			foreach (ItemData allItem in inventory.GetAllItems())
			{
				if (!allItem.IsWeapon() || (int)allItem.m_shared.m_aiTargetType != 0)
				{
					continue;
				}
				SkillType skillType = allItem.m_shared.m_skillType;
				Attack attack = allItem.m_shared.m_attack;
				DamageTypes damage = allItem.GetDamage();
				if (attack != null)
				{
					((DamageTypes)(ref damage)).Modify(attack.m_damageMultiplier);
				}
				AddIfDamaging(attacks, damage, skillType);
				if (attack != null)
				{
					AddAreaEffects(attacks, attack.m_attackProjectile, skillType);
					AddAreaEffects(attacks, attack.m_spawnOnTrigger, skillType);
					Projectile val = (((Object)(object)attack.m_attackProjectile != (Object)null) ? attack.m_attackProjectile.GetComponent<Projectile>() : null);
					if ((Object)(object)val != (Object)null)
					{
						AddAreaEffects(attacks, val.m_spawnOnHit, skillType);
					}
				}
			}
		}

		private static void AddAreaEffects(List<EnemyAttack> attacks, GameObject prefab, SkillType skill)
		{
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)prefab == (Object)null)
			{
				return;
			}
			Aoe[] componentsInChildren = prefab.GetComponentsInChildren<Aoe>(true);
			foreach (Aoe val in componentsInChildren)
			{
				if (!val.m_useAttackSettings)
				{
					AddIfDamaging(attacks, val.m_damage, skill);
				}
			}
		}

		private static void AddIfDamaging(List<EnemyAttack> attacks, DamageTypes damage, SkillType skill)
		{
			//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_0021: 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)
			if (((DamageTypes)(ref damage)).GetTotalDamage() > 0f)
			{
				attacks.Add(new EnemyAttack
				{
					Damage = damage,
					Skill = skill
				});
			}
		}
	}
	internal enum Temperament
	{
		Hostile,
		Passive,
		Skittish,
		Territorial
	}
	internal static class Creatures
	{
		private sealed class Info
		{
			public string PrefabName;

			public int ListVersion = -1;

			public Temperament Temperament;

			public bool FearExcluded;
		}

		private static readonly ConditionalWeakTable<Character, Info> s_info = new ConditionalWeakTable<Character, Info>();

		public static Temperament GetTemperament(Character character)
		{
			return GetInfo(character).Temperament;
		}

		public static bool IsPassive(Character character)
		{
			return GetInfo(character).Temperament != Temperament.Hostile;
		}

		public static bool IsFearExcluded(Character character)
		{
			return GetInfo(character).FearExcluded;
		}

		public static string GetPrefabName(Character character)
		{
			return GetInfo(character).PrefabName;
		}

		private static Info GetInfo(Character character)
		{
			Info value = s_info.GetValue(character, (Character _) => new Info());
			if (value.PrefabName == null)
			{
				value.PrefabName = ResolvePrefabName(character);
			}
			if (value.ListVersion != ModConfig.ListVersion)
			{
				value.ListVersion = ModConfig.ListVersion;
				value.Temperament = ModConfig.GetTemperament(value.PrefabName);
				value.FearExcluded = ModConfig.IsFearExcludedName(value.PrefabName);
			}
			return value;
		}

		private static string ResolvePrefabName(Character character)
		{
			ZDO val = (((Object)(object)character.m_nview != (Object)null) ? character.m_nview.GetZDO() : null);
			if (val != null && (Object)(object)ZNetScene.instance != (Object)null)
			{
				GameObject prefab = ZNetScene.instance.GetPrefab(val.GetPrefab());
				if ((Object)(object)prefab != (Object)null)
				{
					return ((Object)prefab).name;
				}
			}
			string name = ((Object)((Component)character).gameObject).name;
			int num = name.IndexOf('(');
			return ((num >= 0) ? name.Substring(0, num) : name).Trim();
		}
	}
	internal static class FearOfStrongPlayers
	{
		private sealed class State
		{
			public float NextCheck;

			public Player Feared;

			public float FearUntil;

			public Character LastTarget;
		}

		private const float CheckInterval = 0.5f;

		private const float FearLinger = 3f;

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

		public static bool CanBeAfraid(MonsterAI ai)
		{
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Invalid comparison between Unknown and I4
			Character character = ((BaseAI)ai).m_character;
			if ((Object)(object)character == (Object)null || character.IsTamed() || character.IsBoss() || (int)character.GetFaction() == 8 || Creatures.IsFearExcluded(character))
			{
				return false;
			}
			if (ModConfig.RaidersNeverFlee.Value && ai.IsEventCreature())
			{
				return false;
			}
			if (ModConfig.FightBackWhenAttacked.Value && Provocation.WasHitByPlayerWithin(character, ModConfig.FightBackDuration.Value))
			{
				return false;
			}
			return true;
		}

		public static Player GetFearedPlayer(MonsterAI ai)
		{
			//IL_00dd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e8: Unknown result type (might be due to invalid IL or missing references)
			State value = s_states.GetValue(ai, (MonsterAI _) => new State
			{
				NextCheck = Time.time + Random.Range(0f, 0.5f)
			});
			float time = Time.time;
			float value2 = ModConfig.FearRange.Value;
			bool flag = (Object)(object)ai.m_targetCreature != (Object)(object)value.LastTarget;
			value.LastTarget = ai.m_targetCreature;
			if (time >= value.NextCheck || (flag && ai.m_targetCreature is Player))
			{
				value.NextCheck = time + 0.5f;
				Player val = FindOutclassingPlayer(ai, value.Feared, value2);
				if ((Object)(object)val != (Object)null)
				{
					value.Feared = val;
					value.FearUntil = time + 3f;
				}
			}
			Player feared = value.Feared;
			if (feared != null && ((Object)(object)feared == (Object)null || ((Character)feared).IsDead() || time > value.FearUntil || Vector3.Distance(((Component)feared).transform.position, ((Component)ai).transform.position) > value2))
			{
				value.Feared = null;
			}
			return value.Feared;
		}

		private static Player FindOutclassingPlayer(MonsterAI ai, Player currentlyFeared, float range)
		{
			//IL_000d: 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_0061: 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)
			Character character = ((BaseAI)ai).m_character;
			Vector3 position = ((Component)ai).transform.position;
			Player result = null;
			float num = float.MaxValue;
			foreach (Player allPlayer in Player.GetAllPlayers())
			{
				if (!((Object)(object)allPlayer == (Object)null) && !((Character)allPlayer).IsDead() && !((Character)allPlayer).InGhostMode() && !allPlayer.InDebugFlyMode())
				{
					float num2 = Vector3.Distance(((Component)allPlayer).transform.position, position);
					if (!(num2 > range) && !(num2 >= num) && BaseAI.IsEnemy(character, (Character)(object)allPlayer) && ((Object)(object)allPlayer == (Object)(object)currentlyFeared || (Object)(object)ai.m_targetCreature == (Object)(object)allPlayer || ((BaseAI)ai).CanSenseTarget((Character)(object)allPlayer)) && CombatMath.Evaluate(character, allPlayer).Outclassed)
					{
						result = allPlayer;
						num = num2;
					}
				}
			}
			return result;
		}
	}
	internal static class ModConfig
	{
		private const string PassiveSection = "1 - Passive creatures";

		private const string FearSection = "2 - Enemies fear strong players";

		private const string NeutralSection = "3 - Neutral creatures";

		public static ConfigEntry<bool> PassiveEnabled;

		public static ConfigEntry<string> PassiveCreatures;

		public static ConfigEntry<string> SkittishCreatures;

		public static ConfigEntry<float> SkittishRange;

		public static ConfigEntry<float> SkittishTime;

		public static ConfigEntry<string> TerritorialCreatures;

		public static ConfigEntry<float> TerritorialRange;

		public static ConfigEntry<float> TerritorialTime;

		public static ConfigEntry<float> TerritorialRetreatTime;

		public static ConfigEntry<float> ProvokedDuration;

		public static ConfigEntry<bool> FearEnabled;

		public static ConfigEntry<bool> FightBackWhenAttacked;

		public static ConfigEntry<float> FightBackDuration;

		public static ConfigEntry<bool> RaidersNeverFlee;

		public static ConfigEntry<bool> UseEnemyDamageCheck;

		public static ConfigEntry<float> MaxEnemyHitPercent;

		public static ConfigEntry<bool> UsePlayerDamageCheck;

		public static ConfigEntry<int> MaxHitsToKill;

		public static ConfigEntry<bool> RequireBothChecks;

		public static ConfigEntry<float> FearRange;

		public static ConfigEntry<string> FearExcludedCreatures;

		public static ConfigEntry<bool> NeutralEnabled;

		public static ConfigEntry<int> HitsToAnger;

		public static ConfigEntry<float> AngerWindow;

		public static ConfigEntry<float> CalmDownTime;

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

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

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

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

		private static int s_checkedListVersion = -1;

		private static ZNetScene s_checkedScene;

		public static int ListVersion { get; private set; }

		public static void Bind(ConfigFile config)
		{
			PassiveEnabled = Bind(config, "1 - Passive creatures", "Enabled", value: true, "Creatures in the lists below never seek out players, and fight back normally once damaged. A creature in several lists uses Territorial over Skittish over Creatures.");
			PassiveCreatures = Bind(config, "1 - Passive creatures", "Creatures", "Lox, Asksvin, Asksvin_hatchling, Moose, Boar, Hen, Neck, Bjorn, Bjorn_sleeping", "Fully passive: ignore players entirely.");
			SkittishCreatures = Bind(config, "1 - Passive creatures", "SkittishCreatures", "Neck, Lox", "Run away from a player who stays within SkittishRange for SkittishTime seconds.");
			SkittishRange = Bind(config, "1 - Passive creatures", "SkittishRange", 5f, "Meters within which a player makes skittish creatures nervous.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 50f));
			SkittishTime = Bind(config, "1 - Passive creatures", "SkittishTime", 3f, "Seconds a player must stay within SkittishRange before skittish creatures run away.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 120f));
			TerritorialCreatures = Bind(config, "1 - Passive creatures", "TerritorialCreatures", "Asksvin, Moose, Bjorn, Boar", "Attack a player who stays within TerritorialRange for TerritorialTime seconds, then run away for TerritorialRetreatTime seconds.");
			TerritorialRange = Bind(config, "1 - Passive creatures", "TerritorialRange", 3f, "Meters within which a player is intruding on territorial creatures.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 50f));
			TerritorialTime = Bind(config, "1 - Passive creatures", "TerritorialTime", 5f, "Seconds a player must stay within TerritorialRange before territorial creatures attack.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 120f));
			TerritorialRetreatTime = Bind(config, "1 - Passive creatures", "TerritorialRetreatTime", 10f, "Seconds a territorial creature runs away after attacking.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 120f));
			ProvokedDuration = Bind(config, "1 - Passive creatures", "ProvokedDuration", 60f, "Seconds a passive creature stays hostile after being damaged", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 3600f));
			FearEnabled = Bind(config, "2 - Enemies fear strong players", "Enabled", value: true, "All enemies run away from players instead of attacking if the player's gear is good enough.");
			FightBackWhenAttacked = Bind(config, "2 - Enemies fear strong players", "FightBackWhenAttacked", value: true, "An enemy a player damages stops running and fights back until FightBackDuration seconds after the last hit.");
			FightBackDuration = Bind(config, "2 - Enemies fear strong players", "FightBackDuration", 60f, "Seconds an attacked enemy keeps fighting back after being damaged.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 3600f));
			RaidersNeverFlee = Bind(config, "2 - Enemies fear strong players", "RaidersNeverFlee", value: true, "Creatures spawned by a raid never run away.");
			UseEnemyDamageCheck = Bind(config, "2 - Enemies fear strong players", "UseEnemyDamageCheck", value: true, "Checks that the enemy's strongest attack, after your armor and resistances, deals MaxEnemyHitPercent of your max health.");
			MaxEnemyHitPercent = Bind(config, "2 - Enemies fear strong players", "MaxEnemyHitPercent", 10f, "Largest hit (as % of your max health) an enemy may be able to deal and still be afraid of you.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 100f));
			UsePlayerDamageCheck = Bind(config, "2 - Enemies fear strong players", "UsePlayerDamageCheck", value: true, "Check that your equipped weapon (with skill, buffs, and the enemy's resistances) is able to kill the enemy in at most MaxHitsToKill hits.");
			MaxHitsToKill = Bind(config, "2 - Enemies fear strong players", "MaxHitsToKill", 3, "Number of hits it may take you to kill an enemy for it to still be afraid of you.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 100));
			RequireBothChecks = Bind(config, "2 - Enemies fear strong players", "RequireBothChecks", value: true, "true = enemies flee only if both checks pass, false = enemies flee if either passes.");
			FearRange = Bind(config, "2 - Enemies fear strong players", "FearRange", 30f, "Enemies within this many meters will evaluate fear.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(5f, 100f));
			FearExcludedCreatures = Bind(config, "2 - Enemies fear strong players", "ExcludedCreatures", "", "Comma-separated enemies that should never run away.");
			NeutralEnabled = Bind(config, "3 - Neutral creatures", "Enabled", value: true, "Neutral creatures like the Dvergr only turn hostile after repeated hits, and make peace again once the fighting stops.");
			HitsToAnger = Bind(config, "3 - Neutral creatures", "HitsToAnger", 2, "Hits from a player within AngerWindow seconds before a neutral creature, and the others around it, turn hostile. 1 = vanilla.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 20));
			AngerWindow = Bind(config, "3 - Neutral creatures", "AngerWindow", 30f, "Seconds within which those hits have to land.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 600f));
			CalmDownTime = Bind(config, "3 - Neutral creatures", "CalmDownTime", 300f, "Seconds after the fighting stops before hostile neutral creatures make peace again. 0 = never (vanilla).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 3600f));
			PassiveCreatures.SettingChanged += delegate
			{
				RebuildLists();
			};
			SkittishCreatures.SettingChanged += delegate
			{
				RebuildLists();
			};
			TerritorialCreatures.SettingChanged += delegate
			{
				RebuildLists();
			};
			FearExcludedCreatures.SettingChanged += delegate
			{
				RebuildLists();
			};
			RebuildLists();
		}

		private static ConfigEntry<T> Bind<T>(ConfigFile config, string section, string key, T value, string description, AcceptableValueBase range = null)
		{
			//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_001d: Expected O, but got Unknown
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Expected O, but got Unknown
			return config.Bind<T>(section, key, value, new ConfigDescription(description, range, new object[1] { (object)new ConfigurationManagerAttributes
			{
				IsAdminOnly = true
			} }));
		}

		public static Temperament GetTemperament(string prefabName)
		{
			if (s_territorialNames.Contains(prefabName))
			{
				return Temperament.Territorial;
			}
			if (s_skittishNames.Contains(prefabName))
			{
				return Temperament.Skittish;
			}
			if (s_passiveNames.Contains(prefabName))
			{
				return Temperament.Passive;
			}
			return Temperament.Hostile;
		}

		public static bool IsFearExcludedName(string prefabName)
		{
			return s_fearExcludedNames.Contains(prefabName);
		}

		public static void WarnAboutUnknownNames()
		{
			ZNetScene instance = ZNetScene.instance;
			if ((Object)(object)instance == (Object)null || ((Object)(object)instance == (Object)(object)s_checkedScene && ListVersion == s_checkedListVersion))
			{
				return;
			}
			s_checkedScene = instance;
			s_checkedListVersion = ListVersion;
			HashSet<string> hashSet = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
			foreach (GameObject prefab in instance.m_prefabs)
			{
				if ((Object)(object)prefab != (Object)null && (Object)(object)prefab.GetComponent<Character>() != (Object)null)
				{
					hashSet.Add(((Object)prefab).name);
				}
			}
			WarnAboutUnknownNames(PassiveCreatures, s_passiveNames, hashSet);
			WarnAboutUnknownNames(SkittishCreatures, s_skittishNames, hashSet);
			WarnAboutUnknownNames(TerritorialCreatures, s_territorialNames, hashSet);
			WarnAboutUnknownNames(FearExcludedCreatures, s_fearExcludedNames, hashSet);
		}

		private static void WarnAboutUnknownNames(ConfigEntry<string> entry, HashSet<string> names, HashSet<string> creatures)
		{
			foreach (string name in names)
			{
				if (!creatures.Contains(name))
				{
					Plugin.Log.LogWarning((object)(((ConfigEntryBase)entry).Definition.Key + ": '" + name + "' isn't a known creature, check the spelling."));
				}
			}
		}

		private static void RebuildLists()
		{
			s_passiveNames = ParseList(PassiveCreatures.Value);
			s_skittishNames = ParseList(SkittishCreatures.Value);
			s_territorialNames = ParseList(TerritorialCreatures.Value);
			s_fearExcludedNames = ParseList(FearExcludedCreatures.Value);
			ListVersion++;
		}

		private static HashSet<string> ParseList(string value)
		{
			HashSet<string> hashSet = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
			string[] array = (value ?? "").Split(new char[2] { ',', ';' }, StringSplitOptions.RemoveEmptyEntries);
			for (int i = 0; i < array.Length; i++)
			{
				string text = array[i].Trim();
				if (text.Length > 0)
				{
					hashSet.Add(text);
				}
			}
			return hashSet;
		}
	}
	[HarmonyPatch]
	internal static class BaseAIReversePatch
	{
		[HarmonyReversePatch(/*Could not decode attribute arguments.*/)]
		[HarmonyPatch(typeof(BaseAI), "UpdateAI")]
		public static bool BaseUpdateAI(BaseAI instance, float dt)
		{
			throw new NotImplementedException("Harmony reverse patch stub");
		}
	}
	[HarmonyPatch(typeof(MonsterAI), "UpdateAI")]
	internal static class MonsterAI_UpdateAI_Patch
	{
		private static bool Prefix(MonsterAI __instance, float dt, ref bool __result)
		{
			ZNetView nview = ((BaseAI)__instance).m_nview;
			if ((Object)(object)nview == (Object)null || !nview.IsValid() || !nview.IsOwner() || ((BaseAI)__instance).IsSleeping())
			{
				return true;
			}
			NeutralCreatures.Update(__instance);
			Player val;
			if (ModConfig.PassiveEnabled.Value && Creatures.IsPassive(((BaseAI)__instance).m_character))
			{
				val = PassiveCreatures.Update(__instance, dt);
			}
			else
			{
				if (!ModConfig.FearEnabled.Value || !FearOfStrongPlayers.CanBeAfraid(__instance))
				{
					return true;
				}
				val = FearOfStrongPlayers.GetFearedPlayer(__instance);
			}
			if ((Object)(object)val == (Object)null)
			{
				return true;
			}
			__result = BaseAIReversePatch.BaseUpdateAI((BaseAI)(object)__instance, dt);
			if (__result)
			{
				RunFrom(__instance, val, dt);
			}
			return false;
		}

		private static void RunFrom(MonsterAI ai, Player player, float dt)
		{
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)ai.m_targetCreature != (Object)null || (Object)(object)ai.m_targetStatic != (Object)null)
			{
				ai.m_targetCreature = null;
				ai.m_targetStatic = null;
				((BaseAI)ai).SetTargetInfo(ZDOID.None);
			}
			((BaseAI)ai).ChargeStop();
			((BaseAI)ai).SetAlerted(true);
			((BaseAI)ai).Flee(dt, ((Component)player).transform.position);
		}
	}
	internal static class NeutralCreatures
	{
		[HarmonyPatch(typeof(Character), "RPC_Damage")]
		private static class Character_RPC_Damage_Patch
		{
			private static void Prefix(Character __instance, HitData hit)
			{
				s_forgiveThisHit = false;
				if (!ModConfig.NeutralEnabled.Value || hit == null)
				{
					return;
				}
				BaseAI baseAI = __instance.GetBaseAI();
				ZNetView nview = __instance.m_nview;
				if (!((Object)(object)baseAI == (Object)null) && baseAI.IsAggravatable() && !baseAI.IsAggravated() && !((Object)(object)nview == (Object)null) && nview.IsValid() && nview.IsOwner() && hit.GetAttacker() is Player && !(hit.GetTotalDamage() <= 0f))
				{
					ZDO zDO = nview.GetZDO();
					long num = Provocation.NowMs();
					int num2 = zDO.GetInt(s_angerHitsKey, 0);
					if (num - zDO.GetLong(s_angerWindowStartKey, 0L) > (long)(ModConfig.AngerWindow.Value * 1000f))
					{
						num2 = 0;
						zDO.Set(s_angerWindowStartKey, num);
					}
					num2++;
					if (num2 >= ModConfig.HitsToAnger.Value)
					{
						zDO.Set(s_angerHitsKey, 0, false);
						return;
					}
					zDO.Set(s_angerHitsKey, num2, false);
					s_forgiveThisHit = true;
				}
			}

			private static Exception Finalizer(Exception __exception)
			{
				s_forgiveThisHit = false;
				return __exception;
			}
		}

		[HarmonyPatch(typeof(BaseAI), "AggravateAllInArea")]
		private static class BaseAI_AggravateAllInArea_Patch
		{
			private static bool Prefix()
			{
				return !s_forgiveThisHit;
			}
		}

		private static readonly int s_angerHitsKey = StringExtensionMethods.GetStableHashCode("TruePassiveMobs.AngerHits");

		private static readonly int s_angerWindowStartKey = StringExtensionMethods.GetStableHashCode("TruePassiveMobs.AngerWindowStart");

		private static readonly int s_lastHostilityKey = StringExtensionMethods.GetStableHashCode("TruePassiveMobs.LastHostility");

		private const float CombatGrace = 5f;

		private static bool s_forgiveThisHit;

		public static void Update(MonsterAI ai)
		{
			//IL_00ef: Unknown result type (might be due to invalid IL or missing references)
			if (!ModConfig.NeutralEnabled.Value || ModConfig.CalmDownTime.Value <= 0f || !((BaseAI)ai).IsAggravatable() || !((BaseAI)ai).IsAggravated())
			{
				return;
			}
			ZDO zDO = ((BaseAI)ai).m_nview.GetZDO();
			long num = Provocation.NowMs();
			long num2 = zDO.GetLong(s_lastHostilityKey, 0L);
			bool flag = ai.m_targetCreature is Player && ai.m_timeSinceAttacking < 5f;
			if (num2 == 0L || (flag && num - num2 > 1000))
			{
				zDO.Set(s_lastHostilityKey, num);
				return;
			}
			num2 = Math.Max(num2, Provocation.LastPlayerHitMs(((BaseAI)ai).m_character));
			if (num - num2 >= (long)(ModConfig.CalmDownTime.Value * 1000f))
			{
				((BaseAI)ai).m_aggravated = false;
				zDO.Set(ZDOVars.s_aggravated, false);
				zDO.Set(s_lastHostilityKey, 0L);
				ai.m_privateAreaAttacks = 0;
				if (ai.m_targetCreature is Player)
				{
					ai.m_targetCreature = null;
					((BaseAI)ai).SetTargetInfo(ZDOID.None);
				}
			}
		}
	}
	internal static class PassiveCreatures
	{
		private enum Mode
		{
			Calm,
			Fleeing,
			Charging,
			Retreating
		}

		private sealed class State
		{
			public Mode Mode;

			public Player Player;

			public float CloseTime;

			public float ModeUntil;

			public Attack AttackBefore;

			public float AttackStartedAt = -1f;
		}

		[HarmonyPatch(typeof(BaseAI), "FindEnemy")]
		private static class BaseAI_FindEnemy_Patch
		{
			private static void Prefix(BaseAI __instance, out List<Player> __state)
			{
				__state = null;
				if (!ShouldIgnorePlayers(__instance))
				{
					return;
				}
				foreach (Player allPlayer in Player.GetAllPlayers())
				{
					if (!((Character)allPlayer).m_aiSkipTarget)
					{
						((Character)allPlayer).m_aiSkipTarget = true;
						(__state ?? (__state = new List<Player>())).Add(allPlayer);
					}
				}
			}

			private static void Postfix(BaseAI __instance, ref Character __result)
			{
				if (__result is Player && ShouldIgnorePlayers(__instance))
				{
					__result = null;
				}
			}

			private static Exception Finalizer(Exception __exception, List<Player> __state)
			{
				if (__state != null)
				{
					foreach (Player item in __state)
					{
						if ((Object)(object)item != (Object)null)
						{
							((Character)item).m_aiSkipTarget = false;
						}
					}
				}
				return __exception;
			}
		}

		private const float CalmDownRangeFactor = 1.5f;

		private const float GiveUpRangeFactor = 3f;

		private const float ChargeTimeout = 15f;

		private const float MaxAttackDuration = 5f;

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

		public static bool ShouldIgnorePlayers(BaseAI ai)
		{
			if (ModConfig.PassiveEnabled.Value)
			{
				MonsterAI val = (MonsterAI)(object)((ai is MonsterAI) ? ai : null);
				if (val != null)
				{
					Character character = ai.m_character;
					if ((Object)(object)character == (Object)null || character.IsTamed() || !Creatures.IsPassive(character) || IsProvoked(character))
					{
						return false;
					}
					if (s_states.TryGetValue(val, out var value))
					{
						return value.Mode != Mode.Charging;
					}
					return true;
				}
			}
			return false;
		}

		public static bool IsProvoked(Character character)
		{
			return Provocation.WasHitByPlayerWithin(character, ModConfig.ProvokedDuration.Value);
		}

		public static Player Update(MonsterAI ai, float dt)
		{
			Character character = ((BaseAI)ai).m_character;
			State value = s_states.GetValue(ai, (MonsterAI _) => new State());
			if (character.IsTamed() || IsProvoked(character))
			{
				value.Mode = Mode.Calm;
				value.Player = null;
				value.CloseTime = 0f;
				return null;
			}
			switch (Creatures.GetTemperament(character))
			{
			case Temperament.Skittish:
				return UpdateSkittish(ai, value, dt);
			case Temperament.Territorial:
				return UpdateTerritorial(ai, value, dt);
			default:
				value.Mode = Mode.Calm;
				ForgetPlayers(ai);
				return null;
			}
		}

		private static Player UpdateSkittish(MonsterAI ai, State state, float dt)
		{
			float value = ModConfig.SkittishRange.Value;
			if (state.Mode == Mode.Fleeing)
			{
				if (!IsGone(state.Player) && Distance(ai, state.Player) <= value * 1.5f)
				{
					return state.Player;
				}
				BecomeCalm(ai, state);
			}
			state.Mode = Mode.Calm;
			ForgetPlayers(ai);
			Player val = TrackCloseness(ai, state, value, ModConfig.SkittishTime.Value, dt);
			if ((Object)(object)val == (Object)null)
			{
				return null;
			}
			state.Mode = Mode.Fleeing;
			state.Player = val;
			return val;
		}

		private static Player UpdateTerritorial(MonsterAI ai, State state, float dt)
		{
			float time = Time.time;
			float value = ModConfig.TerritorialRange.Value;
			switch (state.Mode)
			{
			case Mode.Charging:
				if (IsGone(state.Player) || time > state.ModeUntil || Distance(ai, state.Player) > value * 3f)
				{
					BecomeCalm(ai, state);
					break;
				}
				if (FinishedAttack(ai, state, time))
				{
					state.Mode = Mode.Retreating;
					state.ModeUntil = time + ModConfig.TerritorialRetreatTime.Value;
					return state.Player;
				}
				Charge(ai, state.Player);
				return null;
			case Mode.Retreating:
				if (!IsGone(state.Player) && time <= state.ModeUntil)
				{
					return state.Player;
				}
				BecomeCalm(ai, state);
				break;
			}
			state.Mode = Mode.Calm;
			ForgetPlayers(ai);
			Player val = TrackCloseness(ai, state, value, ModConfig.TerritorialTime.Value, dt);
			if ((Object)(object)val == (Object)null)
			{
				return null;
			}
			state.Mode = Mode.Charging;
			state.Player = val;
			state.ModeUntil = time + 15f;
			ref Attack attackBefore = ref state.AttackBefore;
			Character character = ((BaseAI)ai).m_character;
			attackBefore = ((Humanoid)(((character is Humanoid) ? character : null)?)).m_currentAttack;
			state.AttackStartedAt = -1f;
			Charge(ai, val);
			return null;
		}

		private static Player TrackCloseness(MonsterAI ai, State state, float range, float time, float dt)
		{
			Player val = null;
			float num = float.MaxValue;
			foreach (Player allPlayer in Player.GetAllPlayers())
			{
				if (!IsGone(allPlayer) && !((Character)allPlayer).InGhostMode() && !allPlayer.InDebugFlyMode() && !((Character)allPlayer).IsCrouching())
				{
					float num2 = Distance(ai, allPlayer);
					if (num2 <= range && num2 < num)
					{
						val = allPlayer;
						num = num2;
					}
				}
			}
			state.CloseTime = (((Object)(object)val != (Object)null) ? (state.CloseTime + dt) : Mathf.Max(0f, state.CloseTime - dt));
			if (!((Object)(object)val != (Object)null) || !(state.CloseTime >= time))
			{
				return null;
			}
			return val;
		}

		private static bool FinishedAttack(MonsterAI ai, State state, float now)
		{
			Character character = ((BaseAI)ai).m_character;
			Humanoid val = (Humanoid)(object)((character is Humanoid) ? character : null);
			if (val == null)
			{
				return false;
			}
			if (state.AttackStartedAt < 0f)
			{
				if (val.m_currentAttack != null && val.m_currentAttack != state.AttackBefore)
				{
					state.AttackStartedAt = now;
				}
				return false;
			}
			float num = now - state.AttackStartedAt;
			if (!(num > 0.25f) || ((Character)val).InAttack())
			{
				return num > 5f;
			}
			return true;
		}

		private static void Charge(MonsterAI ai, Player player)
		{
			//IL_0015: 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)
			ai.m_targetCreature = (Character)(object)player;
			ai.m_targetStatic = null;
			ai.m_lastKnownTargetPos = ((Component)player).transform.position;
			ai.m_beenAtLastPos = false;
			((BaseAI)ai).SetAlerted(true);
		}

		private static void BecomeCalm(MonsterAI ai, State state)
		{
			state.Mode = Mode.Calm;
			state.Player = null;
			state.CloseTime = 0f;
			((BaseAI)ai).SetAlerted(false);
		}

		private static bool IsGone(Player player)
		{
			if (!((Object)(object)player == (Object)null))
			{
				return ((Character)player).IsDead();
			}
			return true;
		}

		private static float Distance(MonsterAI ai, Player player)
		{
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			return Mathf.Max(0f, Vector3.Distance(((Component)player).transform.position, ((Component)ai).transform.position) - ((BaseAI)ai).m_character.GetRadius() - ((Character)player).GetRadius());
		}

		public static void ForgetPlayers(MonsterAI ai)
		{
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			if (ai.m_targetCreature is Player)
			{
				ai.m_targetCreature = null;
				((BaseAI)ai).SetTargetInfo(ZDOID.None);
			}
			ai.m_targetStatic = null;
		}
	}
	[BepInPlugin("com.lhoffl.TruePassiveMobs", "TruePassiveMobs", "1.2.0")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[NetworkCompatibility(/*Could not decode attribute arguments.*/)]
	[BepInProcess("valheim.exe")]
	[BepInProcess("valheim_server.exe")]
	public class Plugin : BaseUnityPlugin
	{
		public const string PluginGUID = "com.lhoffl.TruePassiveMobs";

		public const string PluginName = "TruePassiveMobs";

		public const string PluginVersion = "1.2.0";

		internal static ManualLogSource Log;

		private Harmony _harmony;

		private FileSystemWatcher _configWatcher;

		private volatile bool _configFileChanged;

		private void Awake()
		{
			Log = ((BaseUnityPlugin)this).Logger;
			ModConfig.Bind(((BaseUnityPlugin)this).Config);
			_harmony = Harmony.CreateAndPatchAll(Assembly.GetExecutingAssembly(), "com.lhoffl.TruePassiveMobs");
			WatchConfigFile();
		}

		private void Update()
		{
			if (_configFileChanged)
			{
				_configFileChanged = false;
				((BaseUnityPlugin)this).Config.Reload();
				Log.LogInfo((object)"Config reloaded");
			}
			ProfileSync.Update();
			ModConfig.WarnAboutUnknownNames();
		}

		private void OnDestroy()
		{
			_configWatcher?.Dispose();
			Harmony harmony = _harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
		}

		private void WatchConfigFile()
		{
			string configFilePath = ((BaseUnityPlugin)this).Config.ConfigFilePath;
			_configWatcher = new FileSystemWatcher(Path.GetDirectoryName(configFilePath), Path.GetFileName(configFilePath))
			{
				NotifyFilter = (NotifyFilters.FileName | NotifyFilters.Size | NotifyFilters.LastWrite),
				IncludeSubdirectories = false,
				EnableRaisingEvents = true
			};
			_configWatcher.Changed += delegate
			{
				_configFileChanged = true;
			};
			_configWatcher.Created += delegate
			{
				_configFileChanged = true;
			};
			_configWatcher.Renamed += delegate
			{
				_configFileChanged = true;
			};
		}
	}
	internal static class Provocation
	{
		[HarmonyPatch(typeof(Character), "RPC_Damage")]
		private static class Character_RPC_Damage_Patch
		{
			private static void Prefix(Character __instance, HitData hit)
			{
				if (!__instance.IsPlayer() && hit != null && hit.HaveAttacker())
				{
					ZNetView nview = __instance.m_nview;
					if (!((Object)(object)nview == (Object)null) && nview.IsValid() && nview.IsOwner() && __instance.GetBaseAI() is MonsterAI && hit.GetAttacker() is Player)
					{
						nview.GetZDO().Set(s_lastPlayerHitKey, NowMs());
					}
				}
			}
		}

		private static readonly int s_lastPlayerHitKey = StringExtensionMethods.GetStableHashCode("TruePassiveMobs.LastPlayerHit");

		public static bool WasHitByPlayerWithin(Character character, float seconds)
		{
			long num = LastPlayerHitMs(character);
			if (num > 0)
			{
				return NowMs() - num < (long)(seconds * 1000f);
			}
			return false;
		}

		public static long LastPlayerHitMs(Character character)
		{
			ZDO val = (((Object)(object)character.m_nview != (Object)null && character.m_nview.IsValid()) ? character.m_nview.GetZDO() : null);
			if (val == null)
			{
				return 0L;
			}
			return val.GetLong(s_lastPlayerHitKey, 0L);
		}

		public static long NowMs()
		{
			return (long)(ZNet.instance.GetTimeSeconds() * 1000.0);
		}
	}
}
namespace System.Runtime.CompilerServices
{
	[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
	internal sealed class IgnoresAccessChecksToAttribute : Attribute
	{
		public IgnoresAccessChecksToAttribute(string assemblyName)
		{
		}
	}
}