Decompiled source of Valheim Foresight v1.0.0

BepInEx/plugins/Valheim.Foresight.dll

Decompiled 2 days ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using TMPro;
using UnityEngine;
using UnityEngine.UI;
using Valheim.Foresight.Configuration;
using Valheim.Foresight.Core;
using Valheim.Foresight.HarmonyRefs;
using Valheim.Foresight.Models;
using Valheim.Foresight.Patches;
using Valheim.Foresight.Services.Combat;
using Valheim.Foresight.Services.Combat.Interfaces;
using Valheim.Foresight.Services.Combat.Wrappers;
using Valheim.Foresight.Services.Damage;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyTitle("Valheim Foresight")]
[assembly: AssemblyDescription("Combat threat assessment mod for Valheim")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("CoffeeNova")]
[assembly: AssemblyProduct("Valheim Foresight")]
[assembly: AssemblyCopyright("Copyright © 2025")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("AA62F894-B9B4-4C38-95B2-F74902F5E0A9")]
[assembly: AssemblyFileVersion("1.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.0.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

		public NullableAttribute(byte P_0)
		{
			NullableFlags = new byte[1] { P_0 };
		}

		public NullableAttribute(byte[] P_0)
		{
			NullableFlags = P_0;
		}
	}
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableContextAttribute : Attribute
	{
		public readonly byte Flag;

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.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 Valheim.Foresight
{
	[BepInPlugin("coffeenova.valheim.foresight", "Valheim Foresight", "1.0.0")]
	public sealed class ValheimForesightPlugin : BaseUnityPlugin
	{
		internal static ILogger Log;

		private static ValheimForesightPlugin? _instance;

		private readonly Dictionary<Character?, ThreatAssessment?> _threatCache = new Dictionary<Character, ThreatAssessment>();

		private ForesightConfiguration _config;

		private IThreatCalculationService _threatService;

		private IDifficultyMultiplierCalculator _difficultyCalculator;

		private float _playerLogTimer;

		private float _enemyUpdateTimer;

		public static bool InstanceDebugHudEnabled => _instance?._config.DebugHudEnabled.Value ?? false;

		public static bool TryGetThreatAssessment(Character? character, out ThreatAssessment? assessment)
		{
			assessment = null;
			if ((Object)(object)_instance == (Object)null || (Object)(object)character == (Object)null)
			{
				return false;
			}
			return _instance._threatCache.TryGetValue(character, out assessment);
		}

		private void Awake()
		{
			_instance = this;
			InitializeServices();
			ApplyHarmonyPatches();
			LogDifficultySettings();
			Log.LogInfo("Valheim Foresight 1.0.0 loaded");
		}

		private void InitializeServices()
		{
			_config = new ForesightConfiguration(((BaseUnityPlugin)this).Config);
			_config.SettingsChanged += OnConfigurationChanged;
			Log = new ForesightLogger(((BaseUnityPlugin)this).Logger)
			{
				IsLogsEnabled = _config.IsLogsEnabled.Value,
				IsDebugLogsEnabled = _config.DebugHudEnabled.Value
			};
			BlockDamageEstimator blockEstimator = new BlockDamageEstimator(Log);
			ParryDamageEstimator parryEstimator = new ParryDamageEstimator(Log);
			Lazy<ICreatureAttackInspector> attackInspector = new Lazy<ICreatureAttackInspector>(delegate
			{
				if ((Object)(object)ZNetScene.instance == (Object)null)
				{
					Log.LogError("ZNetScene not found");
					return null;
				}
				return new CreatureAttackInspector(new ZNetSceneWrapper(ZNetScene.instance), Log);
			});
			PlayerWrapper playerWrapper = new PlayerWrapper();
			ZoneSystemWrapper zoneSystemWrapper = new ZoneSystemWrapper();
			MathfWrapper mathfWrapper = new MathfWrapper();
			Vector3Wrapper vector3Wrapper = new Vector3Wrapper();
			_difficultyCalculator = new DifficultyMultiplierCalculator(Log, playerWrapper, zoneSystemWrapper, mathfWrapper);
			_threatService = new ThreatCalculationService(Log, blockEstimator, parryEstimator, attackInspector, _difficultyCalculator, vector3Wrapper, mathfWrapper);
		}

		private void ApplyHarmonyPatches()
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Expected O, but got Unknown
			//IL_0075: Unknown result type (might be due to invalid IL or missing references)
			//IL_0082: Expected O, but got Unknown
			Harmony val = new Harmony("coffeenova.valheim.foresight");
			val.PatchAll();
			MethodInfo methodInfo = AccessTools.Method(typeof(EnemyHud), "LateUpdate", (Type[])null, (Type[])null);
			MethodInfo methodInfo2 = AccessTools.Method(typeof(EnemyHudPatch), "LateUpdatePostfix", (Type[])null, (Type[])null);
			if (methodInfo == null)
			{
				Log.LogError("Failed to find EnemyHud.LateUpdate");
				return;
			}
			if (methodInfo2 == null)
			{
				Log.LogError("Failed to find EnemyHudPatch.LateUpdatePostfix");
				return;
			}
			val.Patch((MethodBase)methodInfo, (HarmonyMethod)null, new HarmonyMethod(methodInfo2), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			Log.LogInfo("Patched EnemyHud.LateUpdate");
		}

		private void Update()
		{
			_playerLogTimer += Time.deltaTime;
			_enemyUpdateTimer += Time.deltaTime;
			if (_playerLogTimer >= 1f)
			{
				_playerLogTimer = 0f;
				LogPlayerHealth();
			}
			if (_enemyUpdateTimer >= 1f)
			{
				_enemyUpdateTimer = 0f;
				UpdateNearbyEnemiesThreat();
			}
			CleanupThreatCache();
		}

		private void LogPlayerHealth()
		{
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null)
			{
				Log.LogDebug("Player not spawned yet");
			}
			else
			{
				Log.LogDebug($"Player HP: {((Character)localPlayer).GetHealth()}");
			}
		}

		private void UpdateNearbyEnemiesThreat()
		{
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: 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)
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null)
			{
				return;
			}
			List<Character> allCharacters = Character.GetAllCharacters();
			Vector3 position = ((Component)localPlayer).transform.position;
			foreach (Character item in allCharacters)
			{
				if (!IsValidEnemy(item))
				{
					continue;
				}
				Vector3 val = ((Component)item).transform.position - position;
				if (!(((Vector3)(ref val)).sqrMagnitude > 10000f))
				{
					ThreatAssessment threatAssessment = _threatService.CalculateThreat(item, localPlayer, detailedMode: false);
					if (threatAssessment != null)
					{
						_threatCache[item] = threatAssessment;
					}
				}
			}
		}

		private bool TryFindNearestEnemy(Player player, out Character nearestEnemy)
		{
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: 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_0044: Unknown result type (might be due to invalid IL or missing references)
			nearestEnemy = null;
			List<Character> allCharacters = Character.GetAllCharacters();
			float num = float.MaxValue;
			Vector3 position = ((Component)player).transform.position;
			foreach (Character item in allCharacters)
			{
				if (IsValidEnemy(item))
				{
					Vector3 val = ((Component)item).transform.position - position;
					float sqrMagnitude = ((Vector3)(ref val)).sqrMagnitude;
					if (sqrMagnitude < num)
					{
						num = sqrMagnitude;
						nearestEnemy = item;
					}
				}
			}
			return (Object)(object)nearestEnemy != (Object)null;
		}

		private bool IsValidEnemy(Character character)
		{
			if ((Object)(object)character != (Object)null && !character.IsPlayer())
			{
				return !string.IsNullOrEmpty(character.m_name);
			}
			return false;
		}

		private void CleanupThreatCache()
		{
			//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_0045: Unknown result type (might be due to invalid IL or missing references)
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null)
			{
				return;
			}
			List<Character> list = new List<Character>();
			Vector3 position = ((Component)localPlayer).transform.position;
			foreach (KeyValuePair<Character, ThreatAssessment> item in _threatCache)
			{
				Character key = item.Key;
				if (ShouldRemoveFromCache(key, position, 10000f))
				{
					list.Add(key);
				}
			}
			foreach (Character item2 in list)
			{
				if ((Object)(object)item2 != (Object)null)
				{
					_threatCache.Remove(item2);
				}
			}
		}

		private bool ShouldRemoveFromCache(Character? character, Vector3 playerPos, float maxDistanceSq)
		{
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)character == (Object)null || character.IsDead())
			{
				return true;
			}
			Vector3 val = ((Component)character).transform.position - playerPos;
			return ((Vector3)(ref val)).sqrMagnitude > maxDistanceSq;
		}

		private void LogDifficultySettings()
		{
			if ((Object)(object)ZoneSystem.instance == (Object)null)
			{
				Log.LogDebug("ZoneSystem not ready yet");
				return;
			}
			List<string> allGlobalKeys = _difficultyCalculator.GetAllGlobalKeys();
			Log.LogInfo($"Global Keys count: {allGlobalKeys.Count}");
			string text = default(string);
			foreach (string item in allGlobalKeys)
			{
				if (ZoneSystem.instance.GetGlobalKey(item, ref text))
				{
					Log.LogInfo("  " + item + " = " + text);
				}
				else
				{
					Log.LogInfo("  " + item + " (no value)");
				}
			}
			float incomingDamageFactor = _difficultyCalculator.GetIncomingDamageFactor();
			float enemyHealthFactor = _difficultyCalculator.GetEnemyHealthFactor();
			Log.LogInfo($"Current difficulty: EnemyDamage={incomingDamageFactor:F2}x, EnemyHP={enemyHealthFactor:F2}x");
		}

		private void OnConfigurationChanged(object? sender, EventArgs e)
		{
			Log.IsLogsEnabled = _config.IsLogsEnabled.Value;
			Log.IsDebugLogsEnabled = _config.DebugHudEnabled.Value;
		}
	}
}
namespace Valheim.Foresight.Autogen
{
	internal static class PluginInfoGenerated
	{
		internal const string PluginVersion = "1.0.0";

		internal const string PluginName = "Valheim Foresight";

		internal const string PluginGuid = "coffeenova.valheim.foresight";
	}
}
namespace Valheim.Foresight.Services.Damage
{
	public sealed class BlockDamageEstimator : DamageEstimatorBase
	{
		private const float BlockingSkillPercentPerLevel = 0.005f;

		public BlockDamageEstimator(ILogger logger)
			: base(logger)
		{
		}

		protected override float ApplyActiveDefense(PlayerDefenseStats defenseStats, float physicalDamage)
		{
			if (physicalDamage <= 0f)
			{
				return 0f;
			}
			if (defenseStats.Shield == null)
			{
				return physicalDamage;
			}
			float num = CalculateEffectiveBlockPower(defenseStats);
			float num2 = Mathf.Min(physicalDamage, num);
			float num3 = physicalDamage - num2;
			Logger.LogDebug(string.Format("[{0}] Block: raw={1:F1}, ", "BlockDamageEstimator", physicalDamage) + $"baseBlock={defenseStats.Shield.m_shared.m_blockPower:F1}, " + $"skill={defenseStats.BlockingSkillLevel:F0}, effBlock={num:F1}, " + $"blocked={num2:F1}, remain={num3:F1}");
			return num3;
		}

		private float CalculateEffectiveBlockPower(PlayerDefenseStats defenseStats)
		{
			if (defenseStats.Shield == null)
			{
				return 0f;
			}
			float blockPower = defenseStats.Shield.m_shared.m_blockPower;
			float num = 1f + 0.005f * defenseStats.BlockingSkillLevel;
			return blockPower * num;
		}
	}
	public abstract class DamageEstimatorBase : IDamageEstimator
	{
		private const float MinimumDamage = 1f;

		protected readonly ILogger Logger;

		protected DamageEstimatorBase(ILogger logger)
		{
			Logger = logger ?? throw new ArgumentNullException("logger");
		}

		public float EstimateEffectiveDamage(Player player, float rawDamage)
		{
			if ((Object)(object)player == (Object)null || rawDamage <= 0f)
			{
				return 0f;
			}
			PlayerDefenseStats defenseStats = PlayerDefenseStats.FromPlayer(player);
			return EstimateEffectiveDamage(defenseStats, rawDamage);
		}

		public float EstimateEffectiveDamage(PlayerDefenseStats defenseStats, float rawDamage)
		{
			if (rawDamage <= 0f)
			{
				return 0f;
			}
			float physicalDamage = ApplyActiveDefense(defenseStats, rawDamage);
			float physicalAfterArmor = ApplyArmor(defenseStats.Armor, physicalDamage);
			float num = ApplyResistances(physicalAfterArmor, 0f);
			return Mathf.Max(1f, num);
		}

		protected abstract float ApplyActiveDefense(PlayerDefenseStats defenseStats, float physicalDamage);

		private float ApplyArmor(float armor, float physicalDamage)
		{
			if (physicalDamage <= 0f)
			{
				return 0f;
			}
			float num = ((armor <= 0f) ? physicalDamage : ((!(armor < physicalDamage / 2f)) ? (physicalDamage * physicalDamage / (4f * armor)) : (physicalDamage - armor)));
			num = Mathf.Max(1f, num);
			Logger.LogDebug($"[{GetType().Name}] Armor: in={physicalDamage:F1}, armor={armor:F1}, out={num:F1}");
			return num;
		}

		private float ApplyResistances(float physicalAfterArmor, float elementalDamage)
		{
			float num = physicalAfterArmor + elementalDamage;
			Logger.LogDebug($"[{GetType().Name}] Resists: phys={physicalAfterArmor:F1}, elem={elementalDamage:F1}, total={num:F1}");
			return num;
		}
	}
	public interface IDamageEstimator
	{
		float EstimateEffectiveDamage(Player player, float rawDamage);

		float EstimateEffectiveDamage(PlayerDefenseStats defenseStats, float rawDamage);
	}
	public sealed class ParryDamageEstimator : DamageEstimatorBase
	{
		private const float BlockingSkillPercentPerLevel = 0.005f;

		public ParryDamageEstimator(ILogger logger)
			: base(logger)
		{
		}

		protected override float ApplyActiveDefense(PlayerDefenseStats defenseStats, float physicalDamage)
		{
			if (physicalDamage <= 0f)
			{
				return 0f;
			}
			if (defenseStats.Shield == null)
			{
				return physicalDamage;
			}
			float num = CalculateEffectiveParryPower(defenseStats);
			float num2 = Mathf.Min(physicalDamage, num);
			float num3 = physicalDamage - num2;
			Logger.LogDebug(string.Format("[{0}] Parry: raw={1:F1}, ", "ParryDamageEstimator", physicalDamage) + $"baseBlock={defenseStats.Shield.m_shared.m_blockPower:F1}, " + $"parryBonus={defenseStats.Shield.m_shared.m_timedBlockBonus:F1}, " + $"skill={defenseStats.BlockingSkillLevel:F0}, " + $"effBlock={num:F1}, blocked={num2:F1}, remain={num3:F1}");
			return num3;
		}

		private float CalculateEffectiveParryPower(PlayerDefenseStats defenseStats)
		{
			if (defenseStats.Shield == null)
			{
				return 0f;
			}
			SharedData shared = defenseStats.Shield.m_shared;
			float blockPower = shared.m_blockPower;
			float timedBlockBonus = shared.m_timedBlockBonus;
			float num = 1f + 0.005f * defenseStats.BlockingSkillLevel;
			return (blockPower + timedBlockBonus) * num;
		}
	}
}
namespace Valheim.Foresight.Services.Combat
{
	public sealed class CreatureAttackInspector : ICreatureAttackInspector
	{
		private readonly IZNetSceneWrapper _zNetSceneWrapper;

		private readonly ILogger _logger;

		public CreatureAttackInspector(IZNetSceneWrapper zNetSceneWrapper, ILogger logger)
		{
			_zNetSceneWrapper = zNetSceneWrapper ?? throw new ArgumentNullException("zNetSceneWrapper");
			_logger = logger ?? throw new ArgumentNullException("logger");
		}

		public float GetMaxAttackByPrefabName(string prefabName)
		{
			if (string.IsNullOrEmpty(prefabName) || _zNetSceneWrapper == null)
			{
				return 0f;
			}
			GameObject prefab = _zNetSceneWrapper.GetPrefab(prefabName);
			if (!((Object)(object)prefab == (Object)null))
			{
				return InspectPrefabForMaxDamage(prefab);
			}
			return 0f;
		}

		public float GetMaxAttackForCharacter(Character character)
		{
			if ((Object)(object)character == (Object)null)
			{
				return 0f;
			}
			return InspectPrefabForMaxDamage(((Component)character).gameObject);
		}

		private float InspectPrefabForMaxDamage(GameObject prefabRoot)
		{
			if ((Object)(object)prefabRoot == (Object)null)
			{
				return 0f;
			}
			float maxDamage = 0f;
			InspectHumanoidEquipment(prefabRoot, ref maxDamage);
			InspectItemDrops(prefabRoot, ref maxDamage);
			InspectAttackComponents(prefabRoot, ref maxDamage);
			_logger.LogDebug(string.Format("[{0}] Max damage: {1}", "InspectPrefabForMaxDamage", maxDamage));
			return maxDamage;
		}

		private void InspectHumanoidEquipment(GameObject prefabRoot, ref float maxDamage)
		{
			Humanoid componentInChildren = prefabRoot.GetComponentInChildren<Humanoid>(true);
			if (!((Object)(object)componentInChildren == (Object)null))
			{
				FieldRef<Humanoid, ItemData>? rightItemRef = HumanoidFieldRefs.RightItemRef;
				ItemData item = ((rightItemRef != null) ? rightItemRef.Invoke(componentInChildren) : null);
				FieldRef<Humanoid, ItemData>? leftItemRef = HumanoidFieldRefs.LeftItemRef;
				ItemData item2 = ((leftItemRef != null) ? leftItemRef.Invoke(componentInChildren) : null);
				UpdateMaxFromItem(item, "RightItem", ref maxDamage);
				UpdateMaxFromItem(item2, "LeftItem", ref maxDamage);
				if ((Object)(object)componentInChildren.m_unarmedWeapon != (Object)null)
				{
					UpdateMaxFromItem(componentInChildren.m_unarmedWeapon.m_itemData, "UnarmedWeapon", ref maxDamage);
				}
			}
		}

		private void InspectItemDrops(GameObject prefabRoot, ref float maxDamage)
		{
			ItemDrop[] componentsInChildren = prefabRoot.GetComponentsInChildren<ItemDrop>(true);
			foreach (ItemDrop val in componentsInChildren)
			{
				UpdateMaxFromItem(val.m_itemData, ((Object)val).name, ref maxDamage);
			}
		}

		private void InspectAttackComponents(GameObject prefabRoot, ref float maxDamage)
		{
			Attack[] componentsInChildren = prefabRoot.GetComponentsInChildren<Attack>(true);
			foreach (Attack attack in componentsInChildren)
			{
				UpdateMaxFromAttack(attack, ref maxDamage);
			}
		}

		private void UpdateMaxFromItem(ItemData? item, string source, ref float maxDamage)
		{
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			if (item != null)
			{
				float num = CalculateTotalDamage(item.m_shared.m_damages, source);
				if (num > maxDamage)
				{
					maxDamage = num;
				}
			}
		}

		private void UpdateMaxFromAttack(Attack? attack, ref float maxDamage)
		{
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			if (attack != null)
			{
				float num = 0f;
				if (attack.m_weapon != null)
				{
					num = CalculateTotalDamage(attack.m_weapon.m_shared.m_damages, "Attack.Weapon");
				}
				float num2 = ((attack.m_damageMultiplier > 0f) ? attack.m_damageMultiplier : 1f);
				float num3 = num * num2;
				if (num3 > maxDamage)
				{
					maxDamage = num3;
				}
			}
		}

		private float CalculateTotalDamage(DamageTypes dmg, string source)
		{
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			//IL_0060: Unknown result type (might be due to invalid IL or missing references)
			//IL_006b: 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_008e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0099: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b4: 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)
			//IL_00c1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cf: 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_00dd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00eb: Unknown result type (might be due to invalid IL or missing references)
			_logger.LogDebug("[CalculateTotalDamage] " + source + ": " + $"physical={dmg.m_damage}, blunt={dmg.m_blunt}, slash={dmg.m_slash}, " + $"pierce={dmg.m_pierce}, fire={dmg.m_fire}, frost={dmg.m_frost}, " + $"lightning={dmg.m_lightning}, poison={dmg.m_poison}, spirit={dmg.m_spirit}");
			float num = dmg.m_damage + dmg.m_slash + dmg.m_pierce + dmg.m_blunt + dmg.m_fire + dmg.m_frost + dmg.m_lightning + dmg.m_poison + dmg.m_spirit;
			_logger.LogDebug(string.Format("[{0}] Total from {1}: {2}", "CalculateTotalDamage", source, num));
			return num;
		}
	}
	public sealed class DifficultyMultiplierCalculator : IDifficultyMultiplierCalculator
	{
		private const float PlayerCountRadius = 200f;

		private const float DamagePerExtraPlayer = 0.04f;

		private const string EnemyDamageKey = "EnemyDamage";

		private const string PlayerDamageKey = "PlayerDamage";

		private readonly ILogger _logger;

		private readonly IPlayerWrapper _playerWrapper;

		private readonly IZoneSystemWrapper _zoneSystemWrapper;

		private readonly IMathfWrapper _mathfWrapper;

		public DifficultyMultiplierCalculator(ILogger logger, IPlayerWrapper playerWrapper, IZoneSystemWrapper zoneSystemWrapper, IMathfWrapper mathfWrapper)
		{
			_logger = logger ?? throw new ArgumentNullException("logger");
			_playerWrapper = playerWrapper ?? throw new ArgumentNullException("playerWrapper");
			_zoneSystemWrapper = zoneSystemWrapper ?? throw new ArgumentNullException("zoneSystemWrapper");
			_mathfWrapper = mathfWrapper ?? throw new ArgumentNullException("mathfWrapper");
		}

		public float GetDamageMultiplier(Vector3 position)
		{
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			float worldDifficultyMultiplier = GetWorldDifficultyMultiplier();
			float playerCountMultiplier = GetPlayerCountMultiplier(position);
			float num = worldDifficultyMultiplier * playerCountMultiplier;
			_logger.LogDebug("[GetDamageMultiplier] " + $"worldDifficulty={worldDifficultyMultiplier:F2}, " + $"playerMultiplier={playerCountMultiplier:F2}, " + $"total={num:F2}");
			return num;
		}

		public float GetWorldDifficultyMultiplier()
		{
			float incomingDamageFactor = GetIncomingDamageFactor();
			_logger.LogDebug(string.Format("[{0}] scale={1:F2}", "GetWorldDifficultyMultiplier", incomingDamageFactor));
			return incomingDamageFactor;
		}

		public float GetPlayerCountMultiplier(Vector3 position)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			int nearbyPlayerCount = GetNearbyPlayerCount(position);
			float num = 1f + (float)_mathfWrapper.Max(0, nearbyPlayerCount - 1) * 0.04f;
			_logger.LogDebug("[GetPlayerCountMultiplier] " + $"players={nearbyPlayerCount}, multiplier={num:F2}");
			return num;
		}

		public int GetNearbyPlayerCount(Vector3 position)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			int playersInRangeXZ = _playerWrapper.GetPlayersInRangeXZ(position, 200f);
			_logger.LogDebug(string.Format("[{0}] Found {1} players within {2}m", "GetNearbyPlayerCount", playersInRangeXZ, 200f));
			return playersInRangeXZ;
		}

		public float GetIncomingDamageFactor()
		{
			try
			{
				if (!_zoneSystemWrapper.IsInitialized)
				{
					_logger.LogDebug("[GetIncomingDamageFactor] ZoneSystem not initialized");
					return 1f;
				}
				if (_zoneSystemWrapper.GetGlobalKey("EnemyDamage", out string value))
				{
					return ParseDamageMultiplier("EnemyDamage", value);
				}
				if (_zoneSystemWrapper.TryGetGlobalKeyValue("EnemyDamage", out string value2))
				{
					return ParseDamageMultiplier("EnemyDamage", value2);
				}
				_logger.LogDebug("[GetIncomingDamageFactor] EnemyDamage not set, using default 1.0x");
				return 1f;
			}
			catch (Exception ex)
			{
				_logger.LogError("[GetIncomingDamageFactor] Exception: " + ex.Message);
				return 1f;
			}
		}

		public float GetEnemyHealthFactor()
		{
			try
			{
				if (!_zoneSystemWrapper.IsInitialized)
				{
					return 1f;
				}
				if (_zoneSystemWrapper.GetGlobalKey("PlayerDamage", out string value) && float.TryParse(value, NumberStyles.Float, CultureInfo.InvariantCulture, out var result))
				{
					float num = ((result > 0f) ? (100f / result) : 1f);
					_logger.LogDebug(string.Format("[{0}] {1}={2}% -> enemy HP {3:F2}x", "GetEnemyHealthFactor", "PlayerDamage", value, num));
					return num;
				}
				return 1f;
			}
			catch (Exception ex)
			{
				_logger.LogError("[GetEnemyHealthFactor] Exception: " + ex.Message);
				return 1f;
			}
		}

		public bool HasGlobalKey(string key)
		{
			if (_zoneSystemWrapper.IsInitialized)
			{
				return _zoneSystemWrapper.GetGlobalKey(key);
			}
			return false;
		}

		List<string> IDifficultyMultiplierCalculator.GetAllGlobalKeys()
		{
			return _zoneSystemWrapper.GetGlobalKeys();
		}

		private float ParseDamageMultiplier(string keyName, string value)
		{
			if (string.IsNullOrEmpty(value))
			{
				return 1f;
			}
			if (float.TryParse(value, NumberStyles.Float, CultureInfo.InvariantCulture, out var result))
			{
				float num = result / 100f;
				_logger.LogDebug(string.Format("[{0}] {1}={2}% -> {3:F2}x", "ParseDamageMultiplier", keyName, value, num));
				if (!(num > 0f))
				{
					return 1f;
				}
				return num;
			}
			_logger.LogWarning("[ParseDamageMultiplier] Failed to parse " + keyName + "='" + value + "', using default");
			return 1f;
		}
	}
	public sealed class ThreatCalculationService : IThreatCalculationService
	{
		private const float MeleeRangeThreshold = 10f;

		private readonly ILogger _logger;

		private readonly IDamageEstimator _blockEstimator;

		private readonly IDamageEstimator _parryEstimator;

		private readonly Lazy<ICreatureAttackInspector?> _attackInspector;

		private readonly IDifficultyMultiplierCalculator _difficultyCalculator;

		private readonly IVector3Wrapper _vector3Wrapper;

		private readonly IMathfWrapper _mathfWrapper;

		public ThreatCalculationService(ILogger logger, IDamageEstimator blockEstimator, IDamageEstimator parryEstimator, Lazy<ICreatureAttackInspector?> attackInspector, IDifficultyMultiplierCalculator difficultyCalculator, IVector3Wrapper vector3Wrapper, IMathfWrapper mathfWrapper)
		{
			_logger = logger ?? throw new ArgumentNullException("logger");
			_blockEstimator = blockEstimator ?? throw new ArgumentNullException("blockEstimator");
			_parryEstimator = parryEstimator ?? throw new ArgumentNullException("parryEstimator");
			_attackInspector = attackInspector ?? throw new ArgumentNullException("attackInspector");
			_difficultyCalculator = difficultyCalculator ?? throw new ArgumentNullException("difficultyCalculator");
			_vector3Wrapper = vector3Wrapper ?? throw new ArgumentNullException("vector3Wrapper");
			_mathfWrapper = mathfWrapper ?? throw new ArgumentNullException("mathfWrapper");
		}

		public ThreatAssessment? CalculateThreat(Character enemy, Player player, bool detailedMode)
		{
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)enemy == (Object)null || (Object)(object)player == (Object)null)
			{
				return null;
			}
			Humanoid val = (Humanoid)(object)((enemy is Humanoid) ? enemy : null);
			if (val == null)
			{
				_logger.LogDebug("Enemy " + enemy.m_name + " is not Humanoid");
				return null;
			}
			float distance = _vector3Wrapper.Distance(((Component)enemy).transform.position, ((Component)player).transform.position);
			float baseDamage;
			float maxMeleeDamage;
			float maxRangedDamage;
			bool usedRangedAttack;
			if (detailedMode)
			{
				(baseDamage, maxMeleeDamage, maxRangedDamage, usedRangedAttack) = CalculateDetailedThreat(val, distance);
			}
			else
			{
				float num = CalculateSimpleThreat(val);
				usedRangedAttack = false;
				maxRangedDamage = 0f;
				maxMeleeDamage = 0f;
				baseDamage = num;
			}
			float rawDamage = ApplyDifficultyMultipliers(enemy, baseDamage);
			DamageInfo damageInfo = CalculateDamageInfo(player, rawDamage);
			float num2 = CalculateDamageRatio(player, damageInfo.EffectiveDamageWithBlock);
			ThreatLevel threatLevel = DetermineThreatLevel(CalculateDamageRatio(player, damageInfo.EffectiveDamageWithBlock), CalculateDamageRatio(player, damageInfo.EffectiveDamageWithParry));
			LogThreatCalculation(enemy, distance, baseDamage, rawDamage, damageInfo, num2, threatLevel);
			return new ThreatAssessment(threatLevel, damageInfo, num2, maxMeleeDamage, maxRangedDamage, usedRangedAttack);
		}

		public ThreatLevel DetermineThreatLevel(float blockRatio, float parryRatio)
		{
			if (parryRatio >= 1f)
			{
				return ThreatLevel.Danger;
			}
			if (blockRatio >= 1f && parryRatio < 1f)
			{
				return ThreatLevel.BlockLethal;
			}
			if (blockRatio >= 0.3f)
			{
				return ThreatLevel.Caution;
			}
			return ThreatLevel.Safe;
		}

		private float CalculateSimpleThreat(Humanoid humanoid)
		{
			if (_attackInspector.Value == null)
			{
				return 0f;
			}
			float maxAttackForCharacter = _attackInspector.Value.GetMaxAttackForCharacter((Character)(object)humanoid);
			_logger.LogDebug(string.Format("[{0}] {1}: maxAttack={2:F1}", "CalculateSimpleThreat", ((Character)humanoid).m_name, maxAttackForCharacter));
			return maxAttackForCharacter;
		}

		private (float baseDamage, float maxMelee, float maxRanged, bool usedRanged) CalculateDetailedThreat(Humanoid humanoid, float distance)
		{
			(float maxMelee, float maxRanged) weaponDamages = GetWeaponDamages(humanoid);
			float item = weaponDamages.maxMelee;
			float item2 = weaponDamages.maxRanged;
			(float damage, bool isRanged) currentAttackInfo = GetCurrentAttackInfo(humanoid);
			float item3 = currentAttackInfo.damage;
			bool item4 = currentAttackInfo.isRanged;
			bool flag = DetermineAttackType(distance, item, item2, item4);
			float num = SelectBaseDamage(item, item2, flag);
			_logger.LogDebug("[CalculateDetailedThreat] " + ((Character)humanoid).m_name + ": " + $"melee={item:F1}, ranged={item2:F1}, " + $"currentAttack={item3:F1}, useRanged={flag}, " + $"dist={distance:F1}, baseDamage={num:F1}");
			return (num, item, item2, flag);
		}

		private (float maxMelee, float maxRanged) GetWeaponDamages(Humanoid humanoid)
		{
			if ((Object)(object)humanoid == (Object)null)
			{
				return (0f, 0f);
			}
			ItemData item = HumanoidMethodRefs.GetRightItem?.Invoke(humanoid);
			ItemData item2 = HumanoidMethodRefs.GetLeftItem?.Invoke(humanoid);
			float itemDamage = GetItemDamage(item);
			float itemDamage2 = GetItemDamage(item2);
			float item3 = _mathfWrapper.Max(itemDamage, itemDamage2);
			float item4 = 0f;
			return (item3, item4);
		}

		private (float damage, bool isRanged) GetCurrentAttackInfo(Humanoid humanoid)
		{
			if ((Object)(object)humanoid == (Object)null)
			{
				return (0f, false);
			}
			FieldRef<Humanoid, Attack>? currentAttackRef = HumanoidFieldRefs.CurrentAttackRef;
			Attack val = ((currentAttackRef != null) ? currentAttackRef.Invoke(humanoid) : null);
			if (val == null)
			{
				return (0f, false);
			}
			FieldRef<Attack, ItemData>? weaponRef = AttackFieldRefs.WeaponRef;
			ItemData val2 = ((weaponRef != null) ? weaponRef.Invoke(val) : null);
			float num = ((val2 != null) ? GetItemDamage(val2) : 0f);
			FieldRef<Attack, float>? damageMultiplierRef = AttackFieldRefs.DamageMultiplierRef;
			float num2 = ((damageMultiplierRef != null) ? damageMultiplierRef.Invoke(val) : 1f);
			if (num2 > 0f)
			{
				num *= num2;
			}
			FieldRef<Attack, AttackType>? attackTypeRef = AttackFieldRefs.AttackTypeRef;
			int num3 = ((attackTypeRef != null) ? ((int)attackTypeRef.Invoke(val)) : 0);
			FieldRef<Attack, GameObject>? projectilePrefabRef = AttackFieldRefs.ProjectilePrefabRef;
			GameObject val3 = ((projectilePrefabRef != null) ? projectilePrefabRef.Invoke(val) : null);
			bool item = num3 == 2 || (Object)(object)val3 != (Object)null;
			return (num, item);
		}

		private float GetItemDamage(ItemData? item)
		{
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: 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)
			//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_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: 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)
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			if (item == null)
			{
				return 0f;
			}
			DamageTypes damages = item.m_shared.m_damages;
			return damages.m_damage + damages.m_blunt + damages.m_slash + damages.m_pierce + damages.m_chop + damages.m_pickaxe + damages.m_fire + damages.m_frost + damages.m_lightning + damages.m_poison + damages.m_spirit;
		}

		private bool DetermineAttackType(float distance, float maxMelee, float maxRanged, bool currentIsRanged)
		{
			if (distance <= 10f && maxMelee > 0f)
			{
				return false;
			}
			if (maxRanged > 0f)
			{
				return true;
			}
			return currentIsRanged;
		}

		private float SelectBaseDamage(float maxMelee, float maxRanged, bool useRanged)
		{
			if (useRanged && maxRanged > 0f)
			{
				return maxRanged;
			}
			if (!useRanged && maxMelee > 0f)
			{
				return maxMelee;
			}
			return _mathfWrapper.Max(maxMelee, maxRanged);
		}

		private float ApplyDifficultyMultipliers(Character enemy, float baseDamage)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: 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)
			Vector3 position = ((Component)enemy).transform.position;
			float damageMultiplier = _difficultyCalculator.GetDamageMultiplier(position);
			return baseDamage * damageMultiplier;
		}

		private DamageInfo CalculateDamageInfo(Player player, float rawDamage)
		{
			float effectiveWithBlock = _blockEstimator.EstimateEffectiveDamage(player, rawDamage);
			float effectiveWithParry = _parryEstimator.EstimateEffectiveDamage(player, rawDamage);
			return new DamageInfo(rawDamage, effectiveWithBlock, effectiveWithParry);
		}

		private float CalculateDamageRatio(Player player, float effectiveDamage)
		{
			float health = ((Character)player).GetHealth();
			if (!(health > 0f))
			{
				return 0f;
			}
			return effectiveDamage / health;
		}

		private void LogThreatCalculation(Character enemy, float distance, float baseDamage, float rawDamage, DamageInfo damageInfo, float ratio, ThreatLevel threatLevel)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			Vector3 position = ((Component)enemy).transform.position;
			float worldDifficultyMultiplier = _difficultyCalculator.GetWorldDifficultyMultiplier();
			int nearbyPlayerCount = _difficultyCalculator.GetNearbyPlayerCount(position);
			float playerCountMultiplier = _difficultyCalculator.GetPlayerCountMultiplier(position);
			float damageMultiplier = _difficultyCalculator.GetDamageMultiplier(position);
			_logger.LogDebug($"Threat: {enemy.m_name} lvl{enemy.GetLevel()} dist={distance:F1}m, " + $"base={baseDamage:F1}, worldDiff={worldDifficultyMultiplier:F2}x, " + $"players={nearbyPlayerCount} ({playerCountMultiplier:F2}x), totalMult={damageMultiplier:F2}x, " + $"raw={rawDamage:F1}, effBlock={damageInfo.EffectiveDamageWithBlock:F1}, " + $"effParry={damageInfo.EffectiveDamageWithParry:F1}, ratio={ratio:F2}, " + $"threat={threatLevel}");
		}
	}
}
namespace Valheim.Foresight.Services.Combat.Wrappers
{
	public sealed class MathfWrapper : IMathfWrapper
	{
		public int Max(int a, int b)
		{
			return Mathf.Max(a, b);
		}

		public float Max(float a, float b)
		{
			return Mathf.Max(a, b);
		}
	}
	public sealed class PlayerWrapper : IPlayerWrapper
	{
		public int GetPlayersInRangeXZ(Vector3 position, float radius)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			return Player.GetPlayersInRangeXZ(position, radius);
		}
	}
	public sealed class Vector3Wrapper : IVector3Wrapper
	{
		public float Distance(Vector3 a, Vector3 b)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			return Vector3.Distance(a, b);
		}
	}
	public sealed class ZNetSceneWrapper : IZNetSceneWrapper
	{
		private readonly ZNetScene _zNetScene;

		public ZNetSceneWrapper(ZNetScene zNetScene)
		{
			_zNetScene = zNetScene ?? throw new ArgumentNullException("zNetScene");
		}

		public GameObject? GetPrefab(string name)
		{
			return _zNetScene.GetPrefab(name);
		}
	}
	public sealed class ZoneSystemWrapper : IZoneSystemWrapper
	{
		public bool IsInitialized => (Object)(object)ZoneSystem.instance != (Object)null;

		public bool GetGlobalKey(string key, out string value)
		{
			value = string.Empty;
			if ((Object)(object)ZoneSystem.instance == (Object)null)
			{
				return false;
			}
			return ZoneSystem.instance.GetGlobalKey(key, ref value);
		}

		public bool GetGlobalKey(string key)
		{
			if ((Object)(object)ZoneSystem.instance == (Object)null)
			{
				return false;
			}
			return ZoneSystem.instance.GetGlobalKey(key);
		}

		public bool TryGetGlobalKeyValue(string key, out string value)
		{
			value = string.Empty;
			if ((Object)(object)ZoneSystem.instance == (Object)null || ZoneSystem.instance.m_globalKeysValues == null)
			{
				return false;
			}
			return ZoneSystem.instance.m_globalKeysValues.TryGetValue(key, out value);
		}

		public List<string> GetGlobalKeys()
		{
			ZoneSystem instance = ZoneSystem.instance;
			return ((instance != null) ? instance.GetGlobalKeys() : null) ?? new List<string>();
		}
	}
}
namespace Valheim.Foresight.Services.Combat.Interfaces
{
	public interface ICreatureAttackInspector
	{
		float GetMaxAttackByPrefabName(string prefabName);

		float GetMaxAttackForCharacter(Character character);
	}
	public interface IDifficultyMultiplierCalculator
	{
		float GetDamageMultiplier(Vector3 position);

		float GetWorldDifficultyMultiplier();

		float GetPlayerCountMultiplier(Vector3 position);

		int GetNearbyPlayerCount(Vector3 position);

		float GetIncomingDamageFactor();

		float GetEnemyHealthFactor();

		List<string> GetAllGlobalKeys();
	}
	public interface IMathfWrapper
	{
		int Max(int a, int b);

		float Max(float a, float b);
	}
	public interface IPlayerWrapper
	{
		int GetPlayersInRangeXZ(Vector3 position, float radius);
	}
	public interface IThreatCalculationService
	{
		ThreatAssessment? CalculateThreat(Character enemy, Player player, bool detailedMode);

		ThreatLevel DetermineThreatLevel(float blockRatio, float parryRatio);
	}
	public interface IVector3Wrapper
	{
		float Distance(Vector3 a, Vector3 b);
	}
	public interface IZNetSceneWrapper
	{
		GameObject? GetPrefab(string name);
	}
	public interface IZoneSystemWrapper
	{
		bool IsInitialized { get; }

		bool GetGlobalKey(string key, out string value);

		bool GetGlobalKey(string key);

		bool TryGetGlobalKeyValue(string key, out string value);

		List<string> GetGlobalKeys();
	}
}
namespace Valheim.Foresight.Patches
{
	public static class EnemyHudPatch
	{
		private static readonly Color SafeColor = Color.white;

		private static readonly Color CautionColor = new Color(1f, 0.75f, 0.25f);

		private static readonly Color BlockLethalColor = new Color(1f, 0.5f, 0.1f);

		private static readonly Color DangerColor = new Color(1f, 0.2f, 0.2f);

		internal static void LateUpdatePostfix(EnemyHud __instance)
		{
			if ((Object)(object)Player.m_localPlayer == (Object)null)
			{
				return;
			}
			FieldRef<EnemyHud, Dictionary<Character, HudData>>? hudsRef = EnemyHudFieldRefs.HudsRef;
			Dictionary<Character, HudData> dictionary = ((hudsRef != null) ? hudsRef.Invoke(__instance) : null);
			if (dictionary == null || dictionary.Count == 0)
			{
				return;
			}
			foreach (KeyValuePair<Character, HudData> item in dictionary)
			{
				Character key = item.Key;
				HudData value = item.Value;
				if (IsValidHud(key, value) && ValheimForesightPlugin.TryGetThreatAssessment(key, out ThreatAssessment assessment) && assessment != null)
				{
					ApplyThreatVisualization(value, assessment);
				}
			}
		}

		private static bool IsValidHud(Character? character, HudData? hud)
		{
			if ((Object)(object)character != (Object)null && hud != null)
			{
				return (Object)(object)hud.m_name != (Object)null;
			}
			return false;
		}

		private static void ApplyThreatVisualization(HudData hud, ThreatAssessment assessment)
		{
			ColorizeByThreatLevel(hud, assessment.Level);
			if (ValheimForesightPlugin.InstanceDebugHudEnabled)
			{
				AppendDebugInfo(hud, assessment);
			}
		}

		private static void ColorizeByThreatLevel(HudData hud, ThreatLevel level)
		{
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: 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)
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: 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)
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_005b: 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_0059: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)hud?.m_name == (Object)null))
			{
				TextMeshProUGUI name = hud.m_name;
				((Graphic)name).color = (Color)(level switch
				{
					ThreatLevel.Safe => SafeColor, 
					ThreatLevel.Caution => CautionColor, 
					ThreatLevel.BlockLethal => BlockLethalColor, 
					ThreatLevel.Danger => DangerColor, 
					_ => SafeColor, 
				});
			}
		}

		private static void AppendDebugInfo(HudData hud, ThreatAssessment assessment)
		{
			string text = (assessment.UsedRangedAttack ? "R" : "M");
			string threatLevelCode = GetThreatLevelCode(assessment.Level);
			string text2 = " [" + threatLevelCode + "-" + text + " " + $"r={assessment.DamageToHealthRatio:F2} " + $"raw={assessment.DamageInfo.RawDamage:F1} " + $"eff={assessment.DamageInfo.EffectiveDamageWithBlock:F1}]";
			TextMeshProUGUI name = hud.m_name;
			((TMP_Text)name).text = ((TMP_Text)name).text + text2;
		}

		private static string GetThreatLevelCode(ThreatLevel level)
		{
			return level switch
			{
				ThreatLevel.Safe => "SAFE", 
				ThreatLevel.Caution => "CAUT", 
				ThreatLevel.BlockLethal => "BLCK", 
				ThreatLevel.Danger => "DNG", 
				_ => "UNK", 
			};
		}
	}
}
namespace Valheim.Foresight.Models
{
	public readonly struct DamageInfo
	{
		public float RawDamage { get; }

		public float EffectiveDamageWithBlock { get; }

		public float EffectiveDamageWithParry { get; }

		public DamageInfo(float rawDamage, float effectiveWithBlock, float effectiveWithParry)
		{
			RawDamage = rawDamage;
			EffectiveDamageWithBlock = effectiveWithBlock;
			EffectiveDamageWithParry = effectiveWithParry;
		}
	}
	public readonly struct PlayerDefenseStats
	{
		public float Health { get; }

		public float Armor { get; }

		public float BlockingSkillLevel { get; }

		public ItemData? Shield { get; }

		public PlayerDefenseStats(float health, float armor, float blockingSkillLevel, ItemData? shield)
		{
			Health = health;
			Armor = armor;
			BlockingSkillLevel = blockingSkillLevel;
			Shield = shield;
		}

		public static PlayerDefenseStats FromPlayer(Player player)
		{
			ItemData shield = PlayerMethodRefs.GetCurrentBlocker?.Invoke(player);
			return new PlayerDefenseStats(((Character)player).GetHealth(), ((Character)player).GetBodyArmor(), ((Character)player).GetSkillLevel((SkillType)6), shield);
		}
	}
	public sealed class ThreatAssessment
	{
		public ThreatLevel Level { get; }

		public DamageInfo DamageInfo { get; }

		public float DamageToHealthRatio { get; }

		public float MaxMeleeDamage { get; }

		public float MaxRangedDamage { get; }

		public bool UsedRangedAttack { get; }

		public ThreatAssessment(ThreatLevel level, DamageInfo damageInfo, float damageToHealthRatio, float maxMeleeDamage, float maxRangedDamage, bool usedRangedAttack)
		{
			Level = level;
			DamageInfo = damageInfo;
			DamageToHealthRatio = damageToHealthRatio;
			MaxMeleeDamage = maxMeleeDamage;
			MaxRangedDamage = maxRangedDamage;
			UsedRangedAttack = usedRangedAttack;
		}
	}
	public enum ThreatLevel
	{
		Safe,
		Caution,
		BlockLethal,
		Danger
	}
}
namespace Valheim.Foresight.HarmonyRefs
{
	public static class AttackFieldRefs
	{
		public static readonly FieldRef<Attack, ItemData>? WeaponRef;

		public static readonly FieldRef<Attack, AttackType>? AttackTypeRef;

		public static readonly FieldRef<Attack, GameObject>? ProjectilePrefabRef;

		public static readonly FieldRef<Attack, float>? DamageMultiplierRef;

		static AttackFieldRefs()
		{
			try
			{
				WeaponRef = AccessTools.FieldRefAccess<Attack, ItemData>("m_weapon");
			}
			catch (Exception arg)
			{
				ValheimForesightPlugin.Log?.LogError(string.Format("Failed to create FieldRef for Attack.{0}: {1}", "m_weapon", arg));
				WeaponRef = null;
			}
			try
			{
				AttackTypeRef = AccessTools.FieldRefAccess<Attack, AttackType>("m_attackType");
			}
			catch (Exception arg2)
			{
				ValheimForesightPlugin.Log?.LogError(string.Format("Failed to create FieldRef for Attack.{0}: {1}", "m_attackType", arg2));
				AttackTypeRef = null;
			}
			try
			{
				ProjectilePrefabRef = AccessTools.FieldRefAccess<Attack, GameObject>("m_attackProjectile");
			}
			catch (Exception arg3)
			{
				ValheimForesightPlugin.Log?.LogError(string.Format("Failed to create FieldRef for Attack.{0}: {1}", "m_attackProjectile", arg3));
				ProjectilePrefabRef = null;
			}
			try
			{
				DamageMultiplierRef = AccessTools.FieldRefAccess<Attack, float>("m_damageMultiplier");
			}
			catch (Exception arg4)
			{
				ValheimForesightPlugin.Log?.LogError(string.Format("Failed to create FieldRef for Attack.{0}: {1}", "m_damageMultiplier", arg4));
				DamageMultiplierRef = null;
			}
		}
	}
	public static class EnemyHudFieldRefs
	{
		public static readonly FieldRef<EnemyHud, Dictionary<Character, HudData>>? HudsRef;

		static EnemyHudFieldRefs()
		{
			try
			{
				HudsRef = AccessTools.FieldRefAccess<EnemyHud, Dictionary<Character, HudData>>("m_huds");
			}
			catch (Exception arg)
			{
				ValheimForesightPlugin.Log?.LogError(string.Format("Failed to create FieldRef for EnemyHud.{0}: {1}", "m_huds", arg));
				HudsRef = null;
			}
		}
	}
	public static class HumanoidFieldRefs
	{
		public static readonly FieldRef<Humanoid, Attack>? CurrentAttackRef;

		public static readonly FieldRef<Humanoid, ItemData>? RightItemRef;

		public static readonly FieldRef<Humanoid, ItemData>? LeftItemRef;

		static HumanoidFieldRefs()
		{
			try
			{
				CurrentAttackRef = AccessTools.FieldRefAccess<Humanoid, Attack>("m_currentAttack");
			}
			catch (Exception arg)
			{
				ValheimForesightPlugin.Log?.LogError(string.Format("Failed to create FieldRef for Humanoid.{0}: {1}", "m_currentAttack", arg));
				CurrentAttackRef = null;
			}
			try
			{
				RightItemRef = AccessTools.FieldRefAccess<Humanoid, ItemData>("m_rightItem");
			}
			catch (Exception arg2)
			{
				ValheimForesightPlugin.Log?.LogError(string.Format("Failed to create FieldRef for Humanoid.{0}: {1}", "m_rightItem", arg2));
				RightItemRef = null;
			}
			try
			{
				LeftItemRef = AccessTools.FieldRefAccess<Humanoid, ItemData>("m_leftItem");
			}
			catch (Exception arg3)
			{
				ValheimForesightPlugin.Log?.LogError(string.Format("Failed to create FieldRef for Humanoid.{0}: {1}", "m_leftItem", arg3));
				RightItemRef = null;
			}
		}
	}
	public static class HumanoidMethodRefs
	{
		public static readonly Func<Humanoid, ItemData?>? GetRightItem;

		public static readonly Func<Humanoid, ItemData?>? GetLeftItem;

		static HumanoidMethodRefs()
		{
			try
			{
				MethodInfo methodInfo = AccessTools.Method(typeof(Humanoid), "GetRightItem", (Type[])null, (Type[])null);
				if (methodInfo != null)
				{
					GetRightItem = (Func<Humanoid, ItemData>)Delegate.CreateDelegate(typeof(Func<Humanoid, ItemData>), methodInfo);
				}
			}
			catch (Exception arg)
			{
				ValheimForesightPlugin.Log?.LogError($"Failed to bind Humanoid.GetRightItem: {arg}");
				GetRightItem = null;
			}
			try
			{
				MethodInfo methodInfo2 = AccessTools.Method(typeof(Humanoid), "GetLeftItem", (Type[])null, (Type[])null);
				if (methodInfo2 != null)
				{
					GetLeftItem = (Func<Humanoid, ItemData>)Delegate.CreateDelegate(typeof(Func<Humanoid, ItemData>), methodInfo2);
				}
			}
			catch (Exception arg2)
			{
				ValheimForesightPlugin.Log?.LogError($"Failed to bind Humanoid.GetLeftItem: {arg2}");
				GetLeftItem = null;
			}
		}
	}
	public class PlayerMethodRefs
	{
		public static readonly Func<Player, ItemData?>? GetCurrentBlocker;

		static PlayerMethodRefs()
		{
			try
			{
				MethodInfo methodInfo = AccessTools.Method(typeof(Player), "GetCurrentBlocker", (Type[])null, (Type[])null);
				if (methodInfo != null)
				{
					GetCurrentBlocker = (Func<Player, ItemData>)Delegate.CreateDelegate(typeof(Func<Player, ItemData>), methodInfo);
				}
			}
			catch (Exception arg)
			{
				ValheimForesightPlugin.Log?.LogError($"Failed to bind Player.GetCurrentBlocker: {arg}");
				GetCurrentBlocker = null;
			}
		}
	}
}
namespace Valheim.Foresight.Core
{
	public sealed class ForesightLogger : ILogger
	{
		private readonly ManualLogSource _logSource;

		public bool IsLogsEnabled { get; set; }

		public bool IsDebugLogsEnabled { get; set; }

		public ForesightLogger(ManualLogSource logSource)
		{
			_logSource = logSource ?? throw new ArgumentNullException("logSource");
		}

		public void LogFatal(object data)
		{
			if (IsLogsEnabled)
			{
				_logSource.LogFatal(data);
			}
		}

		public void LogError(object data)
		{
			if (IsLogsEnabled)
			{
				_logSource.LogError(data);
			}
		}

		public void LogWarning(object data)
		{
			if (IsLogsEnabled)
			{
				_logSource.LogWarning(data);
			}
		}

		public void LogMessage(object data)
		{
			if (IsLogsEnabled)
			{
				_logSource.LogMessage(data);
			}
		}

		public void LogInfo(object data)
		{
			if (IsLogsEnabled)
			{
				_logSource.LogInfo(data);
			}
		}

		public void LogDebug(object data)
		{
			if (IsLogsEnabled && IsDebugLogsEnabled)
			{
				_logSource.LogDebug(data);
			}
		}
	}
	public interface ILogger
	{
		bool IsLogsEnabled { get; set; }

		bool IsDebugLogsEnabled { get; set; }

		void LogFatal(object data);

		void LogError(object data);

		void LogWarning(object data);

		void LogMessage(object data);

		void LogInfo(object data);

		void LogDebug(object data);
	}
}
namespace Valheim.Foresight.Configuration
{
	public sealed class ForesightConfiguration
	{
		private readonly ConfigFile _configFile;

		public ConfigEntry<bool> IsLogsEnabled { get; }

		public ConfigEntry<bool> DebugHudEnabled { get; }

		public event EventHandler? SettingsChanged;

		public ForesightConfiguration(ConfigFile configFile)
		{
			_configFile = configFile ?? throw new ArgumentNullException("configFile");
			IsLogsEnabled = _configFile.Bind<bool>("Debug", "IsLogsEnabled", true, "Enable verbose logging for ValheimForesight (for debugging).");
			DebugHudEnabled = _configFile.Bind<bool>("HUD", "DebugHudEnabled", false, "Show debug threat info near enemy name.");
			BindConfigChangeHandlers();
		}

		private void BindConfigChangeHandlers()
		{
			IsLogsEnabled.SettingChanged += OnSettingChanged;
			DebugHudEnabled.SettingChanged += OnSettingChanged;
		}

		private void OnSettingChanged(object? sender, EventArgs e)
		{
			this.SettingsChanged?.Invoke(this, EventArgs.Empty);
		}
	}
}