Decompiled source of Valheim Foresight v2.0.1

BepInEx/plugins/Valheim.Foresight.dll

Decompiled 2 weeks ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
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.Components;
using Valheim.Foresight.Configuration;
using Valheim.Foresight.Core;
using Valheim.Foresight.HarmonyRefs;
using Valheim.Foresight.Models;
using Valheim.Foresight.Patches;
using Valheim.Foresight.Services.Castbar;
using Valheim.Foresight.Services.Castbar.Interfaces;
using Valheim.Foresight.Services.Combat;
using Valheim.Foresight.Services.Combat.Interfaces;
using Valheim.Foresight.Services.Combat.Wrappers;
using Valheim.Foresight.Services.Damage;
using Valheim.Foresight.Services.Hud;
using Valheim.Foresight.Services.Hud.Interfaces;
using YamlDotNet.Serialization;
using YamlDotNet.Serialization.NamingConventions;

[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("2.0.1")]
[assembly: AssemblyInformationalVersion("2.0.1")]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("2.0.1.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;
		}
	}
}
public sealed class AttackKeyComparer : IEqualityComparer<AttackKey>
{
	public static readonly AttackKeyComparer Instance = new AttackKeyComparer();

	public bool Equals(AttackKey x, AttackKey y)
	{
		if (string.Equals(x.CreaturePrefab, y.CreaturePrefab, StringComparison.OrdinalIgnoreCase))
		{
			return string.Equals(x.AttackAnimation, y.AttackAnimation, StringComparison.OrdinalIgnoreCase);
		}
		return false;
	}

	public int GetHashCode(AttackKey obj)
	{
		return HashCode.Combine<int, int>(StringComparer.OrdinalIgnoreCase.GetHashCode(obj.CreaturePrefab), StringComparer.OrdinalIgnoreCase.GetHashCode(obj.AttackAnimation));
	}
}
namespace Valheim.Foresight
{
	[BepInPlugin("coffeenova.valheim.foresight", "Valheim Foresight", "2.0.1")]
	public sealed class ValheimForesightPlugin : BaseUnityPlugin
	{
		internal static ILogger Log;

		private static IThreatIconSpriteProvider? _spriteProvider;

		private static AttackTimingEditorManager? _editorManager;

		private static ValheimForesightPlugin? _instance;

		private static Harmony? _harmony;

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

		private ForesightConfiguration _config;

		private AttackOverridesConfig _attackConfig;

		private IThreatCalculationService _threatService;

		private IDifficultyMultiplierCalculator _difficultyCalculator;

		private IThreatResponseHintService _threatResponseHintService;

		private IThreatHudIconRenderer? _hudIconRenderer;

		private float _playerLogTimer;

		private float _enemyUpdateTimer;

		private bool _isPluginActive;

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

		public static IForesightConfiguration? ForesightConfig => _instance?._config;

		public static IAttackOverridesConfig? AttackOverridesConfig => _instance?._attackConfig;

		public static IActiveAttackTracker? ActiveAttackTracker { get; private set; }

		public static IUnityCastbarRenderer? CastbarRenderer { get; private set; }

		public static IAttackTimingService? AttackTimingService { get; private set; }

		internal static IThreatResponseHintService ThreatResponseHintService => _instance?._threatResponseHintService ?? throw new InvalidOperationException("ThreatResponseHintService not initialized.");

		internal static IThreatHudIconRenderer? HudIconRenderer => _instance?._hudIconRenderer;

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

		private void Awake()
		{
			_instance = this;
			_isPluginActive = true;
			InitializeServices();
			ApplyHarmonyPatches();
			LogDifficultySettings();
			string[] manifestResourceNames = typeof(ValheimForesightPlugin).Assembly.GetManifestResourceNames();
			foreach (string text in manifestResourceNames)
			{
				Log.LogDebug("Embedded: " + text);
			}
			Log.LogInfo("Valheim Foresight 2.0.1 loaded");
		}

		private void OnDestroy()
		{
			Log.LogInfo("Valheim Foresight 2.0.1 unloading...");
			_config.SettingsChanged -= OnConfigurationChanged;
			DisablePlugin();
			if ((Object)(object)_instance == (Object)(object)this)
			{
				_instance = null;
			}
			Log.LogInfo("Valheim Foresight 2.0.1 unloaded");
			Log = null;
		}

		private void InitializeServices()
		{
			_config = new ForesightConfiguration(((BaseUnityPlugin)this).Config);
			_config.SettingsChanged += OnConfigurationChanged;
			_attackConfig = new AttackOverridesConfig(((BaseUnityPlugin)this).Config);
			Log = new ForesightLogger(((BaseUnityPlugin)this).Logger)
			{
				IsLogsEnabled = _config.IsLogsEnabled.Value,
				IsDebugLogsEnabled = _config.DebugEnabled.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);
			_threatResponseHintService = new ThreatResponseHintService();
			_spriteProvider = new EmbeddedPngSpriteProvider(new AssemblyEmbeddedResourceStreamProvider(typeof(ValheimForesightPlugin).Assembly), Log);
			_hudIconRenderer = new UnityThreatHudIconRenderer(_spriteProvider);
			ActiveAttackTracker = new ActiveAttackTracker();
			ParryWindowService parryWindowService = new ParryWindowService(_config);
			CastbarRenderer = new UnityCastbarRenderer(Log, _config, parryWindowService);
			AttackTimingService = new AttackTimingService(Log, _config, _attackConfig);
			InitializeEditorUI();
		}

		private void InitializeEditorUI()
		{
			//IL_0058: 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 O, but got Unknown
			//IL_008c: Unknown result type (might be due to invalid IL or missing references)
			//IL_009c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			if (AttackTimingService == null)
			{
				Log.LogError("AttackTimingService not initialized");
				return;
			}
			string value = _config.TimingEditorToggleKey.Value;
			if (!Enum.TryParse<KeyCode>(value, ignoreCase: true, out KeyCode result))
			{
				Log.LogWarning("Invalid key '" + value + "', using default F7");
				result = (KeyCode)288;
			}
			GameObject val = new GameObject("AttackTimingEditorManager");
			Object.DontDestroyOnLoad((Object)val);
			_editorManager = val.AddComponent<AttackTimingEditorManager>();
			_editorManager.Initialize(Log, AttackTimingService, (IAttackTimingDataProvider)AttackTimingService, _config, result);
			Log.LogInfo($"Attack Timing Editor UI initialized ({result} to toggle, Global Learning: {_config.AttackTimingLearningEnabled.Value})");
		}

		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_007d: Unknown result type (might be due to invalid IL or missing references)
			//IL_008a: Expected O, but got Unknown
			//IL_0101: Unknown result type (might be due to invalid IL or missing references)
			//IL_010e: Expected O, but got Unknown
			Harmony val = new Harmony("coffeenova.valheim.foresight");
			val.PatchAll();
			_harmony = val;
			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");
			}
			else if (methodInfo2 == null)
			{
				Log.LogError("Failed to find EnemyHudPatch.LateUpdatePostfix");
			}
			else
			{
				val.Patch((MethodBase)methodInfo, (HarmonyMethod)null, new HarmonyMethod(methodInfo2), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				Log.LogInfo("Patched EnemyHud.LateUpdate");
			}
			MethodInfo methodInfo3 = AccessTools.Method(typeof(Character), "CustomFixedUpdate", (Type[])null, (Type[])null);
			MethodInfo methodInfo4 = AccessTools.Method(typeof(CharacterFixedUpdatePatch), "Postfix", (Type[])null, (Type[])null);
			if (methodInfo3 == null)
			{
				Log.LogError("Failed to find Character.CustomFixedUpdate");
				return;
			}
			if (methodInfo4 == null)
			{
				Log.LogError("Failed to find CharacterFixedUpdatePatch.Postfix");
				return;
			}
			val.Patch((MethodBase)methodInfo3, (HarmonyMethod)null, new HarmonyMethod(methodInfo4), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			Log.LogInfo("Patched Character.CustomFixedUpdate");
		}

		private void Update()
		{
			if (_config.PluginEnabled.Value != _isPluginActive)
			{
				_isPluginActive = _config.PluginEnabled.Value;
				if (_isPluginActive)
				{
					EnablePlugin();
				}
				else
				{
					DisablePlugin();
				}
			}
			if (_isPluginActive)
			{
				_playerLogTimer += Time.deltaTime;
				_enemyUpdateTimer += Time.deltaTime;
				_enemyUpdateTimer += Time.deltaTime;
				if (_playerLogTimer >= 1f)
				{
					_playerLogTimer = 0f;
					LogPlayerHealth();
				}
				if (_enemyUpdateTimer >= 1f)
				{
					_enemyUpdateTimer = 0f;
					UpdateNearbyEnemiesThreat();
				}
				CleanupThreatCache();
				AttackTimingService?.Update();
			}
		}

		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 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("[LogDifficultySettings]ZoneSystem not ready yet");
				return;
			}
			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.DebugEnabled.Value;
		}

		private void DisablePlugin()
		{
			Log.LogInfo("Valheim Foresight disabling...");
			_threatCache.Clear();
			CastbarRenderer?.Dispose();
			CastbarRenderer = null;
			_hudIconRenderer?.Dispose();
			_hudIconRenderer = null;
			_spriteProvider?.Dispose();
			_spriteProvider = null;
			AttackTimingService?.Dispose();
			AttackTimingService = null;
			if ((Object)(object)_editorManager != (Object)null)
			{
				Object.Destroy((Object)(object)((Component)_editorManager).gameObject);
				_editorManager = null;
			}
			Harmony? harmony = _harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
			_harmony = null;
			Log.LogInfo("Valheim Foresight disabled");
		}

		private void EnablePlugin()
		{
			Log.LogInfo("Valheim Foresight enabling...");
			InitializeServices();
			ApplyHarmonyPatches();
			Log.LogInfo("Valheim Foresight enabled");
		}
	}
}
namespace Valheim.Foresight.Autogen
{
	internal static class PluginInfoGenerated
	{
		internal const string PluginVersion = "2.0.1";

		internal const string PluginName = "Valheim Foresight";

		internal const string PluginGuid = "coffeenova.valheim.foresight";
	}
}
namespace Valheim.Foresight.Services.Hud
{
	public sealed class AssemblyEmbeddedResourceStreamProvider : IEmbeddedResourceStreamProvider
	{
		private readonly Assembly _asm;

		public AssemblyEmbeddedResourceStreamProvider(Assembly asm)
		{
			_asm = asm;
		}

		public Stream? Open(string resourceName)
		{
			return _asm.GetManifestResourceStream(resourceName);
		}

		public string[] GetNames()
		{
			return _asm.GetManifestResourceNames();
		}
	}
	public sealed class EmbeddedPngSpriteProvider : IThreatIconSpriteProvider, IDisposable
	{
		private readonly IEmbeddedResourceStreamProvider _resources;

		private readonly ILogger _log;

		private readonly Dictionary<ThreatResponseHint, Sprite?> _cache = new Dictionary<ThreatResponseHint, Sprite>();

		private const string BlockRes = "Valheim.Foresight.Assets.Icons.block_icon.png";

		private const string ParryRes = "Valheim.Foresight.Assets.Icons.parry_icon.png";

		private const string DodgeRes = "Valheim.Foresight.Assets.Icons.roll_icon.png";

		private readonly List<Sprite> _createdSprites = new List<Sprite>();

		private readonly List<Texture2D> _createdTextures = new List<Texture2D>();

		public EmbeddedPngSpriteProvider(IEmbeddedResourceStreamProvider resources, ILogger log)
		{
			_resources = resources;
			_log = log;
			_cache[ThreatResponseHint.Block] = Load("Valheim.Foresight.Assets.Icons.block_icon.png");
			_cache[ThreatResponseHint.Parry] = Load("Valheim.Foresight.Assets.Icons.parry_icon.png");
			_cache[ThreatResponseHint.Dodge] = Load("Valheim.Foresight.Assets.Icons.roll_icon.png");
			_cache[ThreatResponseHint.None] = null;
		}

		public Sprite? GetIcon(ThreatResponseHint hint)
		{
			if (!_cache.TryGetValue(hint, out Sprite value))
			{
				return null;
			}
			return value;
		}

		private Sprite? Load(string resourceName)
		{
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_004c: Expected O, but got Unknown
			//IL_0078: Unknown result type (might be due to invalid IL or missing references)
			//IL_007d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0080: Unknown result type (might be due to invalid IL or missing references)
			//IL_008c: Unknown result type (might be due to invalid IL or missing references)
			using Stream stream = _resources.Open(resourceName);
			if (stream == null)
			{
				_log.LogWarning("Embedded sprite not found: " + resourceName);
				return null;
			}
			using MemoryStream memoryStream = new MemoryStream();
			stream.CopyTo(memoryStream);
			byte[] array = memoryStream.ToArray();
			Texture2D val = new Texture2D(2, 2, (TextureFormat)4, false);
			ImageConversion.LoadImage(val, array);
			_createdTextures.Add(val);
			Rect val2 = new Rect(0f, 0f, (float)((Texture)val).width, (float)((Texture)val).height);
			Sprite val3 = Sprite.Create(val, val2, new Vector2(0.5f, 0.5f), 100f);
			_createdSprites.Add(val3);
			return val3;
		}

		public void Dispose()
		{
			foreach (Sprite createdSprite in _createdSprites)
			{
				if ((Object)(object)createdSprite != (Object)null)
				{
					Object.Destroy((Object)(object)createdSprite);
				}
			}
			_createdSprites.Clear();
			foreach (Texture2D createdTexture in _createdTextures)
			{
				if ((Object)(object)createdTexture != (Object)null)
				{
					Object.Destroy((Object)(object)createdTexture);
				}
			}
			_createdTextures.Clear();
		}
	}
	public static class AttackNameFormatter
	{
		private static readonly HashSet<string> ForbiddenWords = new HashSet<string>(StringComparer.OrdinalIgnoreCase) { "idle" };

		public static string GetDisplayName(Attack? attack, string? animationName)
		{
			return FormatAttackName(animationName) ?? FormatAttackName(attack?.m_attackAnimation) ?? "Attack";
		}

		private static string? FormatAttackName(string? rawName)
		{
			if (string.IsNullOrWhiteSpace(rawName))
			{
				return null;
			}
			string sourceName = rawName.Trim();
			if (ForbiddenWords.Any((string word) => sourceName.Contains(word, StringComparison.OrdinalIgnoreCase)))
			{
				return null;
			}
			string text = sourceName.Replace("attack", "Attack").Replace("_", " ").Trim()
				.ToLowerInvariant();
			if (string.IsNullOrEmpty(text))
			{
				return null;
			}
			string text2 = string.Concat(text.Where((char c) => !char.IsDigit(c))).Trim();
			if (string.IsNullOrEmpty(text2))
			{
				return null;
			}
			return text2;
		}
	}
	public sealed class UnityCastbarRenderer : IUnityCastbarRenderer, IDisposable
	{
		private struct CastbarComponents
		{
			public Image? FillImage { get; set; }

			public GameObject? ParryIndicator { get; set; }

			public RectTransform? ParryIndicatorRect { get; set; }

			public Image? ParryIndicatorImage { get; set; }

			public TextMeshProUGUI? AttackNameText { get; set; }

			public TextMeshProUGUI? TimerText { get; set; }
		}

		private const string CastbarObjectName = "Foresight_Castbar";

		private const string FillName = "Castbar_Fill";

		private const string ParryIndicatorName = "Castbar_ParryIndicator";

		private const string AttackNameTextName = "Castbar_AttackName";

		private const string TimerTextName = "Castbar_Timer";

		private const float BorderThickness = 3f;

		private const float OffestLevelFactorY = -11f;

		private TMP_FontAsset? _cachedFont;

		private readonly List<Texture2D> _createdTextures = new List<Texture2D>();

		private readonly List<Sprite> _createdSprites = new List<Sprite>();

		private readonly List<GameObject>? _createdCastbars = new List<GameObject>();

		private Sprite? _cachedParryActiveSprite;

		private Sprite? _cachedParryIndicatorSprite;

		private readonly ILogger _logger;

		private readonly IForesightConfiguration _config;

		private readonly IParryWindowService _parryWindowService;

		public UnityCastbarRenderer(ILogger logger, IForesightConfiguration config, IParryWindowService parryWindowService)
		{
			_logger = logger;
			_config = config ?? throw new ArgumentNullException("config");
			_parryWindowService = parryWindowService ?? throw new ArgumentNullException("parryWindowService");
			_config.SettingsChanged += OnConfigurationChanged;
		}

		public void RenderCastbar(Transform hudParent, ActiveAttackInfo? attackInfo, Character? character = null)
		{
			IForesightConfiguration foresightConfig = ValheimForesightPlugin.ForesightConfig;
			if (foresightConfig == null || !foresightConfig.AttackCastbarEnabled.Value)
			{
				Transform val = hudParent.Find("Foresight_Castbar");
				if (val != null)
				{
					((Component)val).gameObject.SetActive(false);
				}
				return;
			}
			bool flag = foresightConfig.DebugEnabled.Value || foresightConfig.AlwaysDisplayCastbar.Value || (attackInfo != null && !attackInfo.IsExpired);
			bool isBoss = (Object)(object)character != (Object)null && character.IsBoss();
			GameObject orCreateCastbarObject = GetOrCreateCastbarObject(hudParent, isBoss, character);
			orCreateCastbarObject.SetActive(flag);
			if (flag)
			{
				UpdateCastbarSize(orCreateCastbarObject, isBoss, character);
				UpdateCastbarProgress(orCreateCastbarObject, attackInfo);
			}
		}

		private GameObject GetOrCreateCastbarObject(Transform hudParent, bool isBoss, Character? character = null)
		{
			Transform val = hudParent.Find("Foresight_Castbar");
			if (val != null)
			{
				return ((Component)val).gameObject;
			}
			return CreateCastbarObject(hudParent, isBoss, character);
		}

		private GameObject CreateCastbarObject(Transform hudParent, bool isBoss, Character? character = null)
		{
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Expected O, but got Unknown
			//IL_0030: 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_005a: Unknown result type (might be due to invalid IL or missing references)
			//IL_011f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0134: 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_00a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e7: Unknown result type (might be due to invalid IL or missing references)
			//IL_0108: Unknown result type (might be due to invalid IL or missing references)
			IForesightConfiguration foresightConfig = ValheimForesightPlugin.ForesightConfig;
			GameObject val = new GameObject("Foresight_Castbar");
			val.transform.SetParent(hudParent, false);
			RectTransform val2 = val.AddComponent<RectTransform>();
			val2.anchorMin = new Vector2(0.5f, 0f);
			val2.anchorMax = new Vector2(0.5f, 0f);
			val2.pivot = new Vector2(0.5f, 1f);
			if (foresightConfig != null)
			{
				if (isBoss)
				{
					val2.anchoredPosition = new Vector2(foresightConfig.BossCastbarOffsetX.Value, foresightConfig.BossCastbarOffsetY.Value);
					val2.sizeDelta = new Vector2(foresightConfig.BossCastbarWidth.Value, foresightConfig.BossCastbarHeight.Value);
				}
				else
				{
					float num = foresightConfig.AttackCastbarOffsetY.Value;
					if ((Object)(object)character != (Object)null && character.GetLevel() > 1)
					{
						num += -11f;
					}
					val2.anchoredPosition = new Vector2(foresightConfig.AttackCastbarOffsetX.Value, num);
					val2.sizeDelta = new Vector2(foresightConfig.AttackCastbarWidth.Value, foresightConfig.AttackCastbarHeight.Value);
				}
			}
			else
			{
				val2.anchoredPosition = new Vector2(0f, -10f);
				val2.sizeDelta = new Vector2(100f, 16f);
			}
			CreateBackground(val.transform);
			CreateFill(val.transform);
			CreateParryIndicator(val.transform);
			CreateAttackNameText(val.transform);
			CreateTimerText(val.transform);
			_createdCastbars.Add(val);
			return val;
		}

		private void CreateFill(Transform parent)
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: 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_0052: 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_009a: 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_00b9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bc: 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)
			GameObject val = new GameObject("Castbar_Fill");
			val.transform.SetParent(parent, false);
			RectTransform obj = val.AddComponent<RectTransform>();
			obj.anchorMin = new Vector2(0f, 0f);
			obj.anchorMax = new Vector2(0f, 1f);
			obj.pivot = new Vector2(0f, 0.5f);
			obj.anchoredPosition = new Vector2(3f, 0f);
			float num = (ValheimForesightPlugin.ForesightConfig?.AttackCastbarWidth.Value ?? 100f) - 6f;
			float num2 = -6f;
			obj.sizeDelta = new Vector2(num, num2);
			Image obj2 = val.AddComponent<Image>();
			Color value = _config.CastbarFillColor.Value;
			obj2.sprite = CreateGradientSprite(value);
			((Graphic)obj2).color = Color.white;
			obj2.type = (Type)3;
			obj2.fillMethod = (FillMethod)0;
			obj2.fillOrigin = 0;
			obj2.fillAmount = 0f;
			((Graphic)obj2).raycastTarget = false;
		}

		private Sprite CreateWhiteSprite()
		{
			//IL_0004: Unknown result type (might be due to invalid IL or missing references)
			//IL_000a: Expected O, but got Unknown
			//IL_000d: 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_004d: Unknown result type (might be due to invalid IL or missing references)
			Texture2D val = new Texture2D(1, 1, (TextureFormat)4, false);
			val.SetPixel(0, 0, Color.white);
			val.Apply();
			_createdTextures.Add(val);
			Sprite val2 = Sprite.Create(val, new Rect(0f, 0f, 1f, 1f), new Vector2(0.5f, 0.5f), 1f);
			_createdSprites.Add(val2);
			return val2;
		}

		private Sprite CreateGradientSprite(Color baseColor)
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Expected O, but got Unknown
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: 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_0047: 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_008c: Unknown result type (might be due to invalid IL or missing references)
			//IL_009b: Unknown result type (might be due to invalid IL or missing references)
			Texture2D val = new Texture2D(1, 16, (TextureFormat)4, false);
			Color val2 = default(Color);
			for (int i = 0; i < 16; i++)
			{
				float num = (float)i / 15f;
				float num2 = Mathf.Lerp(0.65f, 1f, num);
				((Color)(ref val2))..ctor(baseColor.r * num2, baseColor.g * num2, baseColor.b * num2, baseColor.a);
				val.SetPixel(0, i, val2);
			}
			val.Apply();
			_createdTextures.Add(val);
			Sprite val3 = Sprite.Create(val, new Rect(0f, 0f, 1f, 16f), new Vector2(0.5f, 0.5f), 1f);
			_createdSprites.Add(val3);
			return val3;
		}

		private Sprite CreateHollowBorderSprite()
		{
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Expected O, but got Unknown
			//IL_001a: 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_00af: Unknown result type (might be due to invalid IL or missing references)
			//IL_00be: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d2: Unknown result type (might be due to invalid IL or missing references)
			//IL_0072: Unknown result type (might be due to invalid IL or missing references)
			//IL_006e: Unknown result type (might be due to invalid IL or missing references)
			int num = 16;
			int num2 = 1;
			Texture2D val = new Texture2D(num, num, (TextureFormat)4, false);
			Color value = _config.CastbarBorderColor.Value;
			Color val2 = default(Color);
			((Color)(ref val2))..ctor(0f, 0f, 0f, 0f);
			for (int i = 0; i < num; i++)
			{
				for (int j = 0; j < num; j++)
				{
					bool flag = i < num2 || i >= num - num2 || j < num2 || j >= num - num2;
					val.SetPixel(i, j, flag ? value : val2);
				}
			}
			val.Apply();
			_createdTextures.Add(val);
			Sprite val3 = Sprite.Create(val, new Rect(0f, 0f, (float)num, (float)num), new Vector2(0.5f, 0.5f), 1f, 0u, (SpriteMeshType)0, new Vector4((float)num2, (float)num2, (float)num2, (float)num2));
			_createdSprites.Add(val3);
			return val3;
		}

		private void CreateBackground(Transform parent)
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: 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_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: 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_0070: Unknown result type (might be due to invalid IL or missing references)
			//IL_007d: 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_008f: 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_00b8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00de: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e3: 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)
			GameObject val = new GameObject("Castbar_Border");
			val.transform.SetParent(parent, false);
			RectTransform obj = val.AddComponent<RectTransform>();
			obj.anchorMin = Vector2.zero;
			obj.anchorMax = Vector2.one;
			obj.sizeDelta = Vector2.zero;
			Image obj2 = val.AddComponent<Image>();
			obj2.sprite = CreateHollowBorderSprite();
			((Graphic)obj2).color = Color.white;
			((Graphic)obj2).raycastTarget = false;
			obj2.type = (Type)1;
			GameObject val2 = new GameObject("Castbar_Background");
			val2.transform.SetParent(parent, false);
			RectTransform obj3 = val2.AddComponent<RectTransform>();
			obj3.anchorMin = Vector2.zero;
			obj3.anchorMax = Vector2.one;
			obj3.offsetMin = new Vector2(3f, 3f);
			obj3.offsetMax = new Vector2(-3f, -3f);
			Image obj4 = val2.AddComponent<Image>();
			obj4.sprite = CreateWhiteSprite();
			Color value = _config.CastbarBackgroundColor.Value;
			((Graphic)obj4).color = value;
			((Graphic)obj4).raycastTarget = false;
		}

		private void CreateParryIndicator(Transform parent)
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: 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_0034: 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_0049: 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_0064: Unknown result type (might be due to invalid IL or missing references)
			//IL_0069: 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_0078: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = new GameObject("Castbar_ParryIndicator");
			val.transform.SetParent(parent, false);
			RectTransform obj = val.AddComponent<RectTransform>();
			obj.anchorMin = Vector2.zero;
			obj.anchorMax = Vector2.one;
			obj.offsetMin = Vector2.zero;
			obj.offsetMax = Vector2.zero;
			obj.sizeDelta = Vector2.zero;
			Image obj2 = val.AddComponent<Image>();
			Color value = _config.CastbarParryIndicatorColor.Value;
			obj2.sprite = CreateGradientSprite(value);
			((Graphic)obj2).color = Color.white;
			((Graphic)obj2).raycastTarget = false;
			val.SetActive(false);
		}

		private void CreateAttackNameText(Transform parent)
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: 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_0048: 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_005c: 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_00b7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b9: 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_012b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0130: 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_0142: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = new GameObject("Castbar_AttackName");
			val.transform.SetParent(parent, false);
			RectTransform obj = val.AddComponent<RectTransform>();
			obj.anchorMin = new Vector2(0f, 0f);
			obj.anchorMax = new Vector2(0.6f, 1f);
			obj.sizeDelta = Vector2.zero;
			obj.anchoredPosition = Vector2.zero;
			TextMeshProUGUI val2 = val.AddComponent<TextMeshProUGUI>();
			TMP_FontAsset valheimFont = GetValheimFont();
			if (valheimFont != null)
			{
				((TMP_Text)val2).font = valheimFont;
			}
			((TMP_Text)val2).text = string.Empty;
			((TMP_Text)val2).fontSize = 12f;
			((TMP_Text)val2).fontSizeMin = 6f;
			((TMP_Text)val2).fontSizeMax = 14f;
			((TMP_Text)val2).enableAutoSizing = true;
			Color value = _config.CastbarTextColor.Value;
			((Graphic)val2).color = value;
			((TMP_Text)val2).alignment = (TextAlignmentOptions)4097;
			((TMP_Text)val2).margin = new Vector4(4f, 0f, 2f, 0f);
			((TMP_Text)val2).fontStyle = (FontStyles)0;
			((TMP_Text)val2).textWrappingMode = (TextWrappingModes)0;
			((TMP_Text)val2).overflowMode = (TextOverflowModes)1;
			((Graphic)val2).raycastTarget = false;
			((Behaviour)val2).enabled = _config.AttackCastbarTextEnabled.Value;
			Shadow obj2 = val.AddComponent<Shadow>();
			Color value2 = _config.CastbarTextShadowColor.Value;
			obj2.effectColor = value2;
			obj2.effectDistance = new Vector2(1f, -1f);
		}

		private void CreateTimerText(Transform parent)
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: 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_0048: 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_005c: 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_00b7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b9: 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_011d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0122: Unknown result type (might be due to invalid IL or missing references)
			//IL_0124: Unknown result type (might be due to invalid IL or missing references)
			//IL_0134: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = new GameObject("Castbar_Timer");
			val.transform.SetParent(parent, false);
			RectTransform obj = val.AddComponent<RectTransform>();
			obj.anchorMin = new Vector2(0.6f, 0f);
			obj.anchorMax = new Vector2(1f, 1f);
			obj.sizeDelta = Vector2.zero;
			obj.anchoredPosition = Vector2.zero;
			TextMeshProUGUI val2 = val.AddComponent<TextMeshProUGUI>();
			TMP_FontAsset valheimFont = GetValheimFont();
			if (valheimFont != null)
			{
				((TMP_Text)val2).font = valheimFont;
			}
			((TMP_Text)val2).text = string.Empty;
			((TMP_Text)val2).fontSize = 12f;
			((TMP_Text)val2).fontSizeMin = 6f;
			((TMP_Text)val2).fontSizeMax = 14f;
			((TMP_Text)val2).enableAutoSizing = true;
			Color value = _config.CastbarTextColor.Value;
			((Graphic)val2).color = value;
			((TMP_Text)val2).alignment = (TextAlignmentOptions)4100;
			((TMP_Text)val2).margin = new Vector4(2f, 0f, 4f, 0f);
			((TMP_Text)val2).fontStyle = (FontStyles)0;
			((Graphic)val2).raycastTarget = false;
			((Behaviour)val2).enabled = _config.AttackCastbarTextEnabled.Value;
			Shadow obj2 = val.AddComponent<Shadow>();
			Color value2 = _config.CastbarTextShadowColor.Value;
			obj2.effectColor = value2;
			obj2.effectDistance = new Vector2(1f, -1f);
		}

		private void UpdateCastbarSize(GameObject castbarObject, bool isBoss, Character? character = null)
		{
			//IL_0114: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d8: Unknown result type (might be due to invalid IL or missing references)
			float num = (isBoss ? _config.BossCastbarWidth.Value : _config.AttackCastbarWidth.Value);
			float num2 = (isBoss ? _config.BossCastbarHeight.Value : _config.AttackCastbarHeight.Value);
			RectTransform component = castbarObject.GetComponent<RectTransform>();
			if (component != null)
			{
				float num3 = (isBoss ? _config.BossCastbarOffsetX.Value : _config.AttackCastbarOffsetX.Value);
				float num4 = (isBoss ? _config.BossCastbarOffsetY.Value : _config.AttackCastbarOffsetY.Value);
				if (!isBoss && (Object)(object)character != (Object)null && character.GetLevel() > 1)
				{
					num4 += -11f;
				}
				component.anchoredPosition = new Vector2(num3, num4);
				component.sizeDelta = new Vector2(num, num2);
			}
			Transform val = castbarObject.transform.Find("Castbar_Fill");
			if (val != null)
			{
				RectTransform component2 = ((Component)val).GetComponent<RectTransform>();
				if (component2 != null)
				{
					float num5 = num - 6f;
					component2.sizeDelta = new Vector2(num5, -6f);
				}
			}
			UpdateTextSizes(castbarObject, num, num2);
		}

		private void UpdateTextSizes(GameObject castbarObject, float castbarWidth, float castbarHeight)
		{
			Transform val = castbarObject.transform.Find("Castbar_AttackName");
			Transform val2 = castbarObject.transform.Find("Castbar_Timer");
			float num = Mathf.Clamp(castbarHeight * 0.7f, 8f, 16f);
			float fontSizeMin = Mathf.Max(6f, num * 0.5f);
			float fontSizeMax = Mathf.Min(18f, num * 1.2f);
			if (castbarWidth < 80f)
			{
				num = Mathf.Clamp(castbarHeight * 0.5f, 6f, 10f);
				fontSizeMin = 6f;
				fontSizeMax = 10f;
			}
			if (val != null)
			{
				TextMeshProUGUI component = ((Component)val).GetComponent<TextMeshProUGUI>();
				if (component != null)
				{
					((TMP_Text)component).fontSize = num;
					((TMP_Text)component).fontSizeMin = fontSizeMin;
					((TMP_Text)component).fontSizeMax = fontSizeMax;
					if (castbarWidth < 50f)
					{
						((Behaviour)component).enabled = false;
					}
					else
					{
						((Behaviour)component).enabled = true;
					}
				}
			}
			if (val2 == null)
			{
				return;
			}
			TextMeshProUGUI component2 = ((Component)val2).GetComponent<TextMeshProUGUI>();
			if (component2 != null)
			{
				((TMP_Text)component2).fontSize = num;
				((TMP_Text)component2).fontSizeMin = fontSizeMin;
				((TMP_Text)component2).fontSizeMax = fontSizeMax;
				if (castbarWidth < 30f)
				{
					((Behaviour)component2).enabled = false;
				}
				else
				{
					((Behaviour)component2).enabled = true;
				}
			}
		}

		private void UpdateCastbarProgress(GameObject castbarObject, ActiveAttackInfo? attackInfo)
		{
			CastbarComponents castbarComponents = GetCastbarComponents(castbarObject);
			if (castbarComponents.FillImage != null)
			{
				if (attackInfo == null || attackInfo.IsExpired)
				{
					ResetCastbarDisplay(castbarComponents);
				}
				else
				{
					UpdateActiveAttackDisplay(castbarComponents, attackInfo);
				}
			}
		}

		private CastbarComponents GetCastbarComponents(GameObject castbarObject)
		{
			Transform val = castbarObject.transform.Find("Castbar_Fill");
			Transform val2 = castbarObject.transform.Find("Castbar_ParryIndicator");
			Transform val3 = castbarObject.transform.Find("Castbar_AttackName");
			Transform val4 = castbarObject.transform.Find("Castbar_Timer");
			CastbarComponents result = default(CastbarComponents);
			result.FillImage = ((val != null) ? ((Component)val).GetComponent<Image>() : null);
			result.ParryIndicator = ((val2 != null) ? ((Component)val2).gameObject : null);
			result.ParryIndicatorRect = ((val2 != null) ? ((Component)val2).GetComponent<RectTransform>() : null);
			result.ParryIndicatorImage = ((val2 != null) ? ((Component)val2).GetComponent<Image>() : null);
			result.AttackNameText = ((val3 != null) ? ((Component)val3).GetComponent<TextMeshProUGUI>() : null);
			result.TimerText = ((val4 != null) ? ((Component)val4).GetComponent<TextMeshProUGUI>() : null);
			return result;
		}

		private void ResetCastbarDisplay(CastbarComponents components)
		{
			if (components.FillImage != null)
			{
				components.FillImage.fillAmount = 0f;
			}
			if (components.ParryIndicator != null)
			{
				components.ParryIndicator.SetActive(false);
			}
			if (components.AttackNameText != null)
			{
				((TMP_Text)components.AttackNameText).text = string.Empty;
			}
			if (components.TimerText != null)
			{
				((TMP_Text)components.TimerText).text = string.Empty;
			}
		}

		private void UpdateActiveAttackDisplay(CastbarComponents components, ActiveAttackInfo attackInfo)
		{
			components.FillImage.fillAmount = attackInfo.Progress;
			UpdateAttackText(components, attackInfo);
			(float, float)? parryWindowInfo = _parryWindowService.GetParryWindowInfo(attackInfo, attackInfo.Duration);
			if (!parryWindowInfo.HasValue)
			{
				ApplyNoParryWindowStyle(components);
				return;
			}
			UpdateParryIndicatorPosition(components, parryWindowInfo.Value);
			ApplyParryWindowStyle(components, attackInfo);
		}

		private void UpdateAttackText(CastbarComponents components, ActiveAttackInfo attackInfo)
		{
			bool value = _config.AttackCastbarTextEnabled.Value;
			if (components.AttackNameText != null)
			{
				((TMP_Text)components.AttackNameText).text = (value ? attackInfo.AttackName : string.Empty);
				((Behaviour)components.AttackNameText).enabled = value;
			}
			if (components.TimerText != null)
			{
				((TMP_Text)components.TimerText).text = (value ? $"{attackInfo.TimeRemaining:F1}s" : string.Empty);
				((Behaviour)components.TimerText).enabled = value;
			}
		}

		private void ApplyNoParryWindowStyle(CastbarComponents components)
		{
			//IL_003b: 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_0026: 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_0067: Unknown result type (might be due to invalid IL or missing references)
			if (components.ParryIndicator != null)
			{
				components.ParryIndicator.SetActive(false);
			}
			if (components.FillImage != null)
			{
				((Graphic)components.FillImage).color = Color.white;
			}
			Color value = _config.CastbarTextColor.Value;
			if (components.AttackNameText != null)
			{
				((Graphic)components.AttackNameText).color = value;
			}
			if (components.TimerText != null)
			{
				((Graphic)components.TimerText).color = value;
			}
		}

		private void UpdateParryIndicatorPosition(CastbarComponents components, (float startPos, float windowWidth) parryWindowInfo)
		{
			//IL_0025: 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_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_0074: Unknown result type (might be due to invalid IL or missing references)
			if (components.ParryIndicatorRect != null)
			{
				var (num, num2) = parryWindowInfo;
				components.ParryIndicatorRect.anchorMin = new Vector2(num, 0f);
				components.ParryIndicatorRect.anchorMax = new Vector2(num + num2, 1f);
				components.ParryIndicatorRect.offsetMin = new Vector2(0f, 3f);
				components.ParryIndicatorRect.offsetMax = new Vector2(0f, -3f);
			}
		}

		private void ApplyParryWindowStyle(CastbarComponents components, ActiveAttackInfo attackInfo)
		{
			//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_001d: 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_005e: Unknown result type (might be due to invalid IL or missing references)
			bool isInParryWindow = _parryWindowService.IsInParryWindow(attackInfo);
			if (components.FillImage != null)
			{
				((Graphic)components.FillImage).color = Color.white;
			}
			Color value = _config.CastbarTextColor.Value;
			if (components.AttackNameText != null)
			{
				((Graphic)components.AttackNameText).color = value;
			}
			if (components.TimerText != null)
			{
				((Graphic)components.TimerText).color = value;
			}
			UpdateParryIndicatorVisual(components, isInParryWindow);
		}

		private void UpdateParryIndicatorVisual(CastbarComponents components, bool isInParryWindow)
		{
			//IL_0071: Unknown result type (might be due to invalid IL or missing references)
			//IL_0076: Unknown result type (might be due to invalid IL or missing references)
			//IL_0079: Unknown result type (might be due to invalid IL or missing references)
			//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)
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_009d: Unknown result type (might be due to invalid IL or missing references)
			if (components.ParryIndicator == null)
			{
				return;
			}
			components.ParryIndicator.SetActive(true);
			if (components.ParryIndicatorImage == null)
			{
				return;
			}
			if (isInParryWindow)
			{
				if (_cachedParryActiveSprite == null)
				{
					Color value = _config.CastbarParryActiveColor.Value;
					_cachedParryActiveSprite = CreateGradientSprite(value);
				}
				components.ParryIndicatorImage.sprite = _cachedParryActiveSprite;
			}
			else
			{
				if (_cachedParryIndicatorSprite == null)
				{
					Color value2 = _config.CastbarParryIndicatorColor.Value;
					_cachedParryIndicatorSprite = CreateGradientSprite(value2);
				}
				components.ParryIndicatorImage.sprite = _cachedParryIndicatorSprite;
			}
			((Graphic)components.ParryIndicatorImage).color = Color.white;
		}

		private TMP_FontAsset? GetValheimFont()
		{
			if (_cachedFont != null)
			{
				return _cachedFont;
			}
			try
			{
				EnemyHud instance = EnemyHud.instance;
				if (instance != null)
				{
					TextMeshProUGUI[] componentsInChildren = ((Component)instance).GetComponentsInChildren<TextMeshProUGUI>(true);
					if (componentsInChildren.Length != 0)
					{
						_cachedFont = ((TMP_Text)componentsInChildren[0]).font;
						ILogger log = ValheimForesightPlugin.Log;
						TMP_FontAsset? cachedFont = _cachedFont;
						log.LogInfo("Found and cached Valheim font: " + ((cachedFont != null) ? ((Object)cachedFont).name : null));
						return _cachedFont;
					}
				}
				TextMeshProUGUI[] array = Object.FindObjectsByType<TextMeshProUGUI>((FindObjectsSortMode)0);
				if (array.Length != 0)
				{
					_cachedFont = ((TMP_Text)array[0]).font;
					ILogger log2 = ValheimForesightPlugin.Log;
					TMP_FontAsset? cachedFont2 = _cachedFont;
					log2.LogInfo("Found and cached font from scene: " + ((cachedFont2 != null) ? ((Object)cachedFont2).name : null));
					return _cachedFont;
				}
				ValheimForesightPlugin.Log.LogWarning("Could not find Valheim font!");
			}
			catch (Exception ex)
			{
				ValheimForesightPlugin.Log.LogError("Error getting Valheim font: " + ex.Message);
			}
			return null;
		}

		private void OnConfigurationChanged(object? sender, EventArgs e)
		{
			InvalidateColorCache();
			UpdateExistingCastbarColors();
		}

		private void InvalidateColorCache()
		{
			if (_cachedParryActiveSprite != null)
			{
				_createdSprites.Remove(_cachedParryActiveSprite);
				Object.Destroy((Object)(object)_cachedParryActiveSprite);
				_cachedParryActiveSprite = null;
			}
			if (_cachedParryIndicatorSprite != null)
			{
				_createdSprites.Remove(_cachedParryIndicatorSprite);
				Object.Destroy((Object)(object)_cachedParryIndicatorSprite);
				_cachedParryIndicatorSprite = null;
			}
		}

		private void UpdateExistingCastbarColors()
		{
			//IL_0087: Unknown result type (might be due to invalid IL or missing references)
			//IL_008c: 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_009f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0138: Unknown result type (might be due to invalid IL or missing references)
			//IL_013d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0141: Unknown result type (might be due to invalid IL or missing references)
			//IL_0100: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ee: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f3: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f7: Unknown result type (might be due to invalid IL or missing references)
			//IL_019e: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b6: Unknown result type (might be due to invalid IL or missing references)
			//IL_0254: Unknown result type (might be due to invalid IL or missing references)
			//IL_0259: Unknown result type (might be due to invalid IL or missing references)
			//IL_025d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0216: Unknown result type (might be due to invalid IL or missing references)
			//IL_021b: Unknown result type (might be due to invalid IL or missing references)
			//IL_021f: Unknown result type (might be due to invalid IL or missing references)
			//IL_027c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0281: Unknown result type (might be due to invalid IL or missing references)
			//IL_0285: Unknown result type (might be due to invalid IL or missing references)
			if (_createdCastbars == null)
			{
				return;
			}
			foreach (GameObject createdCastbar in _createdCastbars)
			{
				if (createdCastbar == null || (Object)(object)createdCastbar == (Object)null)
				{
					continue;
				}
				Transform val = createdCastbar.transform.Find("Castbar_Fill");
				if (val != null)
				{
					Image component = ((Component)val).GetComponent<Image>();
					if (component != null)
					{
						if (component.sprite != null)
						{
							_createdSprites.Remove(component.sprite);
							Object.Destroy((Object)(object)component.sprite);
						}
						Color value = _config.CastbarFillColor.Value;
						component.sprite = CreateGradientSprite(value);
						((Graphic)component).color = Color.white;
					}
				}
				Transform val2 = createdCastbar.transform.Find("Castbar_Border");
				if (val2 != null)
				{
					Image component2 = ((Component)val2).GetComponent<Image>();
					if (component2 != null)
					{
						if (component2.sprite != null)
						{
							_createdSprites.Remove(component2.sprite);
							Object.Destroy((Object)(object)component2.sprite);
						}
						component2.sprite = CreateHollowBorderSprite();
						((Graphic)component2).color = Color.white;
					}
				}
				Transform val3 = createdCastbar.transform.Find("Castbar_Background");
				if (val3 != null)
				{
					Image component3 = ((Component)val3).GetComponent<Image>();
					if (component3 != null)
					{
						Color value2 = _config.CastbarBackgroundColor.Value;
						((Graphic)component3).color = value2;
					}
				}
				Transform val4 = createdCastbar.transform.Find("Castbar_ParryIndicator");
				if (val4 != null)
				{
					Image component4 = ((Component)val4).GetComponent<Image>();
					if (component4 != null)
					{
						if (component4.sprite != null)
						{
							_createdSprites.Remove(component4.sprite);
							Object.Destroy((Object)(object)component4.sprite);
						}
						Color value3 = _config.CastbarParryIndicatorColor.Value;
						component4.sprite = CreateGradientSprite(value3);
						((Graphic)component4).color = Color.white;
					}
				}
				Transform val5 = createdCastbar.transform.Find("Castbar_AttackName");
				if (val5 != null)
				{
					TextMeshProUGUI component5 = ((Component)val5).GetComponent<TextMeshProUGUI>();
					if (component5 != null)
					{
						Color value4 = _config.CastbarTextColor.Value;
						((Graphic)component5).color = value4;
					}
					Shadow component6 = ((Component)val5).GetComponent<Shadow>();
					if (component6 != null)
					{
						Color value5 = _config.CastbarTextShadowColor.Value;
						component6.effectColor = value5;
					}
				}
				Transform val6 = createdCastbar.transform.Find("Castbar_Timer");
				if (val6 != null)
				{
					TextMeshProUGUI component7 = ((Component)val6).GetComponent<TextMeshProUGUI>();
					if (component7 != null)
					{
						Color value6 = _config.CastbarTextColor.Value;
						((Graphic)component7).color = value6;
					}
					Shadow component8 = ((Component)val6).GetComponent<Shadow>();
					if (component8 != null)
					{
						Color value7 = _config.CastbarTextShadowColor.Value;
						component8.effectColor = value7;
					}
				}
			}
		}

		public void Dispose()
		{
			_config.SettingsChanged -= OnConfigurationChanged;
			CleanupCastbars();
			foreach (Sprite createdSprite in _createdSprites)
			{
				if (createdSprite != null)
				{
					Object.Destroy((Object)(object)createdSprite);
				}
			}
			_createdSprites.Clear();
			foreach (Texture2D createdTexture in _createdTextures)
			{
				if (createdTexture != null)
				{
					Object.Destroy((Object)(object)createdTexture);
				}
			}
			_createdTextures.Clear();
			_cachedFont = null;
			_cachedParryActiveSprite = null;
			_cachedParryIndicatorSprite = null;
		}

		private void CleanupCastbars()
		{
			try
			{
				if (_createdCastbars == null)
				{
					return;
				}
				foreach (GameObject createdCastbar in _createdCastbars)
				{
					if ((Object)(object)createdCastbar != (Object)null && !((object)createdCastbar).Equals((object?)null))
					{
						_logger?.LogDebug("[CleanupCastbars] Destroying castbar: " + ((Object)createdCastbar).name);
						Object.Destroy((Object)(object)createdCastbar);
					}
				}
				_logger?.LogInfo($"Cleaned up {_createdCastbars.Count} castbar(s)");
				_createdCastbars?.Clear();
			}
			catch (Exception ex)
			{
				_logger?.LogError("[CleanupCastbars] Error cleaning up castbars: " + ex.Message);
			}
		}
	}
	public sealed class UnityResourcesThreatIconSpriteProvider : IThreatIconSpriteProvider, IDisposable
	{
		private readonly IResourcesWrapper _resources;

		private readonly ILogger _logger;

		private readonly Dictionary<ThreatResponseHint, Sprite?> _cache = new Dictionary<ThreatResponseHint, Sprite>();

		private const string BlockResource = "Valheim.Foresight.Assets.Icons.block_icon.png";

		private const string ParryResource = "Valheim.Foresight.Assets.Icons.parry_icon.png";

		private const string DodgeResource = "Valheim.Foresight.Assets.Icons.roll_icon.png";

		public UnityResourcesThreatIconSpriteProvider(IResourcesWrapper resources, ILogger logger)
		{
			_resources = resources;
			_logger = logger;
			Preload();
		}

		private void Preload()
		{
			_cache[ThreatResponseHint.Block] = LoadSprite("Valheim.Foresight.Assets.Icons.block_icon.png");
			_cache[ThreatResponseHint.Parry] = LoadSprite("Valheim.Foresight.Assets.Icons.parry_icon.png");
			_cache[ThreatResponseHint.Dodge] = LoadSprite("Valheim.Foresight.Assets.Icons.roll_icon.png");
			_cache[ThreatResponseHint.None] = null;
			_logger.LogDebug("[Preload] Preload completed. " + $"Block={_cache[ThreatResponseHint.Block] != null}, " + $"Parry={_cache[ThreatResponseHint.Parry] != null}, " + $"Dodge={_cache[ThreatResponseHint.Dodge] != null}");
		}

		private Sprite? LoadSprite(string path)
		{
			Sprite? obj = _resources.LoadSprite(path);
			if ((Object)(object)obj == (Object)null)
			{
				_logger.LogWarning("Failed to load threat icon sprite at '" + path + "'.");
			}
			return obj;
		}

		public Sprite? GetIcon(ThreatResponseHint hint)
		{
			if (!_cache.TryGetValue(hint, out Sprite value))
			{
				_logger.LogWarning($"No cached sprite entry for hint={hint}.");
				return null;
			}
			_logger.LogDebug(string.Format("[{0}] {1}(hint={2}) -> spriteNull={3}", "GetIcon", "GetIcon", hint, value == null));
			return value;
		}

		public void Dispose()
		{
		}
	}
	public sealed class UnityThreatHudIconRenderer : IThreatHudIconRenderer, IDisposable
	{
		private const string IconObjectName = "Foresight_ThreatIcon";

		private readonly IThreatIconSpriteProvider _spriteProvider;

		private readonly List<GameObject> _createdIcons = new List<GameObject>();

		private bool _disposed;

		public UnityThreatHudIconRenderer(IThreatIconSpriteProvider spriteProvider)
		{
			_spriteProvider = spriteProvider;
		}

		public void RenderIcon(TextMeshProUGUI? nameLabel, ThreatResponseHint hint)
		{
			//IL_00ed: Unknown result type (might be due to invalid IL or missing references)
			//IL_010f: Unknown result type (might be due to invalid IL or missing references)
			if (nameLabel == null)
			{
				return;
			}
			IForesightConfiguration foresightConfig = ValheimForesightPlugin.ForesightConfig;
			if (foresightConfig == null || !foresightConfig.ThreatIconEnabled.Value)
			{
				GameObject existingIconObject = GetExistingIconObject(((TMP_Text)nameLabel).transform);
				if ((Object)(object)existingIconObject != (Object)null)
				{
					existingIconObject.SetActive(false);
				}
				return;
			}
			_ = ((TMP_Text)nameLabel).text;
			GameObject orCreateIconObject = GetOrCreateIconObject(((TMP_Text)nameLabel).transform);
			Image val = orCreateIconObject.GetComponent<Image>() ?? orCreateIconObject.AddComponent<Image>();
			Sprite icon = _spriteProvider.GetIcon(hint);
			bool flag = hint != 0 && icon != null;
			orCreateIconObject.SetActive(flag);
			if (!flag)
			{
				if (hint != 0 && icon == null)
				{
					ValheimForesightPlugin.Log?.LogWarning($"Sprite is null for hint={hint}, hiding icon. " + "Check ThreatIconSpriteProvider paths.");
				}
				return;
			}
			val.sprite = icon;
			val.preserveAspect = true;
			RectTransform component = orCreateIconObject.GetComponent<RectTransform>();
			if ((Object)(object)component != (Object)null)
			{
				component.anchoredPosition = new Vector2(foresightConfig.ThreatIconOffsetX.Value, foresightConfig.ThreatIconOffsetY.Value);
				component.sizeDelta = new Vector2(foresightConfig.ThreatIconSize.Value, foresightConfig.ThreatIconSize.Value);
			}
		}

		private GameObject? GetExistingIconObject(Transform nameTransform)
		{
			Transform obj = (nameTransform.parent ?? nameTransform).Find("Foresight_ThreatIcon");
			if (obj == null)
			{
				return null;
			}
			return ((Component)obj).gameObject;
		}

		private GameObject GetOrCreateIconObject(Transform nameTransform)
		{
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: Expected O, but got Unknown
			//IL_007a: Unknown result type (might be due to invalid IL or missing references)
			//IL_008f: 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_00d2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f5: Unknown result type (might be due to invalid IL or missing references)
			GameObject existingIconObject = GetExistingIconObject(nameTransform);
			if ((Object)(object)existingIconObject != (Object)null)
			{
				return existingIconObject;
			}
			Transform val = nameTransform.parent ?? nameTransform;
			ValheimForesightPlugin.Log?.LogDebug("[GetOrCreateIconObject] Creating new icon object 'Foresight_ThreatIcon' under parent '" + ((Object)val).name + "'.");
			GameObject val2 = new GameObject("Foresight_ThreatIcon");
			val2.transform.SetParent(val, false);
			_createdIcons.Add(val2);
			RectTransform val3 = val2.AddComponent<RectTransform>();
			val3.anchorMin = new Vector2(0f, 0.5f);
			val3.anchorMax = new Vector2(0f, 0.5f);
			val3.pivot = new Vector2(0f, 0.5f);
			IForesightConfiguration foresightConfig = ValheimForesightPlugin.ForesightConfig;
			if (foresightConfig != null)
			{
				val3.anchoredPosition = new Vector2(foresightConfig.ThreatIconOffsetX.Value, foresightConfig.ThreatIconOffsetY.Value);
				val3.sizeDelta = new Vector2(foresightConfig.ThreatIconSize.Value, foresightConfig.ThreatIconSize.Value);
			}
			return val2;
		}

		public void Dispose()
		{
			Dispose(disposing: true);
			GC.SuppressFinalize(this);
		}

		private void Dispose(bool disposing)
		{
			if (_disposed)
			{
				return;
			}
			if (disposing)
			{
				_spriteProvider?.Dispose();
			}
			foreach (GameObject createdIcon in _createdIcons)
			{
				if ((Object)(object)createdIcon != (Object)null)
				{
					Object.Destroy((Object)(object)createdIcon);
				}
			}
			_createdIcons.Clear();
			_disposed = true;
		}

		~UnityThreatHudIconRenderer()
		{
			Dispose(disposing: false);
		}
	}
}
namespace Valheim.Foresight.Services.Hud.Wrappers
{
	public sealed class ResourcesWrapper : IResourcesWrapper
	{
		public Sprite? LoadSprite(string path)
		{
			return Resources.Load<Sprite>(path);
		}
	}
}
namespace Valheim.Foresight.Services.Hud.Interfaces
{
	public interface IEmbeddedResourceStreamProvider
	{
		Stream? Open(string resourceName);

		string[] GetNames();
	}
	public interface IResourcesWrapper
	{
		Sprite? LoadSprite(string path);
	}
	public interface IThreatHudIconRenderer : IDisposable
	{
		void RenderIcon(TextMeshProUGUI? hud, ThreatResponseHint hint);
	}
	public interface IThreatIconSpriteProvider : IDisposable
	{
		Sprite? GetIcon(ThreatResponseHint hint);
	}
	public interface IUnityCastbarRenderer : IDisposable
	{
		void RenderCastbar(Transform hudParent, ActiveAttackInfo? attackInfo, Character? character = null);
	}
}
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)
			{
				Logger.LogDebug("[BlockDamageEstimator] no shield.");
				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(string.Format("[{0}] Armor: in={1:F1}, armor={2:F1}, out={3:F1}", "ApplyArmor", physicalDamage, armor, num));
			return num;
		}

		private float ApplyResistances(float physicalAfterArmor, float elementalDamage)
		{
			float num = physicalAfterArmor + elementalDamage;
			Logger.LogDebug(string.Format("[{0}] Resists: phys={1:F1}, elem={2:F1}, total={3:F1}", "ApplyResistances", physicalAfterArmor, elementalDamage, num));
			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)
			{
				Logger.LogDebug("[ApplyActiveDefense] no shield.");
				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);
			InspectItemSets(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 InspectItemSets(GameObject prefabRoot, ref float maxDamage)
		{
			Humanoid componentInChildren = prefabRoot.GetComponentInChildren<Humanoid>(true);
			if (componentInChildren?.m_randomSets == null || componentInChildren.m_randomSets.Length == 0)
			{
				return;
			}
			foreach (ItemDrop item in (from i in componentInChildren.m_randomSets.Where((ItemSet s) => s.m_items != null).SelectMany((ItemSet s) => s.m_items)
				where i != null
				select i).SelectMany((GameObject i) => i.GetComponentsInChildren<ItemDrop>(true)))
			{
				UpdateMaxFromItem(item.m_itemData, ((Object)item).name, 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_0028: Unknown result type (might be due to invalid IL or missing references)
			if (attack != null)
			{
				float num = 0f;
				FieldRef<Attack, ItemData?>? weaponRef = AttackFieldRefs.WeaponRef;
				ItemData val = ((weaponRef != null) ? weaponRef.Invoke(attack) : null);
				if (val != null)
				{
					num = CalculateTotalDamage(val.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);
			return 1f + (float)_mathfWrapper.Max(0, nearbyPlayerCount - 1) * 0.04f;
		}

		public int GetNearbyPlayerCount(Vector3 position)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			return _playerWrapper.GetPlayersInRangeXZ(position, 200f);
		}

		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("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("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("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("[CalculateThreat] 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) = CalculateMaxAttackDetailed(val, distance);
			}
			else
			{
				float num = CalculateMaxAttackSimple(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 CalculateMaxAttackSimple(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}", "CalculateMaxAttackSimple", ((Character)humanoid).m_name, maxAttackForCharacter));
			return maxAttackForCharacter;
		}

		private (float baseDamage, float maxMelee, float maxRanged, bool usedRanged) CalculateMaxAttackDetailed(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("[CalculateMaxAttackDetailed] " + ((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);
			int level = enemy.GetLevel();
			float num = 1f + 0.4f * (float)(level - 1);
			return baseDamage * damageMultiplier * num;
		}

		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("[LogThreatCalculation] name=" + 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}");
		}
	}
	public sealed class ThreatResponseHintService : IThreatResponseHintService
	{
		private const float HighRiskRatio = 0.7f;

		public ThreatResponseHint GetHint(ThreatAssessment assessment)
		{
			if (assessment == null)
			{
				throw new ArgumentNullException("assessment");
			}
			return assessment.Level switch
			{
				ThreatLevel.Safe => ThreatResponseHint.Block, 
				ThreatLevel.Caution => ThreatResponseHint.Block, 
				ThreatLevel.BlockLethal => ThreatResponseHint.Parry, 
				ThreatLevel.Danger => ThreatResponseHint.Dodge, 
				_ => ThreatResponseHint.None, 
			};
		}
	}
}
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 IThreatResponseHintService
	{
		ThreatResponseHint GetHint(ThreatAssessment assessment);
	}
	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.Services.Castbar
{
	public sealed class ActiveAttackTracker : IActiveAttackTracker
	{
		private readonly Dictionary<Character, ActiveAttackInfo> _activeAttacks = new Dictionary<Character, ActiveAttackInfo>();

		public void RegisterAttack(Character attacker, Attack attack, float duration, float startTime, float? predictedHitTime, string? animationName, bool hideParryIndicator)
		{
			_activeAttacks[attacker] = new ActiveAttackInfo(attacker, attack, duration, startTime, predictedHitTime, animationName, hideParryIndicator);
		}

		public ActiveAttackInfo? GetActiveAttack(Character attacker)
		{
			if (_activeAttacks.TryGetValue(attacker, out ActiveAttackInfo value))
			{
				if (value.IsExpired)
				{
					_activeAttacks.Remove(attacker);
					return null;
				}
				return value;
			}
			return null;
		}

		public void CleanupExpired()
		{
			List<Character> list = new List<Character>();
			foreach (KeyValuePair<Character, ActiveAttackInfo> activeAttack in _activeAttacks)
			{
				if (activeAttack.Value.IsExpired)
				{
					list.Add(activeAttack.Key);
				}
			}
			foreach (Character item in list)
			{
				_activeAttacks.Remove(item);
			}
		}
	}
	public sealed class AttackTimingService : IAttackTimingService, IDisposable, IAttackTimingDataProvider
	{
		private const string DataFileName = "attack_timings.yml";

		private const string PrelearnedDataFileName = "attack_timings_prelearned.yml";

		private const float AutoSaveIntervalSeconds = 30f;

		private const int MinSamplesForPrediction = 2;

		private const string UnknownKeyName = "unknown";

		private const string TimingsDbDirectoryName = "foresight.Database";

		pr