Decompiled source of VikingLib v0.2.2

VikingLib.dll

Decompiled a day ago
using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Runtime.Serialization;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using System.Threading;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Jotunn.Entities;
using Jotunn.Managers;
using Microsoft.CodeAnalysis;
using TMPro;
using UnityEngine;
using UnityEngine.UI;
using VikingLib.Combat;
using VikingLib.Combat.Attacks;
using VikingLib.Combat.Defense;
using VikingLib.Combat.Roll;
using VikingLib.Combat.Stagger;
using VikingLib.Combat.Telemetry;
using VikingLib.Configuration;
using VikingLib.Patches;
using VikingLib.SkillSystem;
using VikingLib.Stats;
using VikingLib.StatusEffects;
using VikingLib.UI.Tooltip;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("DigitalVikings")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyDescription("Modular combat mechanics, canonical status effects, and damage calculation framework for Valheim.")]
[assembly: AssemblyFileVersion("0.2.2.0")]
[assembly: AssemblyInformationalVersion("0.2.2+897827c685052c7dd2c93aef59734e74b28a9683")]
[assembly: AssemblyProduct("VikingLib")]
[assembly: AssemblyTitle("VikingLib")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("0.2.2.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[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]
	[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]
	[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 VikingLib
{
	[BepInPlugin("com.digitalvikings.vikinglib", "VikingLib", "0.2.2")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public class VikingLibPlugin : BaseUnityPlugin
	{
		public const string ModGuid = "com.digitalvikings.vikinglib";

		public const string ModName = "VikingLib";

		public const string ModVersion = "0.2.2";

		private Harmony _harmony;

		public static VikingLibPlugin Instance { get; private set; }

		public static ManualLogSource Log { get; internal set; }

		private void Awake()
		{
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Expected O, but got Unknown
			Instance = this;
			Log = ((BaseUnityPlugin)this).Logger;
			Log.LogInfo((object)"VikingLib v0.2.2 initializing...");
			ModConfig.Init(((BaseUnityPlugin)this).Config);
			_harmony = new Harmony("com.digitalvikings.vikinglib");
			_harmony.PatchAll();
			CanonicalStatusEffects.Init();
			CommandManager.Instance.AddConsoleCommand((ConsoleCommand)(object)new TraceConsoleCommand());
			CommandManager.Instance.AddConsoleCommand((ConsoleCommand)(object)new LogConsoleCommand());
			CommandManager.Instance.AddConsoleCommand((ConsoleCommand)(object)new ArmorConsoleCommand());
			CommandManager.Instance.AddConsoleCommand((ConsoleCommand)(object)new DumpAttacksConsoleCommand());
			CommandManager.Instance.AddConsoleCommand((ConsoleCommand)(object)new BuffTestConsoleCommand());
			CommandManager.Instance.AddConsoleCommand((ConsoleCommand)(object)new StatusEffectConsoleCommand());
			HookJotunnSync();
			Log.LogInfo((object)"VikingLib v0.2.2 loaded successfully. Combat pipeline and telemetry active.");
		}

		[MethodImpl(MethodImplOptions.NoInlining)]
		private void HookJotunnSync()
		{
			SynchronizationManager.OnConfigurationSynchronized += delegate
			{
				Log.LogInfo((object)"Server configuration synchronized successfully.");
			};
		}

		private void OnDestroy()
		{
			Harmony harmony = _harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
		}
	}
}
namespace VikingLib.UI
{
	[HarmonyPatch]
	public static class CharacterStatsGlossaryPanel
	{
		public delegate IEnumerable<(string Label, string FormattedValue)>? CustomGlossaryCategoryProvider(Player player);

		private sealed class PrioritizedCategoryProvider
		{
			public string CategoryTitle { get; }

			public int Priority { get; }

			public CustomGlossaryCategoryProvider Provider { get; }

			public PrioritizedCategoryProvider(string categoryTitle, int priority, CustomGlossaryCategoryProvider provider)
			{
				CategoryTitle = categoryTitle;
				Priority = priority;
				Provider = provider;
			}
		}

		public const string TopicName = "Character Stats";

		private static float _lastRefreshTime;

		private const float RefreshInterval = 0.5f;

		private static readonly List<PrioritizedCategoryProvider> _customCategories = new List<PrioritizedCategoryProvider>();

		public static event Action<Player, StringBuilder>? OnFormatCustomSections;

		public static void RegisterCustomCategory(string categoryTitle, CustomGlossaryCategoryProvider provider, int priority = 100)
		{
			if (!string.IsNullOrEmpty(categoryTitle) && provider != null)
			{
				_customCategories.Add(new PrioritizedCategoryProvider(categoryTitle, priority, provider));
				_customCategories.Sort((PrioritizedCategoryProvider a, PrioritizedCategoryProvider b) => a.Priority.CompareTo(b.Priority));
			}
		}

		public static void RegisterCustomCategory(string categoryTitle, Func<Player, IEnumerable<string>?> lineProvider, int priority = 100)
		{
			if (string.IsNullOrEmpty(categoryTitle) || lineProvider == null)
			{
				return;
			}
			RegisterCustomCategory(categoryTitle, delegate(Player player)
			{
				IEnumerable<string> enumerable = lineProvider(player);
				if (enumerable == null)
				{
					return (IEnumerable<(string Label, string FormattedValue)>?)null;
				}
				List<(string, string)> list = new List<(string, string)>();
				foreach (string item in enumerable)
				{
					list.Add((item, string.Empty));
				}
				return list;
			}, priority);
		}

		[HarmonyPatch(typeof(TextsDialog), "UpdateTextsList")]
		[HarmonyPostfix]
		public static void PostfixUpdateTextsList(TextsDialog __instance)
		{
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: Expected O, but got Unknown
			if (ModConfig.ModEnabled.Value && ModConfig.ShowStatsInGlossary.Value)
			{
				string text = FormatCharacterStats(Player.m_localPlayer);
				TextInfo item = new TextInfo("Character Stats", text);
				CharacterStatsHelper.GetTexts(__instance)?.Add(item);
			}
		}

		[HarmonyPatch(typeof(TextsDialog), "ShowText", new Type[] { typeof(TextInfo) })]
		[HarmonyPrefix]
		public static void PrefixShowText(TextsDialog __instance, TextInfo text)
		{
			if (ModConfig.ModEnabled.Value && ModConfig.ShowStatsInGlossary.Value && text != null && !(text.m_topic != "Character Stats"))
			{
				string text2 = FormatCharacterStats(Player.m_localPlayer);
				text.m_text = text2;
				_lastRefreshTime = Time.unscaledTime;
			}
		}

		[HarmonyPatch(typeof(TextsDialog), "Update")]
		[HarmonyPostfix]
		public static void PostfixUpdate(TextsDialog __instance)
		{
			if (!ModConfig.ModEnabled.Value || !ModConfig.ShowStatsInGlossary.Value || !((Component)__instance).gameObject.activeInHierarchy)
			{
				return;
			}
			float num = ModConfig.GlossaryRefreshInterval.Value;
			if (num <= 0.1f)
			{
				num = 1.5f;
			}
			if (Time.unscaledTime - _lastRefreshTime < num)
			{
				return;
			}
			List<TextInfo> texts = CharacterStatsHelper.GetTexts(__instance);
			if (texts == null)
			{
				return;
			}
			TextInfo val = null;
			for (int i = 0; i < texts.Count; i++)
			{
				TextInfo val2 = texts[i];
				if (val2 != null && val2.m_topic == "Character Stats")
				{
					if ((Object)(object)val2.m_selected != (Object)null && val2.m_selected.activeSelf)
					{
						val = val2;
						break;
					}
					if ((Object)(object)__instance.m_textAreaTopic != (Object)null && __instance.m_textAreaTopic.text == "Character Stats")
					{
						val = val2;
						break;
					}
				}
			}
			if (val == null)
			{
				return;
			}
			_lastRefreshTime = Time.unscaledTime;
			string text = FormatCharacterStats(Player.m_localPlayer);
			if (!(val.m_text != text))
			{
				return;
			}
			val.m_text = text;
			if ((Object)(object)__instance.m_textArea != (Object)null)
			{
				float value = (((Object)(object)__instance.m_rightScrollbar != (Object)null) ? __instance.m_rightScrollbar.value : 1f);
				__instance.m_textArea.text = text;
				if ((Object)(object)__instance.m_rightScrollbar != (Object)null)
				{
					__instance.m_rightScrollbar.value = value;
				}
			}
		}

		public static string FormatCharacterStats(Player? player)
		{
			//IL_0364: Unknown result type (might be due to invalid IL or missing references)
			//IL_0369: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)player == (Object)null)
			{
				return "No character currently active.";
			}
			StringBuilder stringBuilder = new StringBuilder();
			stringBuilder.AppendLine("<size=125%><color=#FFCC00><b>CHARACTER OVERVIEW</b></color></size>\n");
			stringBuilder.AppendLine("<color=orange><b>[ MAXIMUM ATTRIBUTES ]</b></color>");
			AppendStatLine(stringBuilder, player, CharacterStatType.MaxHealth, "Max Health", "HP");
			AppendStatLine(stringBuilder, player, CharacterStatType.MaxStamina, "Max Stamina", "pts");
			AppendStatLine(stringBuilder, player, CharacterStatType.MaxEitr, "Max Eitr", "pts");
			AppendStatLine(stringBuilder, player, CharacterStatType.MaxCarryWeight, "Max Carry Weight", "kg");
			AppendStatLineIfModified(stringBuilder, player, CharacterStatType.FoodHealthEffectiveness, "Food Health Effectiveness", "+%", 1f);
			AppendStatLineIfModified(stringBuilder, player, CharacterStatType.FoodHealthRegenEffectiveness, "Food HP Regen Effectiveness", "+%", 1f);
			AppendStatLineIfModified(stringBuilder, player, CharacterStatType.FoodStaminaEffectiveness, "Food Stamina Effectiveness", "+%", 1f);
			AppendStatLineIfModified(stringBuilder, player, CharacterStatType.FoodEitrEffectiveness, "Food Eitr Effectiveness", "+%", 1f);
			AppendStatLineIfModified(stringBuilder, player, CharacterStatType.FoodDurationMultiplier, "Food Duration", "x", 1f);
			stringBuilder.AppendLine();
			stringBuilder.AppendLine("<color=orange><b>[ REGENERATION & RECOVERY ]</b></color>");
			AppendStatLine(stringBuilder, player, CharacterStatType.HealthRegen, "Health Regeneration", "HP/s");
			AppendStatLine(stringBuilder, player, CharacterStatType.HealthRegenDelay, "Combat Health Regen Delay", "sec");
			float remainingRegenDelay = PlayerStatsPatch.GetRemainingRegenDelay((Character)(object)player);
			if (remainingRegenDelay > 0.05f)
			{
				stringBuilder.AppendLine($"    <color=#FF9933>└─ Status: Paused in combat ({remainingRegenDelay:F1}s remaining)</color>");
			}
			AppendStatLine(stringBuilder, player, CharacterStatType.StaminaRegen, "Stamina Regeneration", "pts/s");
			AppendStatLine(stringBuilder, player, CharacterStatType.EitrRegen, "Eitr Regeneration", "pts/s");
			AppendStatLine(stringBuilder, player, CharacterStatType.StaminaRegenDelay, "Stamina Recovery Delay", "sec");
			stringBuilder.AppendLine();
			stringBuilder.AppendLine("<color=orange><b>[ DEFENSE & RESISTANCES ]</b></color>");
			AppendStatLine(stringBuilder, player, CharacterStatType.BodyArmor, "Body Armor", "pts");
			AppendStatLineIfModified(stringBuilder, player, CharacterStatType.ArmorEfficiency, "Armor Efficiency", "+%", 0f);
			AppendStatLineIfModified(stringBuilder, player, CharacterStatType.BluntArmorEfficiency, "Blunt Armor Efficiency", "+%", 0f);
			AppendStatLineIfModified(stringBuilder, player, CharacterStatType.SlashArmorEfficiency, "Slash Armor Efficiency", "+%", 0f);
			AppendStatLineIfModified(stringBuilder, player, CharacterStatType.PierceArmorEfficiency, "Pierce Armor Efficiency", "+%", 0f);
			EntityDefenseData defense = EntityDefenseRegistry.GetDefense((Character)(object)player);
			List<(string, float, string, List<string>)> list = new List<(string, float, string, List<string>)>();
			DamageType[] array = new DamageType[7]
			{
				DamageType.Blunt,
				DamageType.Slash,
				DamageType.Pierce,
				DamageType.Fire,
				DamageType.Frost,
				DamageType.Lightning,
				DamageType.Poison
			};
			for (int i = 0; i < array.Length; i++)
			{
				DamageType type = array[i];
				float resistanceForType = defense.GetResistanceForType(type);
				float num = 1f;
				CharacterStatType? resistanceStat = CharacterStatManager.GetResistanceStat(type);
				if (resistanceStat.HasValue)
				{
					num *= CharacterStatManager.GetStat((Character?)(object)player, resistanceStat.Value);
				}
				if (type.IsPhysical())
				{
					num *= CharacterStatManager.GetStat((Character?)(object)player, CharacterStatType.ResistancePhysical);
				}
				else if (type.IsElemental())
				{
					num *= CharacterStatManager.GetStat((Character?)(object)player, CharacterStatType.ResistanceElemental);
				}
				float num2 = resistanceForType * num;
				if (!(Mathf.Abs(num2 - 1f) > 0.001f))
				{
					continue;
				}
				List<string> resistanceSources = GetResistanceSources(player, type);
				if (resistanceStat.HasValue)
				{
					foreach (var multiplier in CharacterStatManager.GetTrace((Character?)(object)player, resistanceStat.Value).Multipliers)
					{
						resistanceSources.Add($"{multiplier.Source} (x{multiplier.Value:F2})");
					}
				}
				string item = ((object)EntityDefenseRegistry.ToVanillaModifier(num2)/*cast due to .constrained prefix*/).ToString();
				list.Add((type.ToString(), num2, item, resistanceSources));
			}
			if (list.Count > 0)
			{
				stringBuilder.AppendLine("• <b>Damage Resistances</b>:");
				for (int j = 0; j < list.Count; j++)
				{
					(string, float, string, List<string>) tuple = list[j];
					stringBuilder.AppendLine($"    <color=yellow>{tuple.Item1} Taken</color>: {tuple.Item2:F2}x ({tuple.Item3})");
					for (int k = 0; k < tuple.Item4.Count; k++)
					{
						string text = ((k == tuple.Item4.Count - 1) ? "        <color=#A0A0A0>└─ " : "        <color=#A0A0A0>├─ ");
						stringBuilder.AppendLine(text + "[Tier 3 Multiplier] " + tuple.Item4[k] + "</color>");
					}
				}
			}
			AppendStatLineIfModified(stringBuilder, player, CharacterStatType.BlockPowerMultiplier, "Block Power Multiplier", "x", 1f);
			AppendStatLineIfModified(stringBuilder, player, CharacterStatType.ParryMultiplier, "Parry Bonus Multiplier", "x", 1f);
			AppendStatLineIfModified(stringBuilder, player, CharacterStatType.ParryWindow, "Parry Timing Window", "sec", 0.25f);
			AppendStatLineIfModified(stringBuilder, player, CharacterStatType.StaggerResistance, "Stagger Resistance", "x", 1f);
			stringBuilder.AppendLine();
			StringBuilder stringBuilder2 = new StringBuilder();
			StringBuilder stringBuilder3 = new StringBuilder();
			AppendStatLineIfModified(stringBuilder3, player, CharacterStatType.DamageMelee, "Melee Damage Bonus", "x", 1f, "    ");
			AppendStatLineIfModified(stringBuilder3, player, CharacterStatType.DamageOneHanded, "One-Handed Damage Bonus", "x", 1f, "    ");
			AppendStatLineIfModified(stringBuilder3, player, CharacterStatType.DamageTwoHanded, "Two-Handed Damage Bonus", "x", 1f, "    ");
			AppendStatLineIfModified(stringBuilder3, player, CharacterStatType.DamageRanged, "Ranged Damage Bonus", "x", 1f, "    ");
			AppendStatLineIfModified(stringBuilder3, player, CharacterStatType.DamageMagic, "Magic Damage Bonus", "x", 1f, "    ");
			if (stringBuilder3.Length > 0)
			{
				stringBuilder2.AppendLine("  <color=#FFE082><b>[ Delivery & Stance ]</b></color>");
				stringBuilder2.Append(stringBuilder3.ToString());
			}
			StringBuilder stringBuilder4 = new StringBuilder();
			AppendStatLineIfModified(stringBuilder4, player, CharacterStatType.AttackSpeed, "Attack Speed", "x", 1f, "    ");
			AppendStatLineIfModified(stringBuilder4, player, CharacterStatType.CriticalStrikeChance, "Critical Strike Chance", "%", ModConfig.DefaultPlayerCritChance.Value, "    ");
			AppendStatLineIfModified(stringBuilder4, player, CharacterStatType.CriticalStrikeMultiplier, "Critical Strike Multiplier", "x", ModConfig.DefaultCritMultiplier.Value, "    ");
			AppendStatLineIfModified(stringBuilder4, player, CharacterStatType.DamageSecondaryAttack, "Secondary Attack Damage", "x", 1f, "    ");
			AppendStatLineIfModified(stringBuilder4, player, CharacterStatType.DamageComboFinisher, "Combo Finisher Damage", "x", 1f, "    ");
			AppendStatLineIfModified(stringBuilder4, player, CharacterStatType.DamageSneak, "Sneak Attack Damage", "x", 1f, "    ");
			AppendStatLineIfModified(stringBuilder4, player, CharacterStatType.CleaveEfficiency, "Cleave Efficiency", "x", 1f, "    ");
			AppendStatLineIfModified(stringBuilder4, player, CharacterStatType.StaggerDamage, "Stagger Power", "x", 1f, "    ");
			AppendStatLineIfModified(stringBuilder4, player, CharacterStatType.KnockbackDealt, "Knockback Force", "x", 1f, "    ");
			AppendStatLineIfModified(stringBuilder4, player, CharacterStatType.MeleeReach, "Melee Reach", "x", 1f, "    ");
			AppendStatLineIfModified(stringBuilder4, player, CharacterStatType.BowDrawSpeed, "Bow Draw Speed", "x", 1f, "    ");
			AppendStatLineIfModified(stringBuilder4, player, CharacterStatType.CrossbowReloadSpeed, "Crossbow Reload Speed", "x", 1f, "    ");
			AppendStatLineIfModified(stringBuilder4, player, CharacterStatType.ProjectilePrecision, "Projectile Precision Rating", "x", 1f, "    ");
			if (stringBuilder4.Length > 0)
			{
				stringBuilder2.AppendLine("  <color=#FFE082><b>[ Attack Styles & Techniques ]</b></color>");
				stringBuilder2.Append(stringBuilder4.ToString());
			}
			StringBuilder stringBuilder5 = new StringBuilder();
			AppendStatLineIfModified(stringBuilder5, player, CharacterStatType.HealthOnKill, "Health On Kill", "HP", 0f, "    ");
			AppendStatLineIfModified(stringBuilder5, player, CharacterStatType.StaminaOnKill, "Stamina On Kill", "pts", 0f, "    ");
			AppendStatLineIfModified(stringBuilder5, player, CharacterStatType.EitrOnKill, "Eitr On Kill", "pts", 0f, "    ");
			AppendStatLineIfModified(stringBuilder5, player, CharacterStatType.HealthOnDamageTaken, "Health On Damage Taken", "HP", 0f, "    ");
			AppendStatLineIfModified(stringBuilder5, player, CharacterStatType.StaminaOnDamageTaken, "Stamina On Damage Taken", "pts", 0f, "    ");
			AppendStatLineIfModified(stringBuilder5, player, CharacterStatType.EitrOnDamageTaken, "Eitr On Damage Taken", "pts", 0f, "    ");
			AppendStatLineIfModified(stringBuilder5, player, CharacterStatType.HealthOnCauseStagger, "Health On Cause Stagger", "HP", 0f, "    ");
			AppendStatLineIfModified(stringBuilder5, player, CharacterStatType.StaminaOnCauseStagger, "Stamina On Cause Stagger", "pts", 0f, "    ");
			AppendStatLineIfModified(stringBuilder5, player, CharacterStatType.EitrOnCauseStagger, "Eitr On Cause Stagger", "pts", 0f, "    ");
			if (stringBuilder5.Length > 0)
			{
				stringBuilder2.AppendLine("  <color=#FFE082><b>[ Combat Resource Recovery ]</b></color>");
				stringBuilder2.Append(stringBuilder5.ToString());
			}
			StringBuilder stringBuilder6 = new StringBuilder();
			AppendStatLineIfModified(stringBuilder6, player, CharacterStatType.DamageAgainstBosses, "Boss Damage Bonus", "x", 1f, "    ");
			AppendStatLineIfModified(stringBuilder6, player, CharacterStatType.DamageAgainstStaggered, "Stagger Punish Bonus", "x", 1f, "    ");
			AppendStatLineIfModified(stringBuilder6, player, CharacterStatType.DamageAgainstAirborne, "Airborne Damage Bonus", "x", 1f, "    ");
			AppendStatLineIfModified(stringBuilder6, player, CharacterStatType.DamageExecute, "Execute Damage (Target HP <= 35%)", "x", 1f, "    ");
			AppendStatLineIfModified(stringBuilder6, player, CharacterStatType.DamageFirstStrike, "First Strike (Target HP >= 90%)", "x", 1f, "    ");
			AppendStatLineIfModified(stringBuilder6, player, CharacterStatType.DamageBerserk, "Berserk Damage (Self HP <= 35%)", "x", 1f, "    ");
			AppendStatLineIfModified(stringBuilder6, player, CharacterStatType.DamageFlawless, "Flawless Damage (Self HP >= 90%)", "x", 1f, "    ");
			AppendStatLineIfModified(stringBuilder6, player, CharacterStatType.DamageAtNight, "Night Damage Bonus", "x", 1f, "    ");
			AppendStatLineIfModified(stringBuilder6, player, CharacterStatType.DamageWhileAirborne, "Self-Airborne Damage Bonus", "x", 1f, "    ");
			AppendStatLineIfModified(stringBuilder6, player, CharacterStatType.DamageAgainstAfflicted, "Afflicted Target Damage", "x", 1f, "    ");
			if (stringBuilder6.Length > 0)
			{
				stringBuilder2.AppendLine("  <color=#FFE082><b>[ Situational & Target Contexts ]</b></color>");
				stringBuilder2.Append(stringBuilder6.ToString());
			}
			StringBuilder stringBuilder7 = new StringBuilder();
			AppendStatLineIfModified(stringBuilder7, player, CharacterStatType.DamageSwords, "Swords Damage", "x", 1f, "    ");
			AppendStatLineIfModified(stringBuilder7, player, CharacterStatType.AttackSpeedSwords, "Swords Attack Speed", "x", 1f, "    ");
			AppendStatLineIfModified(stringBuilder7, player, CharacterStatType.DamageKnives, "Knives Damage", "x", 1f, "    ");
			AppendStatLineIfModified(stringBuilder7, player, CharacterStatType.AttackSpeedKnives, "Knives Attack Speed", "x", 1f, "    ");
			AppendStatLineIfModified(stringBuilder7, player, CharacterStatType.DamageClubs, "Clubs Damage", "x", 1f, "    ");
			AppendStatLineIfModified(stringBuilder7, player, CharacterStatType.AttackSpeedClubs, "Clubs Attack Speed", "x", 1f, "    ");
			AppendStatLineIfModified(stringBuilder7, player, CharacterStatType.DamagePolearms, "Polearms Damage", "x", 1f, "    ");
			AppendStatLineIfModified(stringBuilder7, player, CharacterStatType.AttackSpeedPolearms, "Polearms Attack Speed", "x", 1f, "    ");
			AppendStatLineIfModified(stringBuilder7, player, CharacterStatType.DamageSpears, "Spears Damage", "x", 1f, "    ");
			AppendStatLineIfModified(stringBuilder7, player, CharacterStatType.AttackSpeedSpears, "Spears Attack Speed", "x", 1f, "    ");
			AppendStatLineIfModified(stringBuilder7, player, CharacterStatType.DamageAxes, "Axes Damage", "x", 1f, "    ");
			AppendStatLineIfModified(stringBuilder7, player, CharacterStatType.AttackSpeedAxes, "Axes Attack Speed", "x", 1f, "    ");
			AppendStatLineIfModified(stringBuilder7, player, CharacterStatType.DamageBows, "Bows Damage", "x", 1f, "    ");
			AppendStatLineIfModified(stringBuilder7, player, CharacterStatType.AttackSpeedBows, "Bows Attack Speed", "x", 1f, "    ");
			AppendStatLineIfModified(stringBuilder7, player, CharacterStatType.DamageCrossbows, "Crossbows Damage", "x", 1f, "    ");
			AppendStatLineIfModified(stringBuilder7, player, CharacterStatType.AttackSpeedCrossbows, "Crossbows Attack Speed", "x", 1f, "    ");
			AppendStatLineIfModified(stringBuilder7, player, CharacterStatType.DamageUnarmed, "Unarmed & Fist Damage", "x", 1f, "    ");
			AppendStatLineIfModified(stringBuilder7, player, CharacterStatType.AttackSpeedUnarmed, "Unarmed Attack Speed", "x", 1f, "    ");
			AppendStatLineIfModified(stringBuilder7, player, CharacterStatType.DamageElementalMagic, "Elemental Magic Damage", "x", 1f, "    ");
			AppendStatLineIfModified(stringBuilder7, player, CharacterStatType.AttackSpeedElementalMagic, "Elemental Magic Attack Speed", "x", 1f, "    ");
			AppendStatLineIfModified(stringBuilder7, player, CharacterStatType.DamageBloodMagic, "Blood Magic Damage", "x", 1f, "    ");
			AppendStatLineIfModified(stringBuilder7, player, CharacterStatType.AttackSpeedBloodMagic, "Blood Magic Attack Speed", "x", 1f, "    ");
			if (stringBuilder7.Length > 0)
			{
				stringBuilder2.AppendLine("  <color=#FFE082><b>[ Weapon Families ]</b></color>");
				stringBuilder2.Append(stringBuilder7.ToString());
			}
			StringBuilder stringBuilder8 = new StringBuilder();
			AppendStatLineIfModified(stringBuilder8, player, CharacterStatType.DamagePhysical, "Physical Damage Bonus", "x", 1f, "    ");
			AppendStatLineIfModified(stringBuilder8, player, CharacterStatType.DamageSlashing, "Slashing Damage", "x", 1f, "    ");
			AppendStatLineIfModified(stringBuilder8, player, CharacterStatType.DamageBlunt, "Blunt Damage", "x", 1f, "    ");
			AppendStatLineIfModified(stringBuilder8, player, CharacterStatType.DamagePiercing, "Piercing Damage", "x", 1f, "    ");
			AppendStatLineIfModified(stringBuilder8, player, CharacterStatType.DamageElemental, "Elemental Damage Bonus", "x", 1f, "    ");
			AppendStatLineIfModified(stringBuilder8, player, CharacterStatType.DamageFire, "Fire Damage", "x", 1f, "    ");
			AppendStatLineIfModified(stringBuilder8, player, CharacterStatType.DamageFrost, "Frost Damage", "x", 1f, "    ");
			AppendStatLineIfModified(stringBuilder8, player, CharacterStatType.DamageLightning, "Lightning Damage", "x", 1f, "    ");
			AppendStatLineIfModified(stringBuilder8, player, CharacterStatType.DamagePoison, "Poison Damage", "x", 1f, "    ");
			AppendStatLineIfModified(stringBuilder8, player, CharacterStatType.DamageSpirit, "Spirit Damage", "x", 1f, "    ");
			AppendStatLineIfModified(stringBuilder8, player, CharacterStatType.DamageChop, "Chop Damage", "x", 1f, "    ");
			AppendStatLineIfModified(stringBuilder8, player, CharacterStatType.DamagePickaxe, "Pickaxe Damage", "x", 1f, "    ");
			if (stringBuilder8.Length > 0)
			{
				stringBuilder2.AppendLine("  <color=#FFE082><b>[ Damage Types & Archetypes ]</b></color>");
				stringBuilder2.Append(stringBuilder8.ToString());
			}
			if (stringBuilder2.Length > 0)
			{
				stringBuilder.AppendLine("<color=orange><b>[ OFFENSIVE PROFICIENCIES ]</b></color>");
				stringBuilder.Append(stringBuilder2.ToString());
				stringBuilder.AppendLine();
			}
			StringBuilder stringBuilder9 = new StringBuilder();
			StringBuilder stringBuilder10 = new StringBuilder();
			AppendStatLineIfModified(stringBuilder10, player, CharacterStatType.AttackStaminaEfficiency, "Attack Stamina Efficiency", "+%", 0f, "    ");
			AppendStatLineIfModified(stringBuilder10, player, CharacterStatType.MeleeStaminaEfficiency, "Melee Stamina Efficiency", "+%", 0f, "    ");
			AppendStatLineIfModified(stringBuilder10, player, CharacterStatType.OneHandedStaminaEfficiency, "One-Handed Stamina Efficiency", "+%", 0f, "    ");
			AppendStatLineIfModified(stringBuilder10, player, CharacterStatType.TwoHandedStaminaEfficiency, "Two-Handed Stamina Efficiency", "+%", 0f, "    ");
			AppendStatLineIfModified(stringBuilder10, player, CharacterStatType.RangedStaminaEfficiency, "Ranged Stamina Efficiency", "+%", 0f, "    ");
			AppendStatLineIfModified(stringBuilder10, player, CharacterStatType.SecondaryAttackStaminaEfficiency, "Secondary Attack Stamina Efficiency", "+%", 0f, "    ");
			AppendStatLineIfModified(stringBuilder10, player, CharacterStatType.AttackEitrEfficiency, "Attack Eitr Efficiency", "+%", 0f, "    ");
			AppendStatLineIfModified(stringBuilder10, player, CharacterStatType.AttackHealthEfficiency, "Attack Health Efficiency", "+%", 0f, "    ");
			AppendStatLineIfModified(stringBuilder10, player, CharacterStatType.SwordsEfficiency, "Swords Stamina Efficiency", "+%", 0f, "    ");
			AppendStatLineIfModified(stringBuilder10, player, CharacterStatType.KnivesEfficiency, "Knives Stamina Efficiency", "+%", 0f, "    ");
			AppendStatLineIfModified(stringBuilder10, player, CharacterStatType.ClubsEfficiency, "Clubs Stamina Efficiency", "+%", 0f, "    ");
			AppendStatLineIfModified(stringBuilder10, player, CharacterStatType.PolearmsEfficiency, "Polearms Stamina Efficiency", "+%", 0f, "    ");
			AppendStatLineIfModified(stringBuilder10, player, CharacterStatType.SpearsEfficiency, "Spears Stamina Efficiency", "+%", 0f, "    ");
			AppendStatLineIfModified(stringBuilder10, player, CharacterStatType.AxesEfficiency, "Axes Stamina Efficiency", "+%", 0f, "    ");
			AppendStatLineIfModified(stringBuilder10, player, CharacterStatType.BowsEfficiency, "Bows Stamina Efficiency", "+%", 0f, "    ");
			AppendStatLineIfModified(stringBuilder10, player, CharacterStatType.CrossbowReloadStaminaEfficiency, "Crossbow Reload Stamina Efficiency", "+%", 0f, "    ");
			AppendStatLineIfModified(stringBuilder10, player, CharacterStatType.UnarmedEfficiency, "Unarmed Stamina Efficiency", "+%", 0f, "    ");
			AppendStatLineIfModified(stringBuilder10, player, CharacterStatType.ElementalMagicEitrEfficiency, "Elemental Magic Eitr Efficiency", "+%", 0f, "    ");
			AppendStatLineIfModified(stringBuilder10, player, CharacterStatType.BloodMagicHealthEfficiency, "Blood Magic Health Efficiency", "+%", 0f, "    ");
			AppendStatLineIfModified(stringBuilder10, player, CharacterStatType.BloodMagicEitrEfficiency, "Blood Magic Eitr Efficiency", "+%", 0f, "    ");
			if (stringBuilder10.Length > 0)
			{
				stringBuilder9.AppendLine("  <color=#FFE082><b>[ Combat & Attack Resource Costs ]</b></color>");
				stringBuilder9.Append(stringBuilder10.ToString());
			}
			StringBuilder stringBuilder11 = new StringBuilder();
			AppendStatLineIfModified(stringBuilder11, player, CharacterStatType.SprintEfficiency, "Sprint Stamina Efficiency", "+%", 0f, "    ");
			AppendStatLineIfModified(stringBuilder11, player, CharacterStatType.JumpEfficiency, "Jump Stamina Efficiency", "+%", 0f, "    ");
			AppendStatLineIfModified(stringBuilder11, player, CharacterStatType.DodgeEfficiency, "Dodge Stamina Efficiency", "+%", 0f, "    ");
			AppendStatLineIfModified(stringBuilder11, player, CharacterStatType.SneakEfficiency, "Sneak Stamina Efficiency", "+%", 0f, "    ");
			AppendStatLineIfModified(stringBuilder11, player, CharacterStatType.SwimEfficiency, "Swim Stamina Efficiency", "+%", 0f, "    ");
			AppendStatLineIfModified(stringBuilder11, player, CharacterStatType.BowDrawStaminaEfficiency, "Bow Hold Stamina Efficiency", "+%", 0f, "    ");
			AppendStatLineIfModified(stringBuilder11, player, CharacterStatType.BowDrawEitrEfficiency, "Staff Hold Eitr Efficiency", "+%", 0f, "    ");
			AppendStatLineIfModified(stringBuilder11, player, CharacterStatType.FishingStaminaEfficiency, "Fishing Reel Stamina Efficiency", "+%", 0f, "    ");
			AppendStatLineIfModified(stringBuilder11, player, CharacterStatType.BlockStaminaEfficiency, "Block Stamina Efficiency", "+%", 0f, "    ");
			AppendStatLineIfModified(stringBuilder11, player, CharacterStatType.RidingStaminaEfficiency, "Riding Stamina Efficiency", "+%", 0f, "    ");
			AppendStatLineIfModified(stringBuilder11, player, CharacterStatType.ToolStaminaEfficiency, "Tool Stamina Efficiency", "+%", 0f, "    ");
			AppendStatLineIfModified(stringBuilder11, player, CharacterStatType.DurabilityEfficiency, "Durability Efficiency", "+%", 0f, "    ");
			if (stringBuilder11.Length > 0)
			{
				stringBuilder9.AppendLine("  <color=#FFE082><b>[ Locomotion & World Activities ]</b></color>");
				stringBuilder9.Append(stringBuilder11.ToString());
			}
			if (stringBuilder9.Length > 0)
			{
				stringBuilder.AppendLine("<color=orange><b>[ ACTION RESOURCE EFFICIENCIES ]</b></color>");
				stringBuilder.Append(stringBuilder9.ToString());
				stringBuilder.AppendLine();
			}
			stringBuilder.AppendLine("<color=orange><b>[ LOCOMOTION & SPEEDS ]</b></color>");
			AppendVelocityStatLine(stringBuilder, player, CharacterStatType.JogSpeed, "Jog Speed", ((Character)player).m_speed);
			AppendVelocityStatLine(stringBuilder, player, CharacterStatType.RunSpeed, "Sprint Speed", ((Character)player).m_runSpeed);
			AppendStatLine(stringBuilder, player, CharacterStatType.WalkSpeed, "Walk Speed", "m/s");
			AppendStatLine(stringBuilder, player, CharacterStatType.CrouchSpeed, "Crouch Speed", "m/s");
			AppendStatLine(stringBuilder, player, CharacterStatType.SwimSpeed, "Swim Speed", "m/s");
			AppendStatLineIfModified(stringBuilder, player, CharacterStatType.JumpHeight, "Jump Apex Height", "m", 1.7f);
			AppendStatLineIfModified(stringBuilder, player, CharacterStatType.Concealment, "Stealth Concealment Rating", "x", 1f);
			AppendStatLineIfModified(stringBuilder, player, CharacterStatType.RidingSpeed, "Mount Run Speed Factor", "x", 1f);
			AppendStatLineIfModified(stringBuilder, player, CharacterStatType.RidingControl, "Mount Steering Obedience", "%", 0f);
			AppendStatLineIfModified(stringBuilder, player, CharacterStatType.ShipSailSpeed, "Ship Sail Speed (Wind)", "x", 1f);
			AppendStatLineIfModified(stringBuilder, player, CharacterStatType.ShipRudderSpeed, "Ship Rudder Speed (Paddle)", "x", 1f);
			AppendStatLineIfModified(stringBuilder, player, CharacterStatType.CartWeightEfficiency, "Cart Weight Efficiency", "+%", 0f);
			stringBuilder.AppendLine();
			StringBuilder stringBuilder12 = new StringBuilder();
			AppendStatLineIfModified(stringBuilder12, player, CharacterStatType.HarvestRadius, "Harvest & Agriculture Radius", "x", 1f);
			AppendStatLineIfModified(stringBuilder12, player, CharacterStatType.CraftingYield, "Crafting Total Yield", "%", 1f);
			AppendStatLineIfModified(stringBuilder12, player, CharacterStatType.CookingYield, "Cooking Total Yield", "%", 1f);
			AppendStatLineIfModified(stringBuilder12, player, CharacterStatType.ForagingYield, "Foraging Total Yield", "%", 1f);
			AppendStatLineIfModified(stringBuilder12, player, CharacterStatType.MiningYield, "Mining Mineral Yield", "%", 1f);
			AppendStatLineIfModified(stringBuilder12, player, CharacterStatType.WoodcuttingYield, "Woodcutting Timber Yield", "%", 1f);
			AppendStatLineIfModified(stringBuilder12, player, CharacterStatType.MonsterDropYield, "Monster Slay Loot Yield", "%", 1f);
			if (stringBuilder12.Length > 0)
			{
				stringBuilder.AppendLine("<color=orange><b>[ GATHERING, CRAFTING & YIELDS ]</b></color>");
				stringBuilder.Append(stringBuilder12.ToString());
				stringBuilder.AppendLine();
			}
			IReadOnlyCollection<CustomStatToken> all = StatRegistry.GetAll();
			if (all.Count > 0)
			{
				StringBuilder stringBuilder13 = new StringBuilder();
				foreach (CustomStatToken item2 in all)
				{
					StatTrace trace = CharacterStatManager.GetTrace((Character?)(object)player, item2.Key, item2.DefaultValue);
					if (trace.FlatModifiers.Count > 0 || trace.IncreasedModifiers.Count > 0 || trace.Multipliers.Count > 0 || Mathf.Abs(trace.FinalValue - item2.DefaultValue) > 0.001f)
					{
						AppendStatLineWithTrace(stringBuilder13, trace, item2.Name, item2.Unit);
					}
				}
				if (stringBuilder13.Length > 0)
				{
					stringBuilder.AppendLine("<color=orange><b>[ CUSTOM & MODDED STATS ]</b></color>");
					stringBuilder.Append(stringBuilder13.ToString());
					stringBuilder.AppendLine();
				}
			}
			if (_customCategories.Count > 0)
			{
				for (int l = 0; l < _customCategories.Count; l++)
				{
					PrioritizedCategoryProvider prioritizedCategoryProvider = _customCategories[l];
					try
					{
						IEnumerable<(string, string)> enumerable = prioritizedCategoryProvider.Provider(player);
						if (enumerable == null)
						{
							continue;
						}
						StringBuilder stringBuilder14 = new StringBuilder();
						foreach (var (text2, text3) in enumerable)
						{
							if (!string.IsNullOrWhiteSpace(text2))
							{
								if (string.IsNullOrEmpty(text3))
								{
									stringBuilder14.AppendLine("  " + text2);
									continue;
								}
								stringBuilder14.AppendLine("  " + text2 + ": <color=orange>" + text3 + "</color>");
							}
						}
						if (stringBuilder14.Length > 0)
						{
							stringBuilder.AppendLine("<color=orange><b>[ " + prioritizedCategoryProvider.CategoryTitle.ToUpperInvariant() + " ]</b></color>");
							stringBuilder.Append(stringBuilder14.ToString());
							stringBuilder.AppendLine();
						}
					}
					catch (Exception ex)
					{
						ManualLogSource log = VikingLibPlugin.Log;
						if (log != null)
						{
							log.LogWarning((object)("Error rendering custom glossary category '" + prioritizedCategoryProvider.CategoryTitle + "': " + ex.Message));
						}
					}
				}
			}
			try
			{
				CharacterStatsGlossaryPanel.OnFormatCustomSections?.Invoke(player, stringBuilder);
			}
			catch (Exception arg)
			{
				ManualLogSource log2 = VikingLibPlugin.Log;
				if (log2 != null)
				{
					log2.LogError((object)$"Error in glossary custom sections hook: {arg}");
				}
			}
			return stringBuilder.ToString();
		}

		private static void AppendVelocityStatLine(StringBuilder sb, Player player, CharacterStatType statType, string label, float baseVelocity)
		{
			StatTrace trace = CharacterStatManager.GetTrace((Character?)(object)player, statType);
			float num = baseVelocity * trace.FinalValue;
			if (trace.FlatModifiers.Count <= 0 && trace.IncreasedModifiers.Count <= 0 && trace.Multipliers.Count <= 0)
			{
				sb.AppendLine($"• <b>{label}</b>: <color=yellow>{num:F1} m/s</color>");
				return;
			}
			sb.AppendLine($"• <b>{label}</b>: <color=yellow>{num:F1} m/s</color> <i>(Base: {baseVelocity:F1} m/s)</i>");
			List<string> list = new List<string>();
			foreach (var flatModifier in trace.FlatModifiers)
			{
				string item = flatModifier.Source;
				float item2 = flatModifier.Value;
				string arg = ((item2 >= 0f) ? "+" : "");
				list.Add($"[Tier 1 Flat] {item}: {arg}{item2:F1}");
			}
			foreach (var increasedModifier in trace.IncreasedModifiers)
			{
				string item3 = increasedModifier.Source;
				float item4 = increasedModifier.Value;
				string arg2 = ((item4 >= 0f) ? "+" : "");
				list.Add($"[Tier 2 Additive] {item3}: {arg2}{item4 * 100f:F1}%");
			}
			foreach (var (arg3, num2) in trace.Multipliers)
			{
				list.Add($"[Tier 3 Multiplier] {arg3}: x{num2:F2}");
			}
			for (int i = 0; i < list.Count; i++)
			{
				string text = ((i == list.Count - 1) ? "    <color=#A0A0A0>└─ " : "    <color=#A0A0A0>├─ ");
				sb.AppendLine(text + list[i] + "</color>");
			}
		}

		private static void AppendStatLine(StringBuilder sb, Player player, CharacterStatType statType, string label, string unit)
		{
			StatTrace trace = CharacterStatManager.GetTrace((Character?)(object)player, statType);
			AppendStatLineWithTrace(sb, trace, label, unit);
		}

		private static void AppendStatLineIfModified(StringBuilder sb, Player player, CharacterStatType statType, string label, string unit, float baseline, string indent = "")
		{
			StatTrace trace = CharacterStatManager.GetTrace((Character?)(object)player, statType);
			if (trace.FlatModifiers.Count > 0 || trace.IncreasedModifiers.Count > 0 || trace.Multipliers.Count > 0 || !(Mathf.Abs(trace.FinalValue - baseline) < 0.001f))
			{
				AppendStatLineWithTrace(sb, trace, label, unit, indent);
			}
		}

		private static void AppendStatLineWithTrace(StringBuilder sb, StatTrace trace, string label, string unit, string indent = "")
		{
			float finalValue = trace.FinalValue;
			bool flag = IsEfficiencyStat(trace.StatType);
			string text = unit switch
			{
				"x" => $"{finalValue:F2}x", 
				"%" => $"{finalValue * 100f:F0}%", 
				"+%" => $"{(flag ? finalValue : (finalValue - 1f)) * 100f:+0.0;-0.0;0}%", 
				"m" => $"{finalValue:F2}m", 
				_ => string.IsNullOrEmpty(unit) ? $"{finalValue:F1}" : $"{finalValue:F1} {unit}", 
			};
			if (trace.FlatModifiers.Count <= 0 && trace.IncreasedModifiers.Count <= 0 && trace.Multipliers.Count <= 0)
			{
				sb.AppendLine(indent + "• <b>" + label + "</b>: <color=yellow>" + text + "</color>");
				return;
			}
			string text2 = unit switch
			{
				"x" => $"{trace.BaseValue:F2}x", 
				"%" => $"{trace.BaseValue * 100f:F0}%", 
				"+%" => "0%", 
				"m" => $"{trace.BaseValue:F2}m", 
				_ => string.IsNullOrEmpty(unit) ? $"{trace.BaseValue:F1}" : $"{trace.BaseValue:F1} {unit}", 
			};
			sb.AppendLine(indent + "• <b>" + label + "</b>: <color=yellow>" + text + "</color> <i>(Base: " + text2 + ")</i>");
			List<string> list = new List<string>();
			foreach (var flatModifier in trace.FlatModifiers)
			{
				string item = flatModifier.Source;
				float item2 = flatModifier.Value;
				string arg = ((item2 >= 0f) ? "+" : "");
				list.Add($"[Tier 1 Flat] {item}: {arg}{item2:F1}");
			}
			foreach (var increasedModifier in trace.IncreasedModifiers)
			{
				string item3 = increasedModifier.Source;
				float item4 = increasedModifier.Value;
				string arg2 = ((item4 >= 0f) ? "+" : "");
				if (flag)
				{
					list.Add($"[Tier 2 Efficiency] {item3}: {arg2}{item4 * 100f:F1}%");
				}
				else
				{
					list.Add($"[Tier 2 Additive] {item3}: {arg2}{item4 * 100f:F1}%");
				}
			}
			foreach (var (arg3, num) in trace.Multipliers)
			{
				list.Add($"[Tier 3 Multiplier] {arg3}: x{num:F2}");
			}
			for (int i = 0; i < list.Count; i++)
			{
				string text3 = ((i == list.Count - 1) ? (indent + "    <color=#A0A0A0>└─ ") : (indent + "    <color=#A0A0A0>├─ "));
				sb.AppendLine(text3 + list[i] + "</color>");
			}
		}

		private static bool IsEfficiencyStat(CharacterStatType statType)
		{
			return CharacterStatManager.IsEfficiencyStat(statType);
		}

		private static List<string> GetResistanceSources(Player player, DamageType type)
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_005f: 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_0066: Unknown result type (might be due to invalid IL or missing references)
			//IL_0068: 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_0070: 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_016a: Unknown result type (might be due to invalid IL or missing references)
			//IL_016f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0171: 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_0178: Unknown result type (might be due to invalid IL or missing references)
			//IL_017b: Unknown result type (might be due to invalid IL or missing references)
			//IL_017d: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d0: 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_01c2: 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_01df: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e1: Unknown result type (might be due to invalid IL or missing references)
			List<string> list = new List<string>();
			DamageType val = ToHitDataType(type);
			List<ItemData> equippedItems = CharacterStatsHelper.GetEquippedItems((Humanoid?)(object)player);
			for (int i = 0; i < equippedItems.Count; i++)
			{
				ItemData val2 = equippedItems[i];
				if (val2?.m_shared?.m_damageModifiers == null)
				{
					continue;
				}
				foreach (DamageModPair damageModifier in val2.m_shared.m_damageModifiers)
				{
					if (damageModifier.m_type == val && (int)damageModifier.m_modifier != 0)
					{
						string text = val2.m_shared.m_name ?? "Gear";
						if (text.StartsWith("$") && Localization.instance != null)
						{
							text = Localization.instance.Localize(text);
						}
						float num = EntityDefenseRegistry.ToFloatMultiplier(damageModifier.m_modifier);
						list.Add($"{text}: x{num:F2} ({damageModifier.m_modifier})");
					}
				}
			}
			SEMan sEMan = ((Character)player).GetSEMan();
			if (sEMan != null)
			{
				List<StatusEffect> statusEffects = sEMan.GetStatusEffects();
				for (int j = 0; j < statusEffects.Count; j++)
				{
					StatusEffect obj = statusEffects[j];
					SE_Stats val3 = (SE_Stats)(object)((obj is SE_Stats) ? obj : null);
					if (val3 == null || val3.m_mods == null)
					{
						continue;
					}
					foreach (DamageModPair mod in val3.m_mods)
					{
						if (mod.m_type == val && (int)mod.m_modifier != 0)
						{
							string text2 = ((StatusEffect)val3).m_name;
							if (string.IsNullOrEmpty(text2))
							{
								text2 = ((Object)val3).name;
							}
							if (text2.StartsWith("$") && Localization.instance != null)
							{
								text2 = Localization.instance.Localize(text2);
							}
							float num2 = EntityDefenseRegistry.ToFloatMultiplier(mod.m_modifier);
							list.Add($"{text2}: x{num2:F2} ({mod.m_modifier})");
						}
					}
				}
			}
			if (list.Count == 0)
			{
				list.Add("Innate Trait");
			}
			return list;
		}

		private static DamageType ToHitDataType(DamageType type)
		{
			//IL_0031: 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_0039: 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_0042: 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_004c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: 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_006a: 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)
			return (DamageType)(type switch
			{
				DamageType.Blunt => 1, 
				DamageType.Slash => 2, 
				DamageType.Pierce => 4, 
				DamageType.Chop => 8, 
				DamageType.Pickaxe => 16, 
				DamageType.Fire => 32, 
				DamageType.Frost => 64, 
				DamageType.Lightning => 128, 
				DamageType.Poison => 256, 
				DamageType.Spirit => 512, 
				_ => 31, 
			});
		}
	}
	[HarmonyPatch]
	public static class EnemyStaggerBar
	{
		private static readonly Dictionary<Character, GuiBar> _bars = new Dictionary<Character, GuiBar>();

		private static FieldInfo? _mHudsField;

		private static FieldInfo? _mHealthFastField;

		private static FieldInfo? _mNameField;

		private static readonly Color AmberColor = new Color(0.96f, 0.62f, 0.04f);

		private static bool EnsureReflected()
		{
			if (_mHudsField == null)
			{
				_mHudsField = AccessTools.Field(typeof(EnemyHud), "m_huds");
			}
			if (_mHealthFastField == null)
			{
				Type type = AccessTools.Inner(typeof(EnemyHud), "HudData");
				if (type != null)
				{
					_mHealthFastField = AccessTools.Field(type, "m_healthFast");
				}
			}
			if (_mNameField == null)
			{
				Type type2 = AccessTools.Inner(typeof(EnemyHud), "HudData");
				if (type2 != null)
				{
					_mNameField = AccessTools.Field(type2, "m_name");
				}
			}
			if (_mHudsField != null)
			{
				return _mHealthFastField != null;
			}
			return false;
		}

		[HarmonyPatch(typeof(EnemyHud), "ShowHud")]
		[HarmonyPostfix]
		public static void ShowHudPostfix(EnemyHud __instance, Character c, bool isMount)
		{
			//IL_00d2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d8: Expected O, but got Unknown
			//IL_0124: Unknown result type (might be due to invalid IL or missing references)
			//IL_013f: Unknown result type (might be due to invalid IL or missing references)
			//IL_014b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0159: Unknown result type (might be due to invalid IL or missing references)
			//IL_0165: Unknown result type (might be due to invalid IL or missing references)
			//IL_0178: Unknown result type (might be due to invalid IL or missing references)
			//IL_01bf: Unknown result type (might be due to invalid IL or missing references)
			if (!ModConfig.ModEnabled.Value || !ModConfig.StaggerOverhaulEnabled.Value || (Object)(object)c == (Object)null || (Object)(object)__instance == (Object)null || ModConfig.EnemyStaggerBarMode.Value == StaggerBarDisplayMode.Off || (ModConfig.EnemyStaggerBarMode.Value == StaggerBarDisplayMode.BossesOnly && !c.IsBoss()) || !EnsureReflected())
			{
				return;
			}
			if (_bars.TryGetValue(c, out GuiBar value) && (Object)(object)value != (Object)null)
			{
				value.SetMaxValue(1f);
				value.m_smoothDrain = false;
				value.m_smoothFill = false;
				value.m_changeDelay = 0f;
				return;
			}
			try
			{
				IDictionary dictionary = (IDictionary)_mHudsField.GetValue(__instance);
				if (dictionary == null || !dictionary.Contains(c))
				{
					return;
				}
				object obj = dictionary[c];
				if (obj == null)
				{
					return;
				}
				GuiBar val = (GuiBar)_mHealthFastField.GetValue(obj);
				if (!((Object)(object)val == (Object)null))
				{
					GameObject val2 = Object.Instantiate<GameObject>(((Component)val).gameObject, ((Component)val).transform.parent);
					((Object)val2).name = "vikinglib_stagger_bar";
					RectTransform component = val2.GetComponent<RectTransform>();
					if ((Object)(object)component != (Object)null)
					{
						float num = Mathf.Max(2.5f, component.sizeDelta.y * 0.35f);
						component.sizeDelta = new Vector2(component.sizeDelta.x, num);
						component.anchoredPosition = new Vector2(component.anchoredPosition.x, component.anchoredPosition.y - num - 1.5f);
					}
					GuiBar component2 = val2.GetComponent<GuiBar>();
					if ((Object)(object)component2 != (Object)null)
					{
						component2.SetMaxValue(1f);
						component2.m_smoothDrain = false;
						component2.m_smoothFill = false;
						component2.m_changeDelay = 0f;
						component2.SetColor(AmberColor);
						component2.SetValue(0f);
						val2.SetActive(false);
						_bars[c] = component2;
					}
				}
			}
			catch (Exception ex)
			{
				ManualLogSource log = VikingLibPlugin.Log;
				if (log != null)
				{
					log.LogWarning((object)("[VikingLib EnemyStaggerBar] Failed to attach stagger bar for '" + ((Object)c).name + "': " + ex.Message));
				}
			}
		}

		[HarmonyPatch(typeof(EnemyHud), "UpdateHuds")]
		[HarmonyPostfix]
		public static void UpdateHudsPostfix(EnemyHud __instance)
		{
			//IL_01f9: Unknown result type (might be due to invalid IL or missing references)
			//IL_012c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0131: 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_01c2: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c9: Expected O, but got Unknown
			if (!ModConfig.ModEnabled.Value || !ModConfig.StaggerOverhaulEnabled.Value || (Object)(object)__instance == (Object)null || _bars.Count == 0)
			{
				return;
			}
			StaggerBarDisplayMode value = ModConfig.EnemyStaggerBarMode.Value;
			List<Character> list = new List<Character>();
			foreach (KeyValuePair<Character, GuiBar> bar in _bars)
			{
				Character key = bar.Key;
				GuiBar value2 = bar.Value;
				if ((Object)(object)key == (Object)null)
				{
					continue;
				}
				if ((Object)(object)value2 == (Object)null)
				{
					list.Add(key);
					continue;
				}
				if (value == StaggerBarDisplayMode.Off || (value == StaggerBarDisplayMode.BossesOnly && !key.IsBoss()))
				{
					if (((Component)value2).gameObject.activeSelf)
					{
						((Component)value2).gameObject.SetActive(false);
					}
					continue;
				}
				float poisePercentage = StaggerManager.GetPoisePercentage(key);
				bool flag = key.IsStaggering() || StaggerManager.IsStaggering(key);
				if (poisePercentage > 0.001f || flag)
				{
					if (!((Component)value2).gameObject.activeSelf)
					{
						((Component)value2).gameObject.SetActive(true);
					}
					if (flag)
					{
						float num = Mathf.PingPong(Time.time * 6f, 1f);
						value2.SetColor(Color.Lerp(AmberColor, Color.white, num));
						float staggerFraction = StaggerManager.GetStaggerFraction(key);
						value2.SetValue(staggerFraction);
						if (!key.IsBoss() || !(_mHudsField != null))
						{
							continue;
						}
						IDictionary dictionary = (IDictionary)_mHudsField.GetValue(__instance);
						if (dictionary == null || !dictionary.Contains(key))
						{
							continue;
						}
						object obj = dictionary[key];
						if (obj != null && _mNameField != null)
						{
							TextMeshProUGUI val = (TextMeshProUGUI)_mNameField.GetValue(obj);
							if ((Object)(object)val != (Object)null)
							{
								((TMP_Text)val).text = Localization.instance.Localize(key.GetHoverName()) + " <color=#F59E0B>[STAGGERED]</color>";
							}
						}
					}
					else
					{
						value2.SetColor(AmberColor);
						value2.SetValue(poisePercentage);
					}
				}
				else if (((Component)value2).gameObject.activeSelf)
				{
					((Component)value2).gameObject.SetActive(false);
				}
			}
			for (int i = 0; i < list.Count; i++)
			{
				_bars.Remove(list[i]);
			}
		}

		[HarmonyPatch(typeof(EnemyHud), "RemoveCharacterHud")]
		[HarmonyPostfix]
		public static void RemoveCharacterHudPostfix(Character character)
		{
			if ((Object)(object)character != (Object)null)
			{
				_bars.Remove(character);
			}
		}
	}
	public struct StatusEffectDisplayData
	{
		public string EffectName;

		public string DisplayText;

		public string FallbackSymbol;

		public Sprite? Icon;

		public Color AccentColor;

		public Color BackgroundColor;
	}
	[HarmonyPatch]
	public static class EnemyStatusEffectsHud
	{
		private sealed class StatusEffectChip
		{
			public GameObject Root { get; }

			public RectTransform Rect { get; }

			public Image BackgroundImage { get; }

			public Image IconImage { get; }

			public TextMeshProUGUI ValueText { get; }

			public StatusEffectChip(GameObject root, RectTransform rect, Image bg, Image icon, TextMeshProUGUI text)
			{
				Root = root;
				Rect = rect;
				BackgroundImage = bg;
				IconImage = icon;
				ValueText = text;
			}

			public void Setup(StatusEffectDisplayData data)
			{
				//IL_0007: 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_0069: Unknown result type (might be due to invalid IL or missing references)
				//IL_00cd: Unknown result type (might be due to invalid IL or missing references)
				((Graphic)BackgroundImage).color = data.BackgroundColor;
				if ((Object)(object)data.Icon != (Object)null)
				{
					((Component)IconImage).gameObject.SetActive(true);
					IconImage.sprite = data.Icon;
					((Graphic)IconImage).color = Color.white;
					((TMP_Text)ValueText).text = data.DisplayText;
					((Graphic)ValueText).color = data.AccentColor;
				}
				else
				{
					((Component)IconImage).gameObject.SetActive(false);
					if (string.IsNullOrEmpty(data.DisplayText))
					{
						((TMP_Text)ValueText).text = data.FallbackSymbol;
					}
					else
					{
						((TMP_Text)ValueText).text = data.FallbackSymbol + " " + data.DisplayText;
					}
					((Graphic)ValueText).color = data.AccentColor;
				}
			}
		}

		private sealed class StatusEffectsHudContainer
		{
			public GameObject Root { get; }

			public RectTransform Rect { get; }

			public List<StatusEffectChip> Chips { get; } = new List<StatusEffectChip>();

			public TMP_FontAsset? Font { get; set; }

			public StatusEffectsHudContainer(GameObject root, RectTransform rect, TMP_FontAsset? font)
			{
				Root = root;
				Rect = rect;
				Font = font;
			}
		}

		private static readonly Dictionary<Character, StatusEffectsHudContainer> _containers = new Dictionary<Character, StatusEffectsHudContainer>();

		private static FieldInfo? _mHudsField;

		private static FieldInfo? _mHealthFastField;

		private static FieldInfo? _mHealthTextField;

		private static FieldInfo? _mNameField;

		private static Sprite? _chipBgSprite;

		private static bool EnsureReflected()
		{
			if (_mHudsField == null)
			{
				_mHudsField = AccessTools.Field(typeof(EnemyHud), "m_huds");
			}
			if (_mHealthFastField == null)
			{
				Type type = AccessTools.Inner(typeof(EnemyHud), "HudData");
				if (type != null)
				{
					_mHealthFastField = AccessTools.Field(type, "m_healthFast");
					_mHealthTextField = AccessTools.Field(type, "m_healthText");
					_mNameField = AccessTools.Field(type, "m_name");
				}
			}
			if (_mHudsField != null)
			{
				return _mHealthFastField != null;
			}
			return false;
		}

		private static Sprite GetChipBackgroundSprite()
		{
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Expected O, but got Unknown
			//IL_00f5: Unknown result type (might be due to invalid IL or missing references)
			//IL_0104: 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_00b8: Unknown result type (might be due to invalid IL or missing references)
			//IL_0089: 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)
			//IL_0071: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)_chipBgSprite != (Object)null)
			{
				return _chipBgSprite;
			}
			try
			{
				Texture2D val = new Texture2D(16, 16, (TextureFormat)4, false);
				for (int i = 0; i < 16; i++)
				{
					for (int j = 0; j < 16; j++)
					{
						float num = Mathf.Min(j, 15 - j);
						float num2 = Mathf.Min(i, 15 - i);
						float num3 = 0f;
						if (num < 3f && num2 < 3f)
						{
							num3 = Vector2.Distance(new Vector2(num, num2), new Vector2(3f, 3f));
						}
						if (num3 > 3f)
						{
							val.SetPixel(j, i, Color.clear);
							continue;
						}
						float num4 = Mathf.Clamp01(3.5f - num3);
						val.SetPixel(j, i, new Color(1f, 1f, 1f, num4));
					}
				}
				val.Apply();
				_chipBgSprite = Sprite.Create(val, new Rect(0f, 0f, 16f, 16f), new Vector2(0.5f, 0.5f), 100f, 0u, (SpriteMeshType)0, new Vector4(4f, 4f, 4f, 4f));
			}
			catch
			{
				_chipBgSprite = null;
			}
			return _chipBgSprite;
		}

		public static bool TryGetDisplayData(StatusEffect se, bool showText, out StatusEffectDisplayData data)
		{
			//IL_00aa: 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_00ca: 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_0144: Unknown result type (might be due to invalid IL or missing references)
			//IL_0149: Unknown result type (might be due to invalid IL or missing references)
			//IL_0164: Unknown result type (might be due to invalid IL or missing references)
			//IL_0169: Unknown result type (might be due to invalid IL or missing references)
			//IL_04d7: Unknown result type (might be due to invalid IL or missing references)
			//IL_04dc: Unknown result type (might be due to invalid IL or missing references)
			//IL_04f7: Unknown result type (might be due to invalid IL or missing references)
			//IL_04fc: 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)
			//IL_01e3: Unknown result type (might be due to invalid IL or missing references)
			//IL_01fe: Unknown result type (might be due to invalid IL or missing references)
			//IL_0203: Unknown result type (might be due to invalid IL or missing references)
			//IL_0593: Unknown result type (might be due to invalid IL or missing references)
			//IL_0598: Unknown result type (might be due to invalid IL or missing references)
			//IL_05b3: Unknown result type (might be due to invalid IL or missing references)
			//IL_05b8: Unknown result type (might be due to invalid IL or missing references)
			//IL_0278: Unknown result type (might be due to invalid IL or missing references)
			//IL_027d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0298: Unknown result type (might be due to invalid IL or missing references)
			//IL_029d: Unknown result type (might be due to invalid IL or missing references)
			//IL_064f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0654: Unknown result type (might be due to invalid IL or missing references)
			//IL_066f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0674: Unknown result type (might be due to invalid IL or missing references)
			//IL_0312: Unknown result type (might be due to invalid IL or missing references)
			//IL_0317: Unknown result type (might be due to invalid IL or missing references)
			//IL_0332: Unknown result type (might be due to invalid IL or missing references)
			//IL_0337: Unknown result type (might be due to invalid IL or missing references)
			//IL_03ef: Unknown result type (might be due to invalid IL or missing references)
			//IL_03f4: Unknown result type (might be due to invalid IL or missing references)
			//IL_040f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0414: Unknown result type (might be due to invalid IL or missing references)
			//IL_0722: Unknown result type (might be due to invalid IL or missing references)
			//IL_0727: Unknown result type (might be due to invalid IL or missing references)
			//IL_0742: Unknown result type (might be due to invalid IL or missing references)
			//IL_0747: Unknown result type (might be due to invalid IL or missing references)
			data = default(StatusEffectDisplayData);
			if (se == null || se.m_hidden || se.IsDone())
			{
				return false;
			}
			if (se is VikingStatusEffect vikingStatusEffect)
			{
				string name = vikingStatusEffect.Definition.Name;
				if (string.Equals(name, "SE_Bleed", StringComparison.OrdinalIgnoreCase))
				{
					data = new StatusEffectDisplayData
					{
						EffectName = "Bleed",
						DisplayText = (showText ? vikingStatusEffect.TotalSliceValue.ToString("F1") : string.Empty),
						FallbackSymbol = "\ud83e\ude78",
						Icon = ((StatusEffect)vikingStatusEffect).m_icon,
						AccentColor = new Color(0.94f, 0.27f, 0.27f),
						BackgroundColor = new Color(0.18f, 0.05f, 0.05f, 0.88f)
					};
					return true;
				}
				if (string.Equals(name, "SE_Vulnerable", StringComparison.OrdinalIgnoreCase))
				{
					data = new StatusEffectDisplayData
					{
						EffectName = "Vulnerable",
						DisplayText = (showText ? ((StatusEffect)vikingStatusEffect).GetIconText() : string.Empty),
						FallbackSymbol = "⚔",
						Icon = ((StatusEffect)vikingStatusEffect).m_icon,
						AccentColor = new Color(0.75f, 0.52f, 0.99f),
						BackgroundColor = new Color(0.12f, 0.04f, 0.18f, 0.88f)
					};
					return true;
				}
				if (string.Equals(name, "SE_Cripple", StringComparison.OrdinalIgnoreCase))
				{
					data = new StatusEffectDisplayData
					{
						EffectName = "Crippled",
						DisplayText = (showText ? ((StatusEffect)vikingStatusEffect).GetIconText() : string.Empty),
						FallbackSymbol = "\ud83e\uddb5",
						Icon = ((StatusEffect)vikingStatusEffect).m_icon,
						AccentColor = new Color(0.96f, 0.62f, 0.04f),
						BackgroundColor = new Color(0.18f, 0.11f, 0.03f, 0.88f)
					};
					return true;
				}
				if (string.Equals(name, "SE_Ward", StringComparison.OrdinalIgnoreCase))
				{
					data = new StatusEffectDisplayData
					{
						EffectName = "Ward",
						DisplayText = (showText ? ((StatusEffect)vikingStatusEffect).GetIconText() : string.Empty),
						FallbackSymbol = "\ud83d\udee1",
						Icon = ((StatusEffect)vikingStatusEffect).m_icon,
						AccentColor = new Color(0.22f, 0.74f, 0.97f),
						BackgroundColor = new Color(0.03f, 0.18f, 0.29f, 0.88f)
					};
					return true;
				}
				if (string.Equals(name, "SE_EmpoweredStrike", StringComparison.OrdinalIgnoreCase))
				{
					data = new StatusEffectDisplayData
					{
						EffectName = "Empowered",
						DisplayText = (showText ? ((StatusEffect)vikingStatusEffect).GetIconText() : string.Empty),
						FallbackSymbol = "⚡",
						Icon = ((StatusEffect)vikingStatusEffect).m_icon,
						AccentColor = new Color(0.98f, 0.75f, 0.14f),
						BackgroundColor = new Color(0.15f, 0.12f, 0.03f, 0.88f)
					};
					return true;
				}
				string text = ((StatusEffect)vikingStatusEffect).GetIconText();
				if (string.IsNullOrEmpty(text) && ((StatusEffect)vikingStatusEffect).m_ttl > 0f)
				{
					text = $"{Mathf.CeilToInt(((StatusEffect)vikingStatusEffect).GetRemaningTime())}s";
				}
				data = new StatusEffectDisplayData
				{
					EffectName = vikingStatusEffect.Definition.DisplayName,
					DisplayText = (showText ? text : string.Empty),
					FallbackSymbol = ((name.Length >= 3) ? name.Substring(0, 3).ToUpper() : "FX"),
					Icon = ((StatusEffect)vikingStatusEffect).m_icon,
					AccentColor = new Color(0.89f, 0.91f, 0.94f),
					BackgroundColor = new Color(0.09f, 0.09f, 0.11f, 0.88f)
				};
				return true;
			}
			string text2 = ((!string.IsNullOrEmpty(se.m_name)) ? se.m_name : (((Object)se).name ?? string.Empty));
			string text3 = text2.ToLowerInvariant();
			if (text3.IndexOf("burn", StringComparison.OrdinalIgnoreCase) >= 0)
			{
				data = new StatusEffectDisplayData
				{
					EffectName = "Burning",
					DisplayText = ((showText && se.m_ttl > 0f) ? $"{Mathf.CeilToInt(se.GetRemaningTime())}s" : string.Empty),
					FallbackSymbol = "\ud83d\udd25",
					Icon = se.m_icon,
					AccentColor = new Color(0.98f, 0.45f, 0.09f),
					BackgroundColor = new Color(0.18f, 0.08f, 0.03f, 0.88f)
				};
				return true;
			}
			if (text3.IndexOf("poison", StringComparison.OrdinalIgnoreCase) >= 0)
			{
				data = new StatusEffectDisplayData
				{
					EffectName = "Poison",
					DisplayText = ((showText && se.m_ttl > 0f) ? $"{Mathf.CeilToInt(se.GetRemaningTime())}s" : string.Empty),
					FallbackSymbol = "☠",
					Icon = se.m_icon,
					AccentColor = new Color(0.13f, 0.77f, 0.37f),
					BackgroundColor = new Color(0.02f, 0.13f, 0.05f, 0.88f)
				};
				return true;
			}
			if (text3.IndexOf("frost", StringComparison.OrdinalIgnoreCase) >= 0)
			{
				data = new StatusEffectDisplayData
				{
					EffectName = "Frost",
					DisplayText = ((showText && se.m_ttl > 0f) ? $"{Mathf.CeilToInt(se.GetRemaningTime())}s" : string.Empty),
					FallbackSymbol = "❄",
					Icon = se.m_icon,
					AccentColor = new Color(0.22f, 0.74f, 0.97f),
					BackgroundColor = new Color(0.02f, 0.1f, 0.15f, 0.88f)
				};
				return true;
			}
			data = new StatusEffectDisplayData
			{
				EffectName = ((!string.IsNullOrEmpty(se.m_name)) ? se.m_name : text2),
				DisplayText = ((showText && se.m_ttl > 0f) ? $"{Mathf.CeilToInt(se.GetRemaningTime())}s" : string.Empty),
				FallbackSymbol = ((text2.Length >= 3) ? text2.Substring(0, 3).ToUpper() : "FX"),
				Icon = se.m_icon,
				AccentColor = new Color(0.89f, 0.91f, 0.94f),
				BackgroundColor = new Color(0.09f, 0.09f, 0.11f, 0.88f)
			};
			return true;
		}

		[HarmonyPatch(typeof(EnemyHud), "ShowHud")]
		[HarmonyPostfix]
		public static void ShowHudPostfix(EnemyHud __instance, Character c, bool isMount)
		{
			//IL_00ad: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b3: Expected O, but got Unknown
			//IL_00f3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fa: Expected O, but got Unknown
			//IL_014e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0153: Unknown result type (might be due to invalid IL or missing references)
			//IL_016a: Unknown result type (might be due to invalid IL or missing references)
			//IL_017e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0194: 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_01b8: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c7: Unknown result type (might be due to invalid IL or missing references)
			//IL_01dc: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e8: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f5: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ff: Unknown result type (might be due to invalid IL or missing references)
			//IL_0232: Unknown result type (might be due to invalid IL or missing references)
			//IL_023c: Expected O, but got Unknown
			//IL_012f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0136: Expected O, but got Unknown
			if (!ModConfig.ModEnabled.Value || (Object)(object)c == (Object)null || (Object)(object)__instance == (Object)null || ModConfig.EnemyStatusEffectsMode.Value == StaggerBarDisplayMode.Off || (ModConfig.EnemyStatusEffectsMode.Value == StaggerBarDisplayMode.BossesOnly && !c.IsBoss()) || !EnsureReflected() || (_containers.TryGetValue(c, out StatusEffectsHudContainer value) && (Object)(object)value?.Root != (Object)null))
			{
				return;
			}
			try
			{
				IDictionary dictionary = (IDictionary)_mHudsField.GetValue(__instance);
				if (dictionary == null || !dictionary.Contains(c))
				{
					return;
				}
				object obj = dictionary[c];
				if (obj == null)
				{
					return;
				}
				GuiBar val = (GuiBar)_mHealthFastField.GetValue(obj);
				if ((Object)(object)val == (Object)null)
				{
					return;
				}
				RectTransform component = ((Component)val).GetComponent<RectTransform>();
				if ((Object)(object)component == (Object)null)
				{
					return;
				}
				TMP_FontAsset val2 = null;
				if (_mHealthTextField != null)
				{
					TextMeshProUGUI val3 = (TextMeshProUGUI)_mHealthTextField.GetValue(obj);
					if ((Object)(object)val3 != (Object)null)
					{
						val2 = ((TMP_Text)val3).font;
					}
				}
				if ((Object)(object)val2 == (Object)null && _mNameField != null)
				{
					TextMeshProUGUI val4 = (TextMeshProUGUI)_mNameField.GetValue(obj);
					if ((Object)(object)val4 != (Object)null)
					{
						val2 = ((TMP_Text)val4).font;
					}
				}
				GameObject val5 = new GameObject("vikinglib_status_effects_root");
				val5.transform.SetParent(((Component)val).transform.parent, false);
				RectTransform val6 = val5.AddComponent<RectTransform>();
				val6.anchorMin = new Vector2(0.5f, 0f);
				val6.anchorMax = new Vector2(0.5f, 0f);
				val6.pivot = new Vector2(0.5f, 1f);
				val6.sizeDelta = new Vector2(component.sizeDelta.x, 18f);
				float num = 13f;
				val6.anchoredPosition = new Vector2(component.anchoredPosition.x, component.anchoredPosition.y - num);
				HorizontalLayoutGroup obj2 = val5.AddComponent<HorizontalLayoutGroup>();
				((LayoutGroup)obj2).childAlignment = (TextAnchor)4;
				((HorizontalOrVerticalLayoutGroup)obj2).spacing = 3f;
				((HorizontalOrVerticalLayoutGroup)obj2).childControlWidth = false;
				((HorizontalOrVerticalLayoutGroup)obj2).childControlHeight = false;
				((HorizontalOrVerticalLayoutGroup)obj2).childForceExpandWidth = false;
				((HorizontalOrVerticalLayoutGroup)obj2).childForceExpandHeight = false;
				StatusEffectsHudContainer value2 = new StatusEffectsHudContainer(val5, val6, val2);
				val5.SetActive(false);
				_containers[c] = value2;
			}
			catch (Exception ex)
			{
				ManualLogSource log = VikingLibPlugin.Log;
				if (log != null)
				{
					log.LogWarning((object)("[VikingLib EnemyStatusEffectsHud] Failed to attach status effect container for '" + ((Object)c).name + "': " + ex.Message));
				}
			}
		}

		private static StatusEffectChip CreateChip(StatusEffectsHudContainer container)
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Expected O, but got Unknown
			//IL_0034: 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_008a: Expected O, but got Unknown
			//IL_00bc: 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_00d3: 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_0117: Unknown result type (might be due to invalid IL or missing references)
			//IL_011c: Unknown result type (might be due to invalid IL or missing references)
			//IL_012e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0135: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = new GameObject("vikinglib_status_chip");
			val.transform.SetParent(container.Root.transform, false);
			RectTransform val2 = val.AddComponent<RectTransform>();
			val2.pivot = new Vector2(0.5f, 0.5f);
			Image val3 = val.AddComponent<Image>();
			Sprite chipBackgroundSprite = GetChipBackgroundSprite();
			if ((Object)(object)chipBackgroundSprite != (Object)null)
			{
				val3.sprite = chipBackgroundSprite;
				val3.type = (Type)1;
			}
			ContentSizeFitter obj = val.AddComponent<ContentSizeFitter>();
			obj.horizontalFit = (FitMode)2;
			obj.verticalFit = (FitMode)2;
			HorizontalLayoutGroup obj2 = val.AddComponent<HorizontalLayoutGroup>();
			((LayoutGroup)obj2).padding = new RectOffset(4, 4, 1, 1);
			((HorizontalOrVerticalLayoutGroup)obj2).spacing = 2f;
			((LayoutGroup)obj2).childAlignment = (TextAnchor)4;
			((HorizontalOrVerticalLayoutGroup)obj2).childControlWidth = true;
			((HorizontalOrVerticalLayoutGroup)obj2).childControlHeight = true;
			((HorizontalOrVerticalLayoutGroup)obj2).childForceExpandWidth = false;
			((HorizontalOrVerticalLayoutGroup)obj2).childForceExpandHeight = false;
			GameObject val4 = new GameObject("Icon");
			val4.transform.SetParent(val.transform, false);
			val4.AddComponent<RectTransform>();
			Image icon = val4.AddComponent<Image>();
			LayoutElement obj3 = val4.AddComponent<LayoutElement>();
			obj3.preferredWidth = 13f;
			obj3.preferredHeight = 13f;
			obj3.minWidth = 13f;
			obj3.minHeight = 13f;
			GameObject val5 = new GameObject("Text");
			val5.transform.SetParent(val.transform, false);
			val5.AddComponent<RectTransform>();
			TextMeshProUGUI val6 = val5.AddComponent<TextMeshProUGUI>();
			if ((Object)(object)container.Font != (Object)null)
			{
				((TMP_Text)val6).font = container.Font;
			}
			((TMP_Text)val6).fontSize = 10f;
			((TMP_Text)val6).fontStyle = (FontStyles)1;
			((TMP_Text)val6).alignment = (TextAlignmentOptions)514;
			ContentSizeFitter obj4 = val5.AddComponent<ContentSizeFitter>();
			obj4.horizontalFit = (FitMode)2;
			obj4.verticalFit = (FitMode)2;
			return new StatusEffectChip(val, val2, val3, icon, val6);
		}

		[HarmonyPatch(typeof(EnemyHud), "UpdateHuds")]
		[HarmonyPostfix]
		public static void UpdateHudsPostfix(EnemyHud __instance)
		{
			if (!ModConfig.ModEnabled.Value || (Object)(object)__instance == (Object)null || _containers.Count == 0)
			{
				return;
			}
			StaggerBarDisplayMode value = ModConfig.EnemyStatusEffectsMode.Value;
			bool value2 = ModConfig.EnemyStatusEffectsShowText.Value;
			List<Character> list = new List<Character>();
			foreach (KeyValuePair<Character, StatusEffectsHudContainer> container in _containers)
			{
				Character key = container.Key;
				StatusEffectsHudContainer value3 = container.Value;
				if ((Object)(object)key == (Object)null)
				{
					continue;
				}
				if (value3 == null || (Object)(object)value3.Root == (Object)null)
				{
					list.Add(key);
					continue;
				}
				if (value == StaggerBarDisplayMode.Off || (value == StaggerBarDisplayMode.BossesOnly && !key.IsBoss()))
				{
					if (value3.Root.activeSelf)
					{
						value3.Root.SetActive(false);
					}
					continue;
				}
				SEMan sEMan = key.GetSEMan();
				List<StatusEffect> list2 = ((sEMan != null) ? sEMan.GetStatusEffects() : null);
				if (list2 == null || list2.Count == 0)
				{
					if (value3.Root.activeSelf)
					{
						value3.Root.SetActive(false);
					}
					continue;
				}
				int num = 0;
				for (int i = 0; i < list2.Count; i++)
				{
					if (TryGetDisplayData(list2[i], value2, out var data))
					{
						if (num >= value3.Chips.Count)
						{
							StatusEffectChip item = CreateChip(value3);
							value3.Chips.Add(item);
						}
						StatusEffectChip statusEffectChip = value3.Chips[num];
						statusEffectChip.Setup(data);
						if (!statusEffectChip.Root.activeSelf)
						{
							statusEffectChip.Root.SetActive(true);
						}
						num++;
						if (num >= 6)
						{
							break;
						}
					}
				}
				for (int j = num; j < value3.Chips.Count; j++)
				{
					if (value3.Chips[j].Root.activeSelf)
					{
						value3.Chips[j].Root.SetActive(false);
					}
				}
				if (num > 0)
				{
					if (!value3.Root.activeSelf)
					{
						value3.Root.SetActive(true);
					}
				}
				else if (value3.Root.activeSelf)
				{
					value3.Root.SetActive(false);
				}
			}
			for (int k = 0; k < list.Count; k++)
			{
				_containers.Remove(list[k]);
			}
		}

		[HarmonyPatch(typeof(EnemyHud), "RemoveCharacterHud")]
		[HarmonyPostfix]
		public static void RemoveCharacterHudPostfix(Character character)
		{
			if ((Object)(object)character != (Object)null && _containers.TryGetValue(character, out StatusEffectsHudContainer value))
			{
				if ((Object)(object)value?.Root != (Object)null)
				{
					Object.Destroy((Object)(object)value.Root);
				}
				_containers.Remove(character);
			}
		}
	}
}
namespace VikingLib.UI.Tooltip
{
	public class ItemTooltipContext
	{
		public static ItemTooltipContext? Current { get; internal set; }

		public ItemData Item { get; }

		public int QualityLevel { get; }

		public bool Crafting { get; }

		public float WorldLevel { get; }

		public Player? Player { get; }

		public ItemTooltipContext(ItemData item, int qualityLevel, bool crafting, float worldLevel, Player? player)
		{
			Item = item ?? throw new ArgumentNullException("item");
			QualityLevel = qualityLevel;
			Crafting = crafting;
			WorldLevel = worldLevel;
			Player = player;
		}
	}
	public interface ITooltipSection
	{
		string SectionName { get; }

		int Priority { get; }

		bool ShouldRender(ItemTooltipContext context);

		string Render(ItemTooltipContext context);
	}
	public static class TooltipEngine
	{
		public delegate IEnumerable<string>? CustomTooltipLineProvider(ItemTooltipContext context);

		private sealed class PrioritizedLineProvider
		{
			public int Priority { get; }

			public CustomTooltipLineProvider Provider { get; }

			public PrioritizedLineProvider(int priority, CustomTooltipLineProvider provider)
			{
				Priority = priority;
				Provider = provider;
			}
		}

		private class AttackCardData
		{
			public string Title = "";

			public string StepKey = "";

			public Attack? NativeAttack;

			public float Cleave;

			public float Stamina;

			public float Eitr;

			public int Knockback;

			public float StaggerMultiplier;

			public string? OnHitStatusEffect;

			public float StatusEffectDuration;

			public Dictionary<DamageType, (float Min, float Max)> StepRanges = new Dictionary<DamageType, (float, float)>();
		}

		private class StepGroup
		{
			public int StartStep;

			public int EndStep;
		}

		private static readonly List<ITooltipSection> _sections = new List<ITooltipSection>();

		private static readonly List<PrioritizedLineProvider> _customLineProviders = new List<PrioritizedLineProvider>();

		public static void RegisterSection(ITooltipSection section)
		{
			if (section != null)
			{
				_sections.Add(section);
				_sections.Sort((ITooltipSection a, ITooltipSection b) => a.Priority.CompareTo(b.Priority));
			}
		}

		public static void RegisterLineProvider(CustomTooltipLineProvider provider, int priority = 100)
		{
			if (provider != null)
			{
				_customLineProviders.Add(new PrioritizedLineProvider(priority, provider));
				_customLineProviders.Sort((PrioritizedLineProvider a, PrioritizedLineProvider b) => a.Priority.CompareTo(b.Priority));
			}
		}

		public static void RegisterLineProvider(Func<ItemData?, Player?, IEnumerable<string>?> provider, int priority = 100)
		{
			if (provider != null)
			{
				RegisterLineProvider((ItemTooltipContext ctx) => provider(ctx.Item, ctx.Player), priority);
			}
		}

		public static bool IsWeaponOrTool(ItemData? item)
		{
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Invalid comparison between Unknown and I4
			//IL_0026: 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_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: Invalid comparison between Unknown and I4
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: Invalid comparison between Unknown and I4
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Invalid comparison between Unknown and I4
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			//IL_0056: Invalid comparison between Unknown and I4
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			//IL_005b: Invalid comparison between Unknown and I4
			if (item?.m_shared == null)
			{
				return false;
			}
			if ((int)item.m_shared.m_itemType == 5)
			{
				return false;
			}
			ItemType itemType = item.m_shared.m_itemType;
			if ((itemType - 3 <= 1 || (int)itemType == 14 || (int)itemType == 22) ? true : false)
			{
				return true;
			}
			itemType = item.m_shared.m_itemType;
			bool flag = (((int)itemType == 15 || (int)itemType == 19) ? true : false);
			if (flag && item.m_shared.m_attack != null && (((DamageTypes)(ref item.m_shared.m_damages)).GetTotalDamage() > 0f || item.m_shared.m_damages.m_chop > 0f || item.m_shared.m_damages.m_pickaxe > 0f))
			{
				return true;
			}
			return false;
		}

		public static bool IsExpanded()
		{
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			if (ModConfig.TooltipExpandBehavior.Value == TooltipExpandBehavior.Toggle)
			{
				return ModConfig.IsTooltipToggleActive;
			}
			return Input.GetKey(ModConfig.TooltipExpandKey.Value);
		}

		public static string FormatFullTooltip(ItemData item, int qualityLevel, bool crafting, float worldLevel)
		{
			if (item?.m_shared == null)
			{
				return "";
			}
			if (!IsExpanded())
			{
				return FormatBaseWeaponTooltip(item, qualityLevel, crafting, worldLevel);
			}
			return FormatExtendedCombatDossier(item, qualityLevel, crafting, worldLevel);
		}

		private static string FormatBaseWeaponTooltip(ItemData item, int qualityLevel, bool crafting, float worldLevel)
		{
			//IL_01d4: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d9: Unknown result type (might be due to invalid IL or missing references)
			//IL_01dd: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e5: Unknown result type (might be due to invalid IL or missing references)
			//IL_03e1: Unknown result type (might be due to invalid IL or missing references)
			StringBuilder stringBuilder = new StringBuilder();
			string description = item.m_shared.m_description;
			if (!string.IsNullOrEmpty(description))
			{
				stringBuilder.Append(LocalizeSafe(description));
				stringBuilder.Append("\n");
			}
			if (item.m_shared.m_maxQuality > 1)
			{
				stringBuilder.Append($"\n$item_quality: <color=orange>{qualityLevel}</color>");
			}
			stringBuilder.Append($"\n$item_weight: <color=orange>{item.GetWeight(-1):0.0}</color>");
			if (!string.IsNullOrEmpty(item.m_crafterName))
			{
				stringBuilder.Append("\n$item_crafter: <color=orange>" + item.m_crafterName + "</color>");
			}
			float maxDurability = item.GetMaxDurability(qualityLevel);
			if (maxDurability > 0f)
			{
				if (crafting)
				{
					stringBuilder.Append($"\n$item_durability: <color=orange>{maxDurability:0}</color>");
				}
				else
				{
					stringBuilder.Append($"\n$item_durability: <color=orange>{item.m_durability:0}/{maxDurability:0}</color>");
				}
			}
			if (item.m_shared.m_canBeReparied && !crafting && (Object)(object)ObjectDB.instance != (Object)null)
			{
				Recipe recipe = ObjectDB.instance.GetRecipe(item);
				if ((Object)(object)recipe != (Object)null && recipe.m_minStationLevel > 0)
				{
					stringBuilder.Append($"\n$item_repairlevel: <color=orange>{recipe.m_minStationLevel}</color>");
				}
			}
			float totalMin;
			if (Mathf.Abs(item.m_shared.m_movementModifier) > 0.001f)
			{
				totalMin = item.m_shared.m_movementModifier * 100f;
				string text = totalMin.ToString("+0;-0");
				stringBuilder.Append("\n$item_movement_modifier: <color=orange>" + text + "%</color>");
			}
			if ((Object)(object)item.m_shared.m_equipStatusEffect != (Object)null)
			{
				stringBuilder.Append("\n" + item.m_shared.m_equipStatusEffect.GetTooltipString());
			}
			Character localPlayerSafe = (Character)(object)GetLocalPlayerSafe();
			DamageTypes damage = item.GetDamage(qualityLevel, worldLevel);
			DamagePipeline.EvaluatePreviewDamages(localPlayerSafe, item, damage, item.m_shared.m_skillType, out Dictionary<DamageType, (float, float)> ranges, out totalMin, out float _);
			foreach (KeyValuePair<DamageType, (float, float)> item2 in ranges.Where<KeyValuePair<DamageType, (float, float)>>((KeyValuePair<DamageType, (float Min, float Max)> kvp) => kvp.Value.Min > 0.01f || kvp.Value.Max > 0.01f).ToList())
			{
				string text2 = FormatRange(item2.Value.Item1, item2.Value.Item2);
				string localizedDamageType = GetLocalizedDamageType(item2.Key);
				string damageTypeColor = GetDamageTypeColor(item2.Key);
				stringBuilder.Append("\n" + localizedDamageType + ": <color=" + damageTypeColor + ">" + text2 + "</color>");
				if (!ModConfig.StaggerOverhaulEnabled.Value)
				{
					continue;
				}
				if (item2.Key == DamageType.Slash)
				{
					float num = ModConfig.SlashBleedPercent.Value;
					if ((Object)(object)localPlayerSafe != (Object)null)
					{
						num += CharacterStatManager.GetStat(localPlayerSafe, CharacterStatType.SlashBleedPercent);
					}
					if (num > 0.001f)
					{
						float min = item2.Value.Item1 * num;
						float max = item2.Value.Item2 * num;
						string text3 = FormatRange(min, max);
						stringBuilder.Append("\n    <color=#B91C1C>Bleed: +" + text3 + "</color>");
					}
				}
				else if (item2.Key == DamageType.Blunt)
				{
					float num2 = item.m_shared.m_attack?.m_staggerMultiplier ?? 1f;
					if ((Object)(object)localPlayerSafe != (Object)null)
					{
						num2 *= CharacterStatManager.GetStat(localPlayerSafe, CharacterStatType.StaggerDamage);
					}
					float min2 = item2.Value.Item1 * num2;
					float max2 = item2.Value.Item2 * num2;
					string text4 = FormatRange(min2, max2);
					stringBuilder.Append("\n    <color=#F59E0B>Stagger: " + text4 + "</color>");
				}
			}
			string keyLabel = GetKeyLabel(ModConfig.TooltipExpandKey.Value);
			string text5 = ((ModConfig.TooltipExpandBehavior.Value == TooltipExpandBehavior.Toggle) ? "Press" : "Hold");
			stringBuilder.Append("\n\n<color=#888888>[" + keyLabel + "] " + text5 + ": Combat Details</color>");
			ItemTooltipContext context = new ItemTooltipContext(item, qualityLevel, crafting, worldLevel, GetLocalPlayerSafe());
			foreach (ITooltipSection section in _sections)
			{
				if (section.ShouldRender(context))
				{
					string text6 = section.Render(context);
					if (!string.IsNullOrEmpty(text6))
					{
						stringBuilder.Append("\n" + text6);
					}
				}
			}
			return LocalizeSafe(stringBuilder.ToString());
		}

		private static string FormatExtendedCombatDossier(ItemData item, int qualityLevel, bool crafting, float worldLevel)
		{
			//IL_0016: 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_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fa: Unknown result type (might be due to invalid IL or missing references)
			//IL_040d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0412: Unknown result type (might be due to invalid IL or missing references)
			//IL_0414: Unknown result type (might be due to invalid IL or missing references)
			//IL_0417: Invalid comparison between Unknown and I4
			//IL_0419: Unknown result type (might be due to invalid IL or missing references)
			//IL_041d: Invalid comparison between Unknown and I4
			//IL_028f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0296: Unknown result type (might be due to invalid IL or missing references)
			//IL_041f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0423: Invalid comparison between Unknown and I4
			//IL_099a: Unknown result type (might be due to invalid IL or missing references)
			//IL_09a0: Invalid comparison between Unknown and I4
			//IL_0ba3: Unknown result type (might be due to invalid IL or missing references)
			//IL_0ba9: Invalid comparison between Unknown and I4
			//IL_0b72: Unknown result type (might be due to invalid IL or missing references)
			//IL_0b78: Invalid comparison between Unknown and I4
			//IL_0bb1: Unknown result type (might be due to invalid IL or missing references)
			//IL_0bb7: Invalid comparison between Unknown and I4
			//IL_0b81: Unknown result type (might be due to invalid IL or missing references)
			//IL_0b87: Invalid comparison between Unknown and I4
			//IL_0bbf: Unknown result type (might be due to invalid IL or missing references)
			//IL_0bc6: Invalid comparison between Unknown and I4
			//IL_0dc3: Unknown result type (might be due to invalid IL or missing references)
			StringBuilder stringBuilder = new StringBuilder();
			Character localPlayerSafe = (Character)(object)GetLocalPlayerSafe();
			WeaponAttackProfile profile = WeaponAttackRegistry.GetProfile(item);
			DamageTypes damage = item.GetDamage(qualityLevel, worldLevel);
			int num = Math.Max(1, profile.PrimaryChainLength);
			List<DamageType> activeCombatTypes = GetActiveCombatTypes(damage);
			List<StepGroup> list = GroupPrimarySteps(profile, num, activeCombatTypes);
			List<AttackCardData> list2 = new List<AttackCardData>();
			float totalMax;
			float totalMin;
			foreach (StepGroup item2 in list)
			{
				string stepKey = $"primary_{item2.StartStep}";
				string title = ((item2.StartStep != item2.EndStep) ? $"Hit {item2.StartStep} - {item2.EndStep}" : ((num == 1) ? "Primary Attack" : ((item2.StartStep == num) ? $"Hit {item2.StartStep} (Combo Finisher)" : $"Hit {item2.StartStep}")));
				DamagePipeline.EvaluatePreviewDamages(localPlayerSafe, item, damage, item.m_shared.m_skillType, out Dictionary<DamageType, (float, float)> ranges, out totalMax, out totalMin, stepKey);
				Attack attack = item.m_shared.m_attack;
				float num2 = ((attack != null && attack.m_forceMultiplier > 0.01f) ? attack.m_forceMultiplier : 1f);
				float staggerMultiplier = ((attack != null && attack.m_staggerMultiplier > 0.01f) ? attack.m_staggerMultiplier : 1f);
				float num3 = attack?.m_attackStamina ?? 0f;
				float stamina = ((num3 > 0f && (Object)(object)localPlayerSafe != (Object)null) ? CharacterStatManager.GetEffectiveWeaponAttackCost(num3, localPlayerSafe, item, isSecondaryAttack: false, CharacterStatType.AttackStaminaEfficiency) : num3);
				float num4 = attack?.m_attackEitr ?? 0f;
				float eitr = ((num4 > 0f && (Object)(object)localPlayerSafe != (Object)null) ? CharacterStatManager.GetEffectiveWeaponAttackCost(num4, localPlayerSafe, item, isSecondaryAttack: false, CharacterStatType.AttackEitrEfficiency) : num4);
				AttackStepModifiers step = profile.GetStep(stepKey);
				list2.Add(new AttackCardData
				{
					Title = title,
					StepKey = stepKey,
					NativeAttack = attack,
					Cleave = step.Cleave,
					Stamina = stamina,
					Eitr = eitr,
					Knockback = Mathf.RoundToInt(item.m_shared.m_attackForce * num2),
					StaggerMultiplier = staggerMultiplier,
					OnHitStatusEffect = step.OnHitStatusEffect,
					StatusEffectDuration = step.StatusEffectDuration,
					StepRanges = ranges
				});
			}
			if (profile.HasSecondary && item.HaveSecondaryAttack())
			{
				DamagePipeline.EvaluatePreviewDamages(localPlayerSafe, item, damage, item.m_shared.m_skillType, out Dictionary<DamageType, (float, float)> ranges2, out totalMin, out totalMax, "secondary");
				Attack secondaryAttack = item.m_shared.m_secondaryAttack;
				float num5 = ((secondaryAttack.m_forceMultiplier > 0.01f) ? secondaryAttack.m_forceMultiplier : 1f);
				float staggerMultiplier2 = ((secondaryAttack.m_staggerMultiplier > 0.01f) ? secondaryAttack.m_staggerMultiplier : 1f);
				float attackStamina = secondaryAttack.m_attackStamina;
				float stamina2 = ((attackStamina > 0f && (Object)(object)localPlayerSafe != (Object)null) ? CharacterStatManager.GetEffectiveWeaponAttackCost(attackStamina, localPlayerSafe, item, isSecondaryAttack: true, CharacterStatType.AttackStaminaEfficiency) : attackStamina);
				float attackEitr = secondaryAttack.m_attackEitr;
				float eitr2 = ((attackEitr > 0f && (Object)(object)localPlayerSafe != (Object)null) ? CharacterStatManager.GetEffectiveWeaponAttackCost(attackEitr, localPlayerSafe, item, isSecondaryAttack: true, CharacterStatType.AttackEitrEfficiency) : attackEitr);
				AttackStepModifiers step2 = profile.GetStep("secondary");
				list2.Add(new AttackCardData
				{
					Title = "Secondary Attack",
					StepKey = "secondary",
					NativeAttack = secondaryAttack,
					Cleave = step2.Cleave,
					Stamina = stamina2,
					Eitr = eitr2,
					Knockback = Mathf.RoundToInt(item.m_shared.m_attackForce * num5),
					StaggerMultiplier = staggerMultiplier2,
					OnHitStatusEffect = step2.OnHitStatusEffect,
					StatusEffectDuration = step2.StatusEffectDuration,
					StepRanges = ranges2
				});
			}
			AttackCardData attackCardData = list2[0];
			stringBuilder.Append("<size=85%>");
			stringBuilder.Append("<color=#FFAA00><b>Common Properties:</b></color>");
			ItemType itemType = item.m_shared.m_itemType;
			bool flag = (((int)itemType == 4 || (int)itemType == 14 || (int)itemType == 22) ? true : false);
			string text = (flag ? "$item_twohanded" : "$item_onehanded");
			stringBuilder.Append("\nType: <color=#C0C0C0>" + text + "</color>");
			float blockPower = item.GetBlockPower((float)qualityLevel);
			float timedBlockBonus = item.m_shared.m_timedBlockBonus;
			float num6 = blockPower * timedBlockBonus;
			float backstabBonus = item.m_shared.m_backstabBonus;
			if (blockPower > 0f)
			{
				stringBuilder.Append($"\n$item_blockarmor: <color=orange>{blockPower:0}</color>");
				if (timedBlockBonus > 1f)
				{
					stringBuilder.Append($"\n$item_parrybonus: <color=orange>{timedBlockBonus:F1}x</color> (<color=yellow>{num6:0}</color>)");
				}
			}
			if (backstabBonus > 1f)
			{
				float num7 = backstabBonus / 2f;
				stringBuilder.Append($"\n$item_backstab: <color=orange>{num7:F1}x</color>");
			}
			if ((Object)(object)item.m_shared.m_equipStatusEffect != (Object)null)
			{
				stringBuilder.Append("\nBuff: <color=orange>" + item.m_shared.m_equipStatusEffect.m_name + "</color>");
			}
			if (!string.IsNullOrEmpty(attackCardData.OnHitStatusEffect))
			{
				VikingStatusEffectDefinition definition = StatusEffectRegistry.GetDefinition(attackCardData.OnHitStatusEffect);
				string arg = ((!string.IsNullOrEmpty(definition?.DisplayName)) ? definition.DisplayName : attackCardData.OnHitStatusEffect);
				stringBuilder.Append($"\nInflicts: <color=yellow>{arg}</color> ({attackCardData.StatusEffectDuration:0.#}s)");
			}
			List<KeyValuePair<DamageType, (float, float)>> list3 = attackCardData.StepRanges.Where((KeyValuePair<DamageType, (float Min, float Max)> kvp) => kvp.Value.Min > 0.01f || kvp.Value.Max > 0.01f).ToList();
			if (list3.Count > 0)
			{
				string text2 = ((num <= 1) ? "Damage" : ((list[0].EndStep > 1) ? $"Damage (Hit 1 - {list[0].EndStep})" : "Damage (Hit 1)"));
				if (list3.Count == 1)
				{
					KeyValuePair<DamageType, (float, float)> keyValuePair = list3[0];
					string text3 = FormatRange(keyValuePair.Value.Item1, keyValuePair.Value.Item2);
					string localizedDamageType = GetLocalizedDamageType(keyValuePair.Key);
					string damageTypeColor = GetDamageTypeColor(keyValuePair.Key);
					stringBuilder.Append("\n" + text2 + ": <color=" + damageTypeColor + ">" + text3 + "</color> (" + localizedDamageType + ")");
					if (ModConfig.StaggerOverhaulEnabled.Value)
					{
						if (keyValuePair.Key == DamageType.Slash)
						{
							float num8 = ModConfig.SlashBleedPercent.Value + (((Object)(object)localPlayerSafe != (Object)null) ? CharacterStatManager.GetStat(localPlayerSafe, CharacterStatType.SlashBleedPercent) : 0f);
							if (num8 > 0.001f)
							{
								string text4 = FormatRange(keyValuePair.Value.Item1 * num8, keyValuePair.Value.Item2 * num8);
								stringBuilder.Append("\n    <color=#B91C1C>Bleed: +" + text4 + "</color>");
							}
						}
						else if (keyValuePair.Key == DamageType.Blunt)
						{
							float num9 = (item.m_shared.m_attack?.m_staggerMultiplier ?? 1f) * (((Object)(object)localPlayerSafe != (Object)null) ? CharacterStatManager.GetStat(localPlayerSafe, CharacterStatType.StaggerDamage) : 1f);
							string text5 = FormatRange(keyValuePair.Value.Item1 * num9, keyValuePair.Value.Item2 * num9);
							stringBuilder.Append("\n    <color=#F59E0B>Stagger: " + text5 + "</color>");
						}
					}
				}
				else
				{
					stringBuilder.Append("\n" + text2 + ":");
					foreach (KeyValuePair<DamageType, (float, float)> item3 in list3)
					{
						string text6 = FormatRange(item3.Value.Item1, item3.Value.Item2);
						string localizedDamageType2 = GetLocalizedDamageType(item3.Key);
						string damageTypeColor2 = GetDamageTypeColor(item3.Key);
						stringBuilder.Append("\n  " + localizedDamageType2 + ": <color=" + damageTypeColor2 + ">" + text6 + "</color>");
						if (!ModConfig.StaggerOverhaulEnabled.Value)
						{
							continue;
						}
						if (item3.Key == DamageType.Slash)
						{
							float num10 = ModConfig.SlashBleedPercent.Value + (((Object)(object)localPlayerSafe != (Object)null) ? CharacterStatManager.GetStat(localPlayerSafe, CharacterStatType.SlashBleedPercent) : 0f);
							if (num10 > 0.001f)
							{
								string text7 = FormatRange(item3.Value.Item1 * num10, item3.Value.Item2 * num10);
								stringBuilder.Append("\n    <color=#B91C1C>Bleed: +" + text7 + "</color>");
							}
						}
						else if (item3.Key == DamageType.Blunt)
						{
							float num11 = (item.m_shared.m_attack?.m_staggerMultiplier ?? 1f) * (((Object)(object)localPlayerSafe != (Object)null) ? CharacterStatManager.GetStat(localPlayerSafe, CharacterStatType.StaggerDamage) : 1f);
							string text8 = FormatRange(item3.Value.Item1 * num11, item3.Value.Item2 * num11);
							stringBuilder.Append("\n    <color=#F59E0B>Stagger: " + text8 + "</color>");
						}
					}
				}
			}
			if ((int)item.m_shared.m_itemType == 4 && item.m_shared.m_attack != null)
			{
				Attack attack2 = item.m_shared.m_attack;
				if (attack2.m_drawDurationMin > 0f)
				{
					float num12 = (((Object)(object)localPlayerSafe != (Object)null) ? CharacterStatManager.GetStat(localPlayerSafe, CharacterStatType.BowDrawSpeed) : 1f);
					float num13 = attack2.m_drawDurationMin / Mathf.Max(0.01f, num12);
					stringBuilder.Append($"\nDraw Time: <color=yellow>{num13:0.0}s</color>");
				}
				if (attack2.m_drawStaminaDrain > 0f)
				{
					float num14 = (((Object)(object)localPlayerSafe != (Object)null) ? CharacterStatManager.GetEffectiveActionCost(attack2.m_drawStaminaDrain, localPlayerSafe, CharacterStatType.BowDrawStaminaEfficiency) : attack2.m_drawStaminaDrain);
					stringBuilder.Append($"\nHold Stamina: <color=yellow>{num14:0}/s</color>");
				}
				if (attack2.m_projectileVel > 0f)
				{
					stringBuilder.Append($"\nVelocity: <color=yellow>{attack2.m_projectileVel:0}</color>");
				}
			}
			Attack attack3 = item.m_shared.m_attack;
			if (atta