Decompiled source of Valheim Parry Sense v1.0.0

Valheim_ParrySense.dll

Decompiled 2 weeks ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using TMPro;
using UnityEngine;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyVersion("0.0.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace Valheim_ParrySense
{
	public struct Verdict
	{
		public bool HasBlockableAttack;

		public bool CanParryAtAll;

		public float BlockHealth;

		public float ParryHealth;

		public string WorstAttack;

		public float WorstStaggerDamage;

		public float BlockPower;

		public float ParryPower;
	}
	public static class BlockMath
	{
		private static readonly List<HitData> s_hits = new List<HitData>();

		private static readonly List<string> s_names = new List<string>();

		public static Verdict Evaluate(Player player, Character enemy)
		{
			//IL_00d0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fd: Unknown result type (might be due to invalid IL or missing references)
			//IL_011a: Unknown result type (might be due to invalid IL or missing references)
			Verdict result = default(Verdict);
			CollectWorstHits(player, enemy);
			if (s_hits.Count == 0)
			{
				return result;
			}
			result.HasBlockableAttack = true;
			ItemData val = ((Humanoid)player).LeftItem ?? ((Humanoid)player).GetCurrentWeapon();
			if (val == null)
			{
				result.BlockHealth = float.PositiveInfinity;
				result.ParryHealth = float.PositiveInfinity;
				return result;
			}
			float skillFactor = ((Character)player).GetSkillFactor((SkillType)6);
			float blockPower = val.GetBlockPower(skillFactor);
			float num = blockPower;
			result.CanParryAtAll = val.m_shared.m_timedBlockBonus > 1f;
			if (result.CanParryAtAll)
			{
				num *= val.m_shared.m_timedBlockBonus;
				((Character)player).GetSEMan().ModifyTimedBlockBonus(ref num);
			}
			result.BlockPower = blockPower;
			result.ParryPower = num;
			float num2 = 1f;
			((Character)player).GetSEMan().ModifyStagger(1f, ref num2);
			DamageModifiers damageModifiers = ((Character)player).GetDamageModifiers((WeakSpot)null);
			float bodyArmor = ((Character)player).GetBodyArmor();
			float staggerDamageFactor = ((Character)player).m_staggerDamageFactor;
			for (int i = 0; i < s_hits.Count; i++)
			{
				float num3 = StaggerThroughBlock(s_hits[i], val, blockPower, damageModifiers, bodyArmor) * num2;
				float num4 = StaggerThroughBlock(s_hits[i], val, num, damageModifiers, bodyArmor) * num2;
				if (num3 > result.WorstStaggerDamage)
				{
					result.WorstStaggerDamage = num3;
					result.WorstAttack = s_names[i];
				}
				float num5 = ((staggerDamageFactor > 0f) ? (num3 / staggerDamageFactor) : 0f);
				float num6 = ((staggerDamageFactor > 0f) ? (num4 / staggerDamageFactor) : 0f);
				result.BlockHealth = Mathf.Max(result.BlockHealth, num5);
				result.ParryHealth = Mathf.Max(result.ParryHealth, num6);
			}
			if (!result.CanParryAtAll)
			{
				result.ParryHealth = float.PositiveInfinity;
			}
			return result;
		}

		private static float StaggerThroughBlock(HitData original, ItemData blocker, float power, DamageModifiers bodyModifiers, float bodyArmor)
		{
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: 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)
			//IL_005f: 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_0065: Unknown result type (might be due to invalid IL or missing references)
			//IL_0072: Expected O, but got Unknown
			//IL_0073: 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_0094: 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_00a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00aa: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			HitData val = original.Clone();
			DamageModifier val3 = default(DamageModifier);
			if (blocker.m_shared.m_damageModifiers.Count > 0)
			{
				DamageModifiers val2 = default(DamageModifiers);
				((DamageModifiers)(ref val2)).Apply(blocker.m_shared.m_damageModifiers);
				val.ApplyResistance(val2, ref val3);
			}
			DamageTypes damage = ((DamageTypes)(ref val.m_damage)).Clone();
			((DamageTypes)(ref damage)).ApplyArmor(power);
			float totalStaggerDamage = ((DamageTypes)(ref damage)).GetTotalStaggerDamage();
			HitData val4 = new HitData
			{
				m_damage = damage,
				m_staggerMultiplier = val.m_staggerMultiplier
			};
			val4.ApplyResistance(bodyModifiers, ref val3);
			val4.ApplyArmor(bodyArmor);
			val4.ApplyModifier(Game.m_localDamgeTakenRate);
			DamageTypes damage2 = val4.m_damage;
			float num = ((((DamageTypes)(ref damage2)).GetTotalDamage() - damage2.m_fire - damage2.m_poison - damage2.m_spirit > 0.1f) ? (((DamageTypes)(ref damage2)).GetTotalStaggerDamage() * val4.m_staggerMultiplier) : 0f);
			return totalStaggerDamage + num;
		}

		private static void CollectWorstHits(Player player, Character enemy)
		{
			//IL_0097: Unknown result type (might be due to invalid IL or missing references)
			//IL_00da: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e0: Invalid comparison between Unknown and I4
			//IL_00f6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fe: Unknown result type (might be due to invalid IL or missing references)
			//IL_0103: 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)
			//IL_0115: 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_0127: Unknown result type (might be due to invalid IL or missing references)
			//IL_0130: Expected O, but got Unknown
			//IL_0216: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c3: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c9: Invalid comparison between Unknown and I4
			//IL_01d9: Unknown result type (might be due to invalid IL or missing references)
			//IL_01df: Invalid comparison between Unknown and I4
			s_hits.Clear();
			s_names.Clear();
			Humanoid val = (Humanoid)(object)((enemy is Humanoid) ? enemy : null);
			if (val == null || val.GetInventory() == null)
			{
				return;
			}
			ZNetView component = ((Component)enemy).GetComponent<ZNetView>();
			float num = (((Object)(object)component != (Object)null && component.GetZDO() != null) ? component.GetZDO().GetFloat(ZDOVars.s_randomSkillFactor, 1f) : 1f);
			float num2 = 1f + (float)Mathf.Max(0, enemy.GetLevel() - 1) * 0.5f;
			float num3 = (((Object)(object)Game.instance != (Object)null) ? Game.instance.GetDifficultyDamageScalePlayer(((Component)player).transform.position) : 1f);
			foreach (ItemData allItem in val.GetInventory().GetAllItems())
			{
				Attack attack = allItem.m_shared.m_attack;
				if (attack == null || (int)attack.m_attackType == 3 || !allItem.m_shared.m_blockable)
				{
					continue;
				}
				HitData val2 = new HitData
				{
					m_damage = allItem.GetDamage(),
					m_staggerMultiplier = attack.m_staggerMultiplier,
					m_skill = allItem.m_shared.m_skillType,
					m_blockable = true
				};
				if (((DamageTypes)(ref val2.m_damage)).GetTotalBlockableDamage() <= 0f)
				{
					continue;
				}
				float num4 = attack.m_damageMultiplier * num * num2;
				if (attack.m_damageMultiplierPerMissingHP > 0f)
				{
					num4 *= 1f + (enemy.GetMaxHealth() - enemy.GetHealth()) * attack.m_damageMultiplierPerMissingHP;
				}
				if (attack.m_damageMultiplierByTotalHealthMissing > 0f)
				{
					num4 *= 1f + (1f - enemy.GetHealthPercentage()) * attack.m_damageMultiplierByTotalHealthMissing;
				}
				if (attack.m_attackChainLevels > 1)
				{
					if ((int)attack.m_attackType == 0 || (int)attack.m_attackType == 1)
					{
						num4 *= 2f;
					}
					else if ((int)attack.m_attackType == 4 && attack.m_lastChainDamageMultiplier > 1f)
					{
						num4 *= attack.m_lastChainDamageMultiplier;
					}
				}
				((DamageTypes)(ref val2.m_damage)).Modify(num4);
				enemy.GetSEMan().ModifyAttack(allItem.m_shared.m_skillType, ref val2);
				val2.ApplyModifier(num3);
				val2.ApplyModifier(Game.m_enemyDamageRate);
				s_hits.Add(val2);
				s_names.Add(allItem.m_shared.m_name);
			}
		}
	}
	public static class FoodForecast
	{
		private static int s_frame = -1;

		private static float s_offset;

		private static float s_horizon;

		public static float MaxHealthIn(Player player, float seconds)
		{
			Prepare(player);
			return Raw(player, seconds) + s_offset;
		}

		public static float SecondsUntilBelow(Player player, float threshold)
		{
			Prepare(player);
			if (((Character)player).GetMaxHealth() <= threshold)
			{
				return 0f;
			}
			if (Game.m_foodRate <= 0f || Raw(player, s_horizon) + s_offset > threshold)
			{
				return float.PositiveInfinity;
			}
			float num = 0f;
			float num2 = s_horizon;
			for (int i = 0; i < 30; i++)
			{
				float num3 = (num + num2) * 0.5f;
				if (Raw(player, num3) + s_offset > threshold)
				{
					num = num3;
				}
				else
				{
					num2 = num3;
				}
			}
			return num2;
		}

		private static void Prepare(Player player)
		{
			if (s_frame == Time.frameCount)
			{
				return;
			}
			s_frame = Time.frameCount;
			s_offset = ((Character)player).GetMaxHealth() - Raw(player, 0f);
			float num = 0f;
			foreach (Food food in player.GetFoods())
			{
				num = Mathf.Max(num, food.m_time);
			}
			s_horizon = ((Game.m_foodRate > 0f) ? (num / Game.m_foodRate) : 0f);
		}

		private static float Raw(Player player, float seconds)
		{
			float num = player.GetBaseFoodHP();
			float num2 = seconds * Game.m_foodRate;
			foreach (Food food in player.GetFoods())
			{
				float num3 = food.m_time - num2;
				if (!(num3 <= 0f) && food.m_item != null)
				{
					float foodBurnTime = food.m_item.m_shared.m_foodBurnTime;
					float num4 = ((foodBurnTime > 0f) ? Mathf.Clamp01(num3 / foodBurnTime) : 0f);
					num += food.m_item.m_shared.m_food * Mathf.Pow(num4, 0.3f);
				}
			}
			return num;
		}
	}
	public static class ParrySenseConfig
	{
		public static ConfigEntry<bool> Enabled;

		public static ConfigEntry<float> IconSize;

		public static ConfigEntry<float> WarnMinutes;

		public static ConfigEntry<bool> ShowTimer;

		public static ConfigEntry<Color> SafeColor;

		public static ConfigEntry<Color> WarnColor;

		public static ConfigEntry<Color> DangerColor;

		public static ConfigEntry<Color> HopelessColor;

		public static ConfigEntry<Color> OutlineSafeColor;

		public static ConfigEntry<Color> OutlineDangerColor;

		public static ConfigEntry<float> RefreshInterval;

		public static ConfigEntry<bool> LogVerdicts;

		public static void Bind(ConfigFile config)
		{
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_004e: Expected O, but got Unknown
			//IL_007c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0086: Expected O, but got Unknown
			//IL_00c5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f8: Unknown result type (might be due to invalid IL or missing references)
			//IL_012b: Unknown result type (might be due to invalid IL or missing references)
			//IL_015e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0191: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c4: Unknown result type (might be due to invalid IL or missing references)
			//IL_0201: Unknown result type (might be due to invalid IL or missing references)
			//IL_020b: Expected O, but got Unknown
			Enabled = config.Bind<bool>("General", "Enabled", true, "Master switch. Turn off to disable the mod without uninstalling it.");
			IconSize = config.Bind<float>("Display", "IconSize", 18f, new ConfigDescription("Size of the shield under a regular enemy's health bar, in pixels of the HUD canvas. The boss bar gets half as much again.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(8f, 64f), Array.Empty<object>()));
			WarnMinutes = config.Bind<float>("Display", "WarnMinutes", 5f, new ConfigDescription("Warning window. The shield (parry) turns from SafeColor to WarnColor, then DangerColor, and the outline (plain block) from OutlineSafeColor to OutlineDangerColor, when food wearing off will make them fail in less than this many minutes.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.5f, 60f), Array.Empty<object>()));
			ShowTimer = config.Bind<bool>("Display", "ShowTimer", true, "Write the time left (m:ss) next to the shield while inside the warning window: time until the block fails while it still holds, then until the parry fails.");
			SafeColor = config.Bind<Color>("Colors", "SafeColor", new Color(0.55f, 1f, 0.55f, 1f), "Shield tint when the parry will still hold after WarnMinutes.");
			WarnColor = config.Bind<Color>("Colors", "WarnColor", new Color(1f, 0.9f, 0.3f, 1f), "Shield tint halfway through the warning window.");
			DangerColor = config.Bind<Color>("Colors", "DangerColor", new Color(1f, 0.25f, 0.2f, 1f), "Shield tint when the parry is about to fail.");
			HopelessColor = config.Bind<Color>("Colors", "HopelessColor", new Color(0.45f, 0.45f, 0.45f, 0.9f), "Shield tint when the parry does not hold (the red cross is shown too).");
			OutlineSafeColor = config.Bind<Color>("Colors", "OutlineSafeColor", new Color(1f, 0.82f, 0.25f, 1f), "Outline around the shield, shown while a plain block holds, when it will still hold after WarnMinutes.");
			OutlineDangerColor = config.Bind<Color>("Colors", "OutlineDangerColor", new Color(1f, 0.15f, 0.1f, 1f), "Outline colour when the plain block is about to fail.");
			RefreshInterval = config.Bind<float>("Advanced", "RefreshInterval", 0.25f, new ConfigDescription("Seconds between two evaluations of one enemy.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.05f, 2f), Array.Empty<object>()));
			LogVerdicts = config.Bind<bool>("Advanced", "LogVerdicts", false, "Write every change of verdict to the BepInEx log, with the numbers behind it: worst hit, stagger it causes, maximum health needed. For checking the mod against the game.");
		}
	}
	[BepInPlugin("Tchernobill.Valheim_ParrySense", "Parry Sense", "1.0.0")]
	public class ParrySensePlugin : BaseUnityPlugin
	{
		public const string GUID = "Tchernobill.Valheim_ParrySense";

		public const string NAME = "Parry Sense";

		public const string VERSION = "1.0.0";

		public static ManualLogSource Log { get; private set; }

		private void Awake()
		{
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			Log = ((BaseUnityPlugin)this).Logger;
			ParrySenseConfig.Bind(((BaseUnityPlugin)this).Config);
			if (!ParrySenseConfig.Enabled.Value)
			{
				Log.LogInfo((object)"Disabled by configuration, nothing is hooked.");
				return;
			}
			new Harmony("Tchernobill.Valheim_ParrySense").PatchAll();
			Log.LogInfo((object)"Parry Sense 1.0.0 ready.");
		}
	}
	[HarmonyPatch(typeof(EnemyHud), "ShowHud")]
	internal static class EnemyHudShowHudPatch
	{
		private static readonly FieldInfo HudsField = AccessTools.Field(typeof(EnemyHud), "m_huds");

		private static FieldInfo s_guiField;

		private static void Postfix(EnemyHud __instance, Character c, bool isMount)
		{
			if (isMount || (Object)(object)c == (Object)null || c.IsPlayer() || !(HudsField?.GetValue(__instance) is IDictionary dictionary))
			{
				return;
			}
			object obj = dictionary[c];
			if (obj != null)
			{
				if ((object)s_guiField == null)
				{
					s_guiField = AccessTools.Field(obj.GetType(), "m_gui");
				}
				object? obj2 = s_guiField?.GetValue(obj);
				GameObject val = (GameObject)((obj2 is GameObject) ? obj2 : null);
				if (val != null && !((Object)(object)val.GetComponent<ShieldIndicator>() != (Object)null))
				{
					val.AddComponent<ShieldIndicator>().Init(c);
				}
			}
		}
	}
	public static class ShieldIcons
	{
		private const int Inner = 64;

		private const int Pad = 10;

		private const float OutlineRadius = 6f;

		public const float CanvasScale = 1.3125f;

		private static bool s_built;

		public static Sprite Shield { get; private set; }

		public static Sprite Outline { get; private set; }

		public static Sprite Cross { get; private set; }

		public static bool Ready()
		{
			if (s_built)
			{
				return true;
			}
			if ((Object)(object)Player.m_localPlayer == (Object)null)
			{
				return false;
			}
			Sprite val = FindShieldSprite();
			if ((Object)(object)val == (Object)null)
			{
				return false;
			}
			BuildShield(val);
			Cross = FindCrossSprite() ?? DrawCross();
			s_built = true;
			ParrySensePlugin.Log.LogInfo((object)("Icons built from sprite '" + ((Object)val).name + "', cross from '" + ((Object)Cross).name + "'."));
			return true;
		}

		private static Sprite FindShieldSprite()
		{
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Invalid comparison between Unknown and I4
			Skills skills = ((Character)Player.m_localPlayer).GetSkills();
			if ((Object)(object)skills != (Object)null)
			{
				foreach (SkillDef skill in skills.m_skills)
				{
					if ((int)skill.m_skill == 6 && (Object)(object)skill.m_icon != (Object)null)
					{
						return skill.m_icon;
					}
				}
			}
			GameObject val = (((Object)(object)ObjectDB.instance != (Object)null) ? ObjectDB.instance.GetItemPrefab("ShieldWood") : null);
			ItemDrop val2 = (((Object)(object)val != (Object)null) ? val.GetComponent<ItemDrop>() : null);
			if (!((Object)(object)val2 != (Object)null) || val2.m_itemData.m_shared.m_icons.Length == 0)
			{
				return null;
			}
			return val2.m_itemData.m_shared.m_icons[0];
		}

		private static Sprite FindCrossSprite()
		{
			GameObject val = (((Object)(object)Minimap.instance != (Object)null) ? Minimap.instance.m_pinPrefab : null);
			Transform val2 = (((Object)(object)val != (Object)null) ? val.transform.Find("Checked") : null);
			Image val3 = (((Object)(object)val2 != (Object)null) ? ((Component)val2).GetComponent<Image>() : null);
			if (!((Object)(object)val3 != (Object)null))
			{
				return null;
			}
			return val3.sprite;
		}

		private static void BuildShield(Sprite source)
		{
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0050: 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_005f: Unknown result type (might be due to invalid IL or missing references)
			//IL_006d: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a8: 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_00b4: 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_01b3: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b8: Unknown result type (might be due to invalid IL or missing references)
			Texture2D val = CopyReadable(source);
			int num = 84;
			Color[] array = (Color[])(object)new Color[num * num];
			float[] array2 = new float[num * num];
			for (int i = 0; i < 64; i++)
			{
				for (int j = 0; j < 64; j++)
				{
					Color pixelBilinear = val.GetPixelBilinear(((float)j + 0.5f) / 64f, ((float)i + 0.5f) / 64f);
					float num2 = pixelBilinear.r * 0.299f + pixelBilinear.g * 0.587f + pixelBilinear.b * 0.114f;
					float num3 = Mathf.Lerp(0.3f, 1f, num2);
					int num4 = (i + 10) * num + j + 10;
					array[num4] = new Color(num3, num3, num3, pixelBilinear.a);
					array2[num4] = pixelBilinear.a;
				}
			}
			Object.Destroy((Object)(object)val);
			Color[] array3 = (Color[])(object)new Color[num * num];
			int num5 = Mathf.CeilToInt(6f);
			for (int k = 0; k < num; k++)
			{
				for (int l = 0; l < num; l++)
				{
					float num6 = 0f;
					for (int m = -num5; m <= num5; m++)
					{
						if (!(num6 < 1f))
						{
							break;
						}
						int num7 = k + m;
						if (num7 < 0 || num7 >= num)
						{
							continue;
						}
						for (int n = -num5; n <= num5; n++)
						{
							int num8 = l + n;
							if (num8 >= 0 && num8 < num)
							{
								float num9 = Mathf.Clamp01(6.5f - Mathf.Sqrt((float)(n * n + m * m)));
								num6 = Mathf.Max(num6, array2[num7 * num + num8] * num9);
							}
						}
					}
					array3[k * num + l] = new Color(1f, 1f, 1f, num6);
				}
			}
			Shield = MakeSprite(array, num, "ParrySense_shield");
			Outline = MakeSprite(array3, num, "ParrySense_outline");
		}

		private static Texture2D CopyReadable(Sprite sprite)
		{
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_006a: 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_0088: 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_0097: Expected O, but got Unknown
			Texture2D texture = sprite.texture;
			Rect textureRect = sprite.textureRect;
			RenderTexture temporary = RenderTexture.GetTemporary(((Texture)texture).width, ((Texture)texture).height, 0, (RenderTextureFormat)0, (RenderTextureReadWrite)0);
			Graphics.Blit((Texture)(object)texture, temporary);
			RenderTexture active = RenderTexture.active;
			RenderTexture.active = temporary;
			Texture2D val = new Texture2D((int)((Rect)(ref textureRect)).width, (int)((Rect)(ref textureRect)).height, (TextureFormat)4, false);
			val.ReadPixels(new Rect(((Rect)(ref textureRect)).x, ((Rect)(ref textureRect)).y, ((Rect)(ref textureRect)).width, ((Rect)(ref textureRect)).height), 0, 0);
			val.Apply();
			RenderTexture.active = active;
			RenderTexture.ReleaseTemporary(temporary);
			((Texture)val).filterMode = (FilterMode)1;
			((Texture)val).wrapMode = (TextureWrapMode)1;
			return val;
		}

		private static Sprite DrawCross()
		{
			//IL_0098: Unknown result type (might be due to invalid IL or missing references)
			//IL_009e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ac: 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_00b1: Unknown result type (might be due to invalid IL or missing references)
			Color[] array = (Color[])(object)new Color[4096];
			Color val = default(Color);
			((Color)(ref val))..ctor(0.85f, 0.1f, 0.1f, 1f);
			for (int i = 0; i < 64; i++)
			{
				for (int j = 0; j < 64; j++)
				{
					float num = (float)Mathf.Abs(j - i) / 1.414f;
					float num2 = (float)Mathf.Abs(j + i - 63) / 1.414f;
					float num3 = Mathf.Clamp01(5f - Mathf.Min(num, num2));
					bool flag = j > 6 && j < 57 && i > 6 && i < 57;
					array[i * 64 + j] = (Color)(flag ? new Color(val.r, val.g, val.b, num3) : Color.clear);
				}
			}
			return MakeSprite(array, 64, "ParrySense_cross");
		}

		private static Sprite MakeSprite(Color[] pixels, int size, string name)
		{
			//IL_0004: Unknown result type (might be due to invalid IL or missing references)
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: 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_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: Expected O, but got Unknown
			Texture2D val = new Texture2D(size, size, (TextureFormat)4, false)
			{
				name = name,
				filterMode = (FilterMode)1,
				wrapMode = (TextureWrapMode)1
			};
			val.SetPixels(pixels);
			val.Apply(false, true);
			Sprite obj = Sprite.Create(val, new Rect(0f, 0f, (float)size, (float)size), new Vector2(0.5f, 0.5f));
			((Object)obj).name = name;
			return obj;
		}
	}
	public class ShieldIndicator : MonoBehaviour
	{
		private Character m_enemy;

		private bool m_boss;

		private GameObject m_root;

		private Image m_outline;

		private Image m_shield;

		private Image m_cross;

		private TextMeshProUGUI m_timer;

		private float m_nextRefresh;

		private string m_lastLog;

		public void Init(Character enemy)
		{
			m_enemy = enemy;
			m_boss = enemy.IsBoss();
			m_nextRefresh = Time.time + Random.value * ParrySenseConfig.RefreshInterval.Value;
		}

		private void LateUpdate()
		{
			if ((!((Object)(object)m_root == (Object)null) || Build()) && !(Time.time < m_nextRefresh))
			{
				m_nextRefresh = Time.time + ParrySenseConfig.RefreshInterval.Value;
				Refresh();
			}
		}

		private bool Build()
		{
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_004c: Expected O, but got Unknown
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_005d: Expected O, but got Unknown
			//IL_00e7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ee: 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_010d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0118: Unknown result type (might be due to invalid IL or missing references)
			//IL_0191: Unknown result type (might be due to invalid IL or missing references)
			//IL_0197: Expected O, but got Unknown
			//IL_019d: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a2: Unknown result type (might be due to invalid IL or missing references)
			//IL_01aa: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_01bd: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c4: Unknown result type (might be due to invalid IL or missing references)
			//IL_01cb: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d6: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e0: Unknown result type (might be due to invalid IL or missing references)
			//IL_01eb: Unknown result type (might be due to invalid IL or missing references)
			//IL_01fb: 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_00a1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bb: Unknown result type (might be due to invalid IL or missing references)
			if (!ShieldIcons.Ready())
			{
				return false;
			}
			float num = ParrySenseConfig.IconSize.Value * (m_boss ? 1.5f : 1f);
			m_root = new GameObject("ParrySense", new Type[1] { typeof(RectTransform) });
			RectTransform val = (RectTransform)m_root.transform;
			Transform val2 = ((Component)this).transform.Find("Health");
			Vector2 val3 = default(Vector2);
			if (m_boss && (Object)(object)val2 != (Object)null)
			{
				((Transform)val).SetParent(val2, false);
				((Vector2)(ref val3))..ctor(1f, 0.5f);
				val.anchorMax = val3;
				val.anchorMin = val3;
				val.anchoredPosition = new Vector2(num * 0.5f + 10f, 0f);
			}
			else
			{
				((Transform)val).SetParent(((Component)this).transform, false);
				((Vector2)(ref val3))..ctor(0.5f, 0.5f);
				val.anchorMax = val3;
				val.anchorMin = val3;
				val.anchoredPosition = new Vector2(41f, -7f);
			}
			val.sizeDelta = new Vector2(num, num);
			((Transform)val).localScale = Vector3.one;
			m_outline = AddImage("Outline", ShieldIcons.Outline, num * 1.3125f);
			m_shield = AddImage("Shield", ShieldIcons.Shield, num * 1.3125f);
			m_cross = AddImage("Cross", ShieldIcons.Cross, num * 0.9f);
			GameObject val4 = new GameObject("Timer", new Type[1] { typeof(RectTransform) });
			RectTransform val5 = (RectTransform)val4.transform;
			((Transform)val5).SetParent((Transform)(object)val, false);
			((Vector2)(ref val3))..ctor(1f, 0.5f);
			val5.anchorMax = val3;
			val5.anchorMin = val3;
			val5.pivot = new Vector2(0f, 0.5f);
			val5.anchoredPosition = new Vector2(3f, 0f);
			val5.sizeDelta = new Vector2(60f, num);
			m_timer = val4.AddComponent<TextMeshProUGUI>();
			Transform obj = ((Component)this).transform.Find("Name");
			TextMeshProUGUI val6 = ((obj != null) ? ((Component)obj).GetComponent<TextMeshProUGUI>() : null);
			if ((Object)(object)val6 != (Object)null)
			{
				((TMP_Text)m_timer).font = ((TMP_Text)val6).font;
				((TMP_Text)m_timer).fontSharedMaterial = ((TMP_Text)val6).fontSharedMaterial;
			}
			((TMP_Text)m_timer).fontSize = (m_boss ? 16f : 11f);
			((TMP_Text)m_timer).alignment = (TextAlignmentOptions)4097;
			((TMP_Text)m_timer).textWrappingMode = (TextWrappingModes)0;
			((Graphic)m_timer).raycastTarget = false;
			((TMP_Text)m_timer).text = "";
			m_root.SetActive(false);
			return true;
		}

		private Image AddImage(string name, Sprite sprite, float side)
		{
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_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)
			//IL_0036: 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_0049: 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_0055: Unknown result type (might be due to invalid IL or missing references)
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			//IL_0062: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = new GameObject(name, new Type[1] { typeof(RectTransform) });
			RectTransform val2 = (RectTransform)val.transform;
			((Transform)val2).SetParent(m_root.transform, false);
			Vector2 val3 = default(Vector2);
			((Vector2)(ref val3))..ctor(0.5f, 0.5f);
			val2.anchorMax = val3;
			val2.anchorMin = val3;
			val2.anchoredPosition = Vector2.zero;
			val2.sizeDelta = new Vector2(side, side);
			Image obj = val.AddComponent<Image>();
			obj.sprite = sprite;
			obj.preserveAspect = true;
			((Graphic)obj).raycastTarget = false;
			return obj;
		}

		private void Refresh()
		{
			//IL_00d7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ce: Unknown result type (might be due to invalid IL or missing references)
			//IL_0102: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f9: Unknown result type (might be due to invalid IL or missing references)
			//IL_0107: Unknown result type (might be due to invalid IL or missing references)
			//IL_0120: Unknown result type (might be due to invalid IL or missing references)
			//IL_0140: Unknown result type (might be due to invalid IL or missing references)
			//IL_0145: Unknown result type (might be due to invalid IL or missing references)
			//IL_0154: Unknown result type (might be due to invalid IL or missing references)
			//IL_0156: Unknown result type (might be due to invalid IL or missing references)
			//IL_016e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0173: Unknown result type (might be due to invalid IL or missing references)
			//IL_01de: Unknown result type (might be due to invalid IL or missing references)
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null || (Object)(object)m_enemy == (Object)null || m_enemy.IsDead() || !BaseAI.IsEnemy((Character)(object)localPlayer, m_enemy))
			{
				m_root.SetActive(false);
				return;
			}
			Verdict v = BlockMath.Evaluate(localPlayer, m_enemy);
			if (!v.HasBlockableAttack)
			{
				m_root.SetActive(false);
				return;
			}
			float maxHealth = ((Character)localPlayer).GetMaxHealth();
			bool flag = maxHealth > v.BlockHealth;
			bool flag2 = v.CanParryAtAll && maxHealth > v.ParryHealth;
			float num = (flag ? FoodForecast.SecondsUntilBelow(localPlayer, v.BlockHealth) : float.NaN);
			float num2 = (flag2 ? FoodForecast.SecondsUntilBelow(localPlayer, v.ParryHealth) : float.NaN);
			((Graphic)m_shield).color = (flag2 ? ParryTint(num2) : ParrySenseConfig.HopelessColor.Value);
			((Component)m_cross).gameObject.SetActive(!flag2);
			Color val = (flag ? BlockTint(num) : Color.clear);
			((Component)m_outline).gameObject.SetActive(flag);
			((Graphic)m_outline).color = val;
			float num3 = ParrySenseConfig.WarnMinutes.Value * 60f;
			float num4 = float.NaN;
			Color color = Color.white;
			if (flag && num < num3)
			{
				num4 = num;
				color = val;
			}
			else if (flag2 && num2 < num3)
			{
				num4 = num2;
				color = ((Graphic)m_shield).color;
			}
			bool flag3 = ParrySenseConfig.ShowTimer.Value && !float.IsNaN(num4);
			((Component)m_timer).gameObject.SetActive(flag3);
			if (flag3)
			{
				int num5 = Mathf.CeilToInt(num4);
				((TMP_Text)m_timer).text = $"{num5 / 60}:{num5 % 60:00}";
				((Graphic)m_timer).color = color;
			}
			m_root.SetActive(true);
			if (ParrySenseConfig.LogVerdicts.Value)
			{
				LogIfChanged(v, maxHealth, flag, flag2, num, num2);
			}
		}

		private static float WindowLeft(float secondsLeft)
		{
			float num = ParrySenseConfig.WarnMinutes.Value * 60f;
			if (!float.IsInfinity(secondsLeft))
			{
				return Mathf.Clamp01(secondsLeft / num);
			}
			return 1f;
		}

		private static Color ParryTint(float secondsLeft)
		{
			//IL_003b: 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_0051: 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_001e: 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)
			float num = WindowLeft(secondsLeft);
			if (num >= 0.5f)
			{
				return Color.Lerp(ParrySenseConfig.WarnColor.Value, ParrySenseConfig.SafeColor.Value, (num - 0.5f) * 2f);
			}
			return Color.Lerp(ParrySenseConfig.DangerColor.Value, ParrySenseConfig.WarnColor.Value, num * 2f);
		}

		private static Color BlockTint(float secondsLeft)
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			return Color.Lerp(ParrySenseConfig.OutlineDangerColor.Value, ParrySenseConfig.OutlineSafeColor.Value, WindowLeft(secondsLeft));
		}

		private void LogIfChanged(Verdict v, float maxHealth, bool blockHolds, bool parryHolds, float blockLeft, float parryLeft)
		{
			string text = $"{blockHolds}/{parryHolds}/{Mathf.RoundToInt(v.BlockHealth)}/{Mathf.RoundToInt(v.ParryHealth)}";
			if (!(text == m_lastLog))
			{
				m_lastLog = text;
				ParrySensePlugin.Log.LogInfo((object)($"{m_enemy.m_name} lvl {m_enemy.GetLevel()}: worst '{v.WorstAttack}' stagger {v.WorstStaggerDamage:0.#} " + $"(block power {v.BlockPower:0.#}, parry {v.ParryPower:0.#}); max HP {maxHealth:0.#}, " + $"block needs >{v.BlockHealth:0.#} => {blockHolds}, parry needs >{v.ParryHealth:0.#} => {parryHolds}, " + "block lost in " + Seconds(blockLeft) + ", parry lost in " + Seconds(parryLeft)));
			}
		}

		private static string Seconds(float s)
		{
			if (!float.IsNaN(s))
			{
				if (!float.IsInfinity(s))
				{
					return s.ToString("0") + " s";
				}
				return "never";
			}
			return "-";
		}
	}
}