Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of TruePassiveMobs v1.2.0
TruePassiveMobs.dll
Decompiled 3 weeks agousing 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) { } } }