Decompiled source of CombatStats v1.0.0

plugins/Tenemo.CombatStats.dll

Decompiled 16 hours ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Text;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using CombatStats.Collect;
using CombatStats.Model;
using CombatStats.Net;
using CombatStats.Stats;
using CombatStats.Ui;
using HarmonyLib;
using Jotunn.Utils;
using Microsoft.CodeAnalysis;
using Muindor.Windows;
using Newtonsoft.Json;
using TMPro;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.UI;

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

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace Muindor.Windows
{
	public static class CursorRelease
	{
		public enum Step
		{
			RunVanilla,
			Release,
			SkipVanilla
		}

		public static Step Decide(bool windowOpen, bool cursorFree)
		{
			if (!windowOpen)
			{
				return Step.RunVanilla;
			}
			if (!cursorFree)
			{
				return Step.Release;
			}
			return Step.SkipVanilla;
		}
	}
}
namespace CombatStats
{
	internal sealed class ModConfig
	{
		public readonly ConfigEntry<KeyboardShortcut> CompactKey;

		public readonly ConfigEntry<KeyboardShortcut> DetailKey;

		public readonly ConfigEntry<MeterMode> CompactMode;

		public readonly ConfigEntry<int> CompactRows;

		public readonly ConfigEntry<ScreenAnchor> CompactAnchor;

		public readonly ConfigEntry<Vector2> CompactOffset;

		public readonly ConfigEntry<Vector2> DetailSize;

		public readonly ConfigEntry<float> CompactWidth;

		public readonly ConfigEntry<float> CompactScale;

		public readonly ConfigEntry<float> CompactOpacity;

		public readonly ConfigEntry<bool> CompactCaption;

		public readonly ConfigEntry<bool> CompactShare;

		public readonly ConfigEntry<bool> CompactBars;

		public readonly ConfigEntry<bool> IsDebug;

		public readonly ConfigEntry<string> Colors;

		private readonly ConfigFile config;

		private readonly SortedDictionary<string, SortedDictionary<string, string>> personal = new SortedDictionary<string, SortedDictionary<string, string>>();

		private readonly string path;

		public ModConfig(ConfigFile config)
		{
			//IL_0035: 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_00dd: Unknown result type (might be due to invalid IL or missing references)
			//IL_01bb: Unknown result type (might be due to invalid IL or missing references)
			this.config = config;
			config.SaveOnConfigSet = false;
			CompactKey = config.Bind<KeyboardShortcut>("Compact", "CompactKey", new KeyboardShortcut((KeyCode)291, Array.Empty<KeyCode>()), "Cycle compact visibility.");
			DetailKey = config.Bind<KeyboardShortcut>("Detail", "DetailKey", new KeyboardShortcut((KeyCode)292, Array.Empty<KeyCode>()), "Open encounter details.");
			CompactMode = config.Bind<MeterMode>("Compact", "CompactMode", MeterMode.Off, "Auto retains the latest encounter; Always also shows an empty meter; Off hides it. Recording is always shared.");
			CompactRows = config.Bind<int>("Compact", "CompactRows", 5, "Visible rows, including your own.");
			CompactAnchor = config.Bind<ScreenAnchor>("Compact", "CompactAnchor", ScreenAnchor.TopRight, "Screen anchor.");
			CompactOffset = config.Bind<Vector2>("Compact", "CompactOffset", new Vector2(-28f, -164f), "Offset from the anchor.");
			CompactWidth = config.Bind<float>("Compact", "CompactWidth", 360f, "Compact width.");
			CompactScale = config.Bind<float>("Compact", "CompactScale", 1f, "Compact scale.");
			CompactOpacity = config.Bind<float>("Compact", "CompactOpacity", 0.9f, "Compact background opacity.");
			CompactCaption = config.Bind<bool>("Compact", "CompactCaption", true, "Show encounter name and duration.");
			CompactShare = config.Bind<bool>("Compact", "CompactShare", true, "Show contribution percentages.");
			CompactBars = config.Bind<bool>("Compact", "CompactBars", true, "Show damage-type bars.");
			DetailSize = config.Bind<Vector2>("Detail", "DetailSize", new Vector2(700f, 480f), "Detail window size.");
			Colors = config.Bind<string>("Colours", "Colors", "", "Damage-type overrides, such as Fire=#ff7733.");
			IsDebug = config.Bind<bool>("General", "IsDebug", false, "Log transport and encounter diagnostics.");
			path = Path.Combine(Paths.BepInExRootPath, "combat-stats-state", "settings.dat");
			LoadPersonal();
			config.SettingChanged += delegate(object _, SettingChangedEventArgs change)
			{
				Remember(change.ChangedSetting.Definition, change.ChangedSetting.GetSerializedValue());
				Save();
			};
		}

		private void LoadPersonal()
		{
			//IL_00ae: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b5: Expected O, but got Unknown
			if (!File.Exists(path))
			{
				return;
			}
			string text = null;
			bool flag = false;
			string[] array = File.ReadAllLines(path);
			for (int i = 0; i < array.Length; i++)
			{
				string text2 = array[i].Trim();
				if (text2.Length == 0 || text2.StartsWith("#"))
				{
					continue;
				}
				if (text2.StartsWith("[") && text2.EndsWith("]"))
				{
					text = text2.Substring(1, text2.Length - 2);
					continue;
				}
				string[] array2 = text2.Split(new char[1] { '=' }, 2);
				if (text != null && array2.Length == 2)
				{
					ConfigDefinition val = new ConfigDefinition(text, array2[0].Trim());
					if (text == "General" && val.Key == "Enabled" && bool.TryParse(array2[1].Trim(), out var result))
					{
						flag = !result;
					}
					if (config.ContainsKey(val))
					{
						config[val].SetSerializedValue(array2[1].Trim());
						Remember(val, array2[1].Trim());
					}
				}
			}
			if (flag)
			{
				CompactMode.Value = MeterMode.Off;
				Remember(((ConfigEntryBase)CompactMode).Definition, ((ConfigEntryBase)CompactMode).GetSerializedValue());
			}
		}

		private void Remember(ConfigDefinition definition, string value)
		{
			if (!personal.TryGetValue(definition.Section, out var value2))
			{
				value2 = (personal[definition.Section] = new SortedDictionary<string, string>());
			}
			value2[definition.Key] = value;
		}

		private void Save()
		{
			StringBuilder stringBuilder = new StringBuilder("## Personal combat meter display settings. Gale does not replace this file.\n");
			foreach (KeyValuePair<string, SortedDictionary<string, string>> item in personal)
			{
				stringBuilder.Append("\n[").Append(item.Key).Append("]\n");
				foreach (KeyValuePair<string, string> item2 in item.Value)
				{
					stringBuilder.Append(item2.Key).Append(" = ").Append(item2.Value)
						.Append('\n');
				}
			}
			Storage.Write(path, stringBuilder.ToString());
		}

		public Color[] ParseColors()
		{
			//IL_0089: Unknown result type (might be due to invalid IL or missing references)
			//IL_008b: Unknown result type (might be due to invalid IL or missing references)
			Color[] array = (Color[])(object)new Color[11];
			for (int i = 0; i < array.Length; i++)
			{
				ColorUtility.TryParseHtmlString(DamageKinds.ColorHex((DamageKind)i), ref array[i]);
			}
			string[] array2 = Colors.Value.Split(',');
			Color val = default(Color);
			for (int j = 0; j < array2.Length; j++)
			{
				string[] array3 = array2[j].Split('=');
				if (array3.Length == 2 && Enum.TryParse<DamageKind>(array3[0].Trim(), ignoreCase: true, out var result) && result >= DamageKind.Blunt && (int)result < array.Length && ColorUtility.TryParseHtmlString(array3[1].Trim(), ref val))
				{
					array[(int)result] = val;
				}
			}
			return array;
		}
	}
	internal static class Storage
	{
		internal static void Write(string path, string text)
		{
			Directory.CreateDirectory(Path.GetDirectoryName(path));
			string text2 = path + ".tmp";
			File.WriteAllText(text2, text, new UTF8Encoding(encoderShouldEmitUTF8Identifier: false));
			if (File.Exists(path))
			{
				File.Replace(text2, path, null);
			}
			else
			{
				File.Move(text2, path);
			}
		}
	}
	[BepInPlugin("tenemo.CombatStats", "Combat stats", "1.0.0")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInIncompatibility("muindor.CombatStats")]
	[NetworkCompatibility(/*Could not decode attribute arguments.*/)]
	public sealed class Plugin : BaseUnityPlugin
	{
		public const string Id = "tenemo.CombatStats";

		public const string Version = "1.0.0";

		internal static ManualLogSource Log;

		internal static ModConfig Settings;

		internal static DamageCollector Collector;

		internal static CombatHistory View = new CombatHistory();

		internal static DamageChannel Channel;

		internal static bool Enabled;

		internal static bool Headless;

		private Harmony harmony;

		private void Awake()
		{
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Invalid comparison between Unknown and I4
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			//IL_0052: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			Settings = new ModConfig(((BaseUnityPlugin)this).Config);
			Headless = (int)SystemInfo.graphicsDeviceType == 4;
			Collector = new DamageCollector();
			Channel = ((Component)this).gameObject.AddComponent<DamageChannel>();
			harmony = new Harmony("tenemo.CombatStats");
			harmony.PatchAll(typeof(Plugin).Assembly);
			Enabled = true;
			if (!Headless)
			{
				((Component)this).gameObject.AddComponent<CompactMeter>();
				((Component)this).gameObject.AddComponent<DetailWindow>();
			}
			Log.LogInfo((object)"Combat stats 1.0.0 loaded: shared encounters, whole-fight DPS and effect attribution.");
		}

		private void OnDestroy()
		{
			Harmony obj = harmony;
			if (obj != null)
			{
				obj.UnpatchSelf();
			}
		}

		internal static void Debug(string text)
		{
			if (Settings.IsDebug.Value)
			{
				Log.LogInfo((object)text);
			}
		}
	}
	internal static class Shortcut
	{
		public static bool IsPressed(KeyboardShortcut shortcut)
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			if (!IsTyping())
			{
				return WentDown(shortcut);
			}
			return false;
		}

		public static bool IsTyping()
		{
			if (!Console.IsVisible() && !TextInput.IsVisible())
			{
				if ((Object)(object)Chat.instance != (Object)null)
				{
					return Chat.instance.HasFocus();
				}
				return false;
			}
			return true;
		}

		private static bool WentDown(KeyboardShortcut shortcut)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			if ((int)((KeyboardShortcut)(ref shortcut)).MainKey == 0 || !ZInput.GetKeyDown(((KeyboardShortcut)(ref shortcut)).MainKey, false))
			{
				return false;
			}
			foreach (KeyCode modifier in ((KeyboardShortcut)(ref shortcut)).Modifiers)
			{
				if (!ZInput.GetKey(modifier, false))
				{
					return false;
				}
			}
			return true;
		}
	}
}
namespace CombatStats.Patches
{
	internal static class Windows
	{
		public static bool AnyOpen
		{
			get
			{
				if (!DetailWindow.IsOpen)
				{
					return SettingsPanel.IsOpen;
				}
				return true;
			}
		}
	}
	[HarmonyPatch(typeof(Player), "TakeInput")]
	internal static class Player_TakeInput_Patch
	{
		private static void Postfix(ref bool __result)
		{
			if (Windows.AnyOpen)
			{
				__result = false;
			}
		}
	}
	[HarmonyPatch(typeof(PlayerController), "TakeInput")]
	internal static class PlayerController_TakeInput_Patch
	{
		private static void Postfix(ref bool __result)
		{
			if (Windows.AnyOpen)
			{
				__result = false;
			}
		}
	}
	[HarmonyPatch(typeof(ZInput), "GetMouseScrollWheel")]
	internal static class ZInput_GetMouseScrollWheel_Patch
	{
		private static void Postfix(ref float __result)
		{
			if (__result != 0f && DetailWindow.IsOpen)
			{
				DetailWindow.FeedScrollWheel(__result);
				__result = 0f;
			}
		}
	}
	[HarmonyPatch(typeof(GameCamera), "UpdateMouseCapture")]
	internal static class GameCamera_UpdateMouseCapture_Patch
	{
		private static bool Prefix()
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Invalid comparison between Unknown and I4
			switch (CursorRelease.Decide(Windows.AnyOpen, (int)Cursor.lockState == 0))
			{
			case CursorRelease.Step.RunVanilla:
				return true;
			case CursorRelease.Step.Release:
				ZCursor.LockState = (CursorLockMode)0;
				break;
			}
			ZCursor.Show();
			return false;
		}
	}
	[HarmonyPatch(typeof(Menu), "Update")]
	internal static class Menu_Update_Patch
	{
		private static bool Prefix()
		{
			if (!Windows.AnyOpen || Menu.IsVisible())
			{
				return true;
			}
			if (!ZInput.GetKeyDown((KeyCode)27, false))
			{
				return true;
			}
			if (SettingsPanel.IsOpen)
			{
				SettingsPanel.Instance?.Close();
				return false;
			}
			DetailWindow.Instance?.Close();
			return false;
		}
	}
}
namespace CombatStats.Collect
{
	internal sealed class DamageCollector
	{
		internal sealed class Source
		{
			internal long Id;

			internal string Name = "";

			internal string Pet = "";
		}

		internal sealed class DamageState
		{
			internal float Health;

			internal bool Valid;

			internal float[] Raw;
		}

		internal sealed class DotState
		{
			internal float Before;

			internal float Amount;

			internal int Kind;
		}

		internal sealed class RpcContext
		{
			internal Character Target;

			internal HitData Hit;
		}

		private sealed class EffectKinds
		{
			internal readonly HashSet<int> Values = new HashSet<int>();
		}

		private readonly Dictionary<string, Character> tracked = new Dictionary<string, Character>();

		private readonly ConditionalWeakTable<Character, Source> petOwners = new ConditionalWeakTable<Character, Source>();

		private readonly ConditionalWeakTable<StatusEffect, EffectKinds> effectKinds = new ConditionalWeakTable<StatusEffect, EffectKinds>();

		internal static RpcContext CurrentHit;

		internal static StatusEffect CurrentEffect;

		private static readonly FieldRef<StatusEffect, Character> EffectCharacter = AccessTools.FieldRefAccess<StatusEffect, Character>("m_character");

		private static readonly FieldRef<SE_Burning, float> FireLeft = AccessTools.FieldRefAccess<SE_Burning, float>("m_fireDamageLeft");

		private static readonly FieldRef<SE_Burning, float> SpiritLeft = AccessTools.FieldRefAccess<SE_Burning, float>("m_spiritDamageLeft");

		private static readonly FieldRef<SE_Poison, float> PoisonLeft = AccessTools.FieldRefAccess<SE_Poison, float>("m_damageLeft");

		private static readonly FieldRef<AnimalAI, Character> AnimalTarget = AccessTools.FieldRefAccess<AnimalAI, Character>("m_target");

		internal static long LocalId()
		{
			if (!Object.op_Implicit((Object)(object)Player.m_localPlayer))
			{
				return 0L;
			}
			return Player.m_localPlayer.GetPlayerID();
		}

		internal static string Key(Character character)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			return ((object)character.GetZDOID()/*cast due to .constrained prefix*/).ToString();
		}

		internal static string Name(Character character)
		{
			Player val = (Player)(object)((character is Player) ? character : null);
			if (val == null)
			{
				return character.m_name;
			}
			return val.GetPlayerName();
		}

		internal static bool Owns(Character character)
		{
			if (Plugin.Enabled && Object.op_Implicit((Object)(object)character))
			{
				return character.IsOwner();
			}
			return false;
		}

		private Source Actor(Character actor)
		{
			if (!Object.op_Implicit((Object)(object)actor))
			{
				return new Source();
			}
			Player val = (Player)(object)((actor is Player) ? actor : null);
			if (val != null)
			{
				return new Source
				{
					Id = val.GetPlayerID(),
					Name = val.GetPlayerName()
				};
			}
			if (!actor.IsTamed())
			{
				return new Source();
			}
			BaseAI baseAI = actor.GetBaseAI();
			MonsterAI val2 = (MonsterAI)(object)((baseAI is MonsterAI) ? baseAI : null);
			GameObject val3 = (Object.op_Implicit((Object)(object)val2) ? val2.GetFollowTarget() : null);
			Player val4 = (Object.op_Implicit((Object)(object)val3) ? val3.GetComponent<Player>() : null);
			if (Object.op_Implicit((Object)(object)val4))
			{
				Source source = new Source
				{
					Id = val4.GetPlayerID(),
					Name = val4.GetPlayerName(),
					Pet = Name(actor)
				};
				petOwners.Remove(actor);
				petOwners.Add(actor, source);
				return source;
			}
			if (petOwners.TryGetValue(actor, out var value))
			{
				return value;
			}
			ZNetView component = ((Component)actor).GetComponent<ZNetView>();
			string name = ((Object.op_Implicit((Object)(object)component) && component.IsValid()) ? component.GetZDO().GetString("follow", "") : "");
			Source source2 = Plugin.Channel.FindPlayer(name);
			if (source2 != null)
			{
				source2.Pet = Name(actor);
				petOwners.Add(actor, source2);
				return source2;
			}
			return new Source
			{
				Id = long.MinValue + (uint)StringExtensionMethods.GetStableHashCode(Key(actor)),
				Name = Name(actor),
				Pet = Name(actor)
			};
		}

		private Observation Create(Character target, HitData hit)
		{
			//IL_008b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0090: Unknown result type (might be due to invalid IL or missing references)
			//IL_011c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0128: Unknown result type (might be due to invalid IL or missing references)
			//IL_0134: Unknown result type (might be due to invalid IL or missing references)
			if (!Owns(target) || (!target.IsPlayer() && target.IsTamed()))
			{
				return null;
			}
			Character val = ((hit != null) ? hit.GetAttacker() : null);
			if (Object.op_Implicit((Object)(object)val) && val.IsPlayer() && target.IsPlayer())
			{
				return null;
			}
			Source source = Actor(val);
			if (!Object.op_Implicit((Object)(object)val) && hit != null && !((ZDOID)(ref hit.m_attacker)).IsNone())
			{
				Source source2 = Plugin.Channel.FindCharacter(((object)Unsafe.As<ZDOID, ZDOID>(ref hit.m_attacker)/*cast due to .constrained prefix*/).ToString());
				if (source2 != null)
				{
					source = source2;
				}
			}
			Vector3 position = ((Component)target).transform.position;
			Observation obj = new Observation
			{
				Time = DamageChannel.Now,
				Target = Key(target),
				TargetName = Name(target),
				Actor = source.Id,
				ActorName = source.Name,
				Pet = source.Pet,
				Skill = ((hit == null) ? "Other" : ((object)Unsafe.As<SkillType, SkillType>(ref hit.m_skill)/*cast due to .constrained prefix*/).ToString())
			};
			Player val2 = (Player)(object)((target is Player) ? target : null);
			obj.Victim = ((val2 != null) ? val2.GetPlayerID() : 0);
			obj.X = position.x;
			obj.Y = position.y;
			obj.Z = position.z;
			obj.Boss = target.IsBoss();
			Observation observation = obj;
			if (!target.IsPlayer())
			{
				tracked[observation.Target] = target;
			}
			else if (Object.op_Implicit((Object)(object)val) && !val.IsPlayer() && !val.IsTamed())
			{
				observation.Enemy = Key(val);
				observation.EnemyName = Name(val);
				observation.Boss = val.IsBoss();
				if (val.IsOwner())
				{
					tracked[observation.Enemy] = val;
				}
			}
			return observation;
		}

		internal void Activity(Character target, HitData hit)
		{
			if (Object.op_Implicit((Object)(object)target) && !target.IsDead() && !target.IsTeleporting() && hit != null)
			{
				Observation observation = Create(target, hit);
				if (observation != null && (observation.Actor != 0L || observation.Enemy.Length != 0))
				{
					observation.Kind = ObservationKind.Activity;
					Plugin.Channel.Queue(observation);
				}
			}
		}

		internal void TrackAttacker(Character target, HitData hit)
		{
			if (Plugin.Enabled && Object.op_Implicit((Object)(object)target) && hit != null && (target.IsPlayer() || target.IsTamed()))
			{
				Character attacker = hit.GetAttacker();
				if (Object.op_Implicit((Object)(object)attacker) && !attacker.IsPlayer() && !attacker.IsTamed() && attacker.IsOwner())
				{
					tracked[Key(attacker)] = attacker;
				}
			}
		}

		internal void Applied(Character target, HitData hit, DamageState state)
		{
			if (!state.Valid || hit == null)
			{
				return;
			}
			Observation observation = Create(target, hit);
			if (observation == null)
			{
				return;
			}
			float[] array = Values(hit);
			float num = array.Sum();
			if (num <= 0.1f)
			{
				return;
			}
			float num2 = Math.Min(num, Math.Max(0f, state.Health));
			float num3 = num2 / num;
			for (int i = 0; i < array.Length; i++)
			{
				array[i] *= num3;
			}
			observation.Kind = ObservationKind.Damage;
			observation.Amounts = array;
			observation.Overkill = num - num2;
			observation.Periodic = CurrentEffect is SE_Burning || CurrentEffect is SE_Poison;
			if (observation.Actor != 0L || observation.Victim != 0L || observation.Periodic)
			{
				if (observation.Periodic)
				{
					observation.Consumed = state.Raw;
				}
				Plugin.Channel.Queue(observation);
			}
		}

		internal void Death(Character target)
		{
			Observation observation = Create(target, null);
			if (observation != null)
			{
				observation.Kind = ObservationKind.Death;
				Plugin.Channel.Queue(observation);
				tracked.Remove(observation.Target);
			}
		}

		internal void Healed(Character target, float before)
		{
			if (!Owns(target))
			{
				return;
			}
			Player val = (Player)(object)((target is Player) ? target : null);
			if (val != null)
			{
				float num = target.GetHealth() - before;
				if (!(num <= 0f))
				{
					Observation observation = Create(target, null);
					observation.Kind = ObservationKind.Healing;
					observation.ActorName = val.GetPlayerName();
					observation.Amounts[10] = num;
					Plugin.Channel.Queue(observation);
				}
			}
		}

		internal void Engagement()
		{
			foreach (KeyValuePair<string, Character> item in tracked.ToList())
			{
				Character value = item.Value;
				if (!Object.op_Implicit((Object)(object)value) || !value.IsOwner())
				{
					tracked.Remove(item.Key);
					continue;
				}
				if (value.IsDead())
				{
					Death(value);
					continue;
				}
				BaseAI baseAI = value.GetBaseAI();
				AnimalAI val = (AnimalAI)(object)((baseAI is AnimalAI) ? baseAI : null);
				Character val2 = ((val != null) ? AnimalTarget.Invoke(val) : (Object.op_Implicit((Object)(object)baseAI) ? baseAI.GetTargetCreature() : null));
				Source source = Actor(val2);
				Observation observation = Create(value, null);
				observation.Kind = ObservationKind.Engagement;
				observation.Actor = source.Id;
				observation.ActorName = source.Name;
				observation.Engaged = Object.op_Implicit((Object)(object)baseAI) && baseAI.IsAlerted() && Object.op_Implicit((Object)(object)val2) && !val2.IsDead() && source.Id != 0;
				Plugin.Channel.Queue(observation);
				if (!observation.Engaged)
				{
					tracked.Remove(item.Key);
				}
			}
		}

		internal DotState BeforeDot(StatusEffect effect, float amount, int kind)
		{
			return new DotState
			{
				Before = Remaining(effect, kind),
				Amount = amount,
				Kind = kind
			};
		}

		private static float Remaining(StatusEffect effect, int kind)
		{
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: Expected O, but got Unknown
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Expected O, but got Unknown
			SE_Poison val = (SE_Poison)(object)((effect is SE_Poison) ? effect : null);
			if (val == null)
			{
				if (kind != 5)
				{
					return SpiritLeft.Invoke((SE_Burning)effect);
				}
				return FireLeft.Invoke((SE_Burning)effect);
			}
			return PoisonLeft.Invoke(val);
		}

		internal void AddedDot(StatusEffect effect, DotState state, bool accepted)
		{
			Character val = EffectCharacter.Invoke(effect);
			if (accepted && Owns(val))
			{
				HitData hit = ((CurrentHit != null && (Object)(object)CurrentHit.Target == (Object)(object)val) ? CurrentHit.Hit : null);
				Observation observation = Create(val, hit);
				if (observation != null)
				{
					observation.Kind = ObservationKind.DotAdded;
					observation.DotKind = state.Kind;
					observation.DotBefore = state.Before;
					observation.ReplaceDot = effect is SE_Poison;
					observation.DotAmount = (observation.ReplaceDot ? Remaining(effect, state.Kind) : Math.Max(0f, Remaining(effect, state.Kind) - state.Before));
					effectKinds.GetOrCreateValue(effect).Values.Add(state.Kind);
					Plugin.Channel.Queue(observation);
				}
			}
		}

		internal void RemovedDot(StatusEffect effect)
		{
			if (!Object.op_Implicit((Object)(object)effect) || !effectKinds.TryGetValue(effect, out var value))
			{
				return;
			}
			Character target = EffectCharacter.Invoke(effect);
			foreach (int value2 in value.Values)
			{
				Observation observation = Create(target, null);
				if (observation != null)
				{
					observation.Kind = ObservationKind.DotCleared;
					observation.DotKind = value2;
					Plugin.Channel.Queue(observation);
				}
			}
			effectKinds.Remove(effect);
		}

		internal void Clear()
		{
			tracked.Clear();
		}

		internal static float[] Values(HitData hit)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: 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_006c: Unknown result type (might be due to invalid IL or missing references)
			DamageTypes damage = hit.m_damage;
			return new float[11]
			{
				damage.m_blunt, damage.m_slash, damage.m_pierce, damage.m_chop, damage.m_pickaxe, damage.m_fire, damage.m_frost, damage.m_lightning, damage.m_poison, damage.m_spirit,
				damage.m_damage
			}.Select((float x) => Math.Max(0f, x)).ToArray();
		}
	}
	[HarmonyPatch(typeof(Character), "Damage")]
	internal static class ObserveOutgoing
	{
		private static void Prefix(Character __instance, HitData hit)
		{
			Plugin.Collector.TrackAttacker(__instance, hit);
		}
	}
	[HarmonyPatch(typeof(Character), "RPC_Damage")]
	internal static class ObserveContact
	{
		private static void Prefix(Character __instance, HitData hit, out DamageCollector.RpcContext __state)
		{
			__state = DamageCollector.CurrentHit;
			if (DamageCollector.Owns(__instance))
			{
				DamageCollector.CurrentHit = new DamageCollector.RpcContext
				{
					Target = __instance,
					Hit = hit
				};
				Plugin.Collector.Activity(__instance, hit);
			}
		}

		private static void Finalizer(DamageCollector.RpcContext __state)
		{
			DamageCollector.CurrentHit = __state;
		}
	}
	[HarmonyPatch(typeof(Character), "ApplyDamage")]
	internal static class ObserveDamage
	{
		[HarmonyPriority(0)]
		private static void Prefix(Character __instance, HitData hit, out DamageCollector.DamageState __state)
		{
			__state = new DamageCollector.DamageState
			{
				Valid = (DamageCollector.Owns(__instance) && !__instance.IsDead() && !__instance.IsTeleporting() && !__instance.InCutscene() && !__instance.IsDebugFlying() && !CinematicsManager.IsPlaying()),
				Health = __instance.GetHealth(),
				Raw = ((hit == null) ? new float[11] : DamageCollector.Values(hit))
			};
		}

		private static void Postfix(Character __instance, HitData hit, DamageCollector.DamageState __state)
		{
			Plugin.Collector.Applied(__instance, hit, __state);
		}
	}
	[HarmonyPatch(typeof(Character), "OnDeath")]
	internal static class ObserveDeath
	{
		private static void Prefix(Character __instance)
		{
			Plugin.Collector.Death(__instance);
		}
	}
	[HarmonyPatch(typeof(Character), "RPC_Heal")]
	internal static class ObserveHealing
	{
		private static void Prefix(Character __instance, out float __state)
		{
			__state = __instance.GetHealth();
		}

		private static void Postfix(Character __instance, float __state)
		{
			Plugin.Collector.Healed(__instance, __state);
		}
	}
	[HarmonyPatch]
	internal static class ObserveTick
	{
		private static IEnumerable<MethodBase> TargetMethods()
		{
			yield return AccessTools.Method(typeof(SE_Burning), "UpdateStatusEffect", (Type[])null, (Type[])null);
			yield return AccessTools.Method(typeof(SE_Poison), "UpdateStatusEffect", (Type[])null, (Type[])null);
		}

		private static void Prefix(StatusEffect __instance, out StatusEffect __state)
		{
			__state = DamageCollector.CurrentEffect;
			DamageCollector.CurrentEffect = __instance;
		}

		private static void Finalizer(StatusEffect __state)
		{
			DamageCollector.CurrentEffect = __state;
		}
	}
	[HarmonyPatch(typeof(SE_Burning), "AddFireDamage")]
	internal static class ObserveFire
	{
		private static void Prefix(SE_Burning __instance, float __0, out DamageCollector.DotState __state)
		{
			__state = Plugin.Collector.BeforeDot((StatusEffect)(object)__instance, __0, 5);
		}

		private static void Postfix(SE_Burning __instance, bool __result, DamageCollector.DotState __state)
		{
			Plugin.Collector.AddedDot((StatusEffect)(object)__instance, __state, __result);
		}
	}
	[HarmonyPatch(typeof(SE_Burning), "AddSpiritDamage")]
	internal static class ObserveSpirit
	{
		private static void Prefix(SE_Burning __instance, float __0, out DamageCollector.DotState __state)
		{
			__state = Plugin.Collector.BeforeDot((StatusEffect)(object)__instance, __0, 9);
		}

		private static void Postfix(SE_Burning __instance, bool __result, DamageCollector.DotState __state)
		{
			Plugin.Collector.AddedDot((StatusEffect)(object)__instance, __state, __result);
		}
	}
	[HarmonyPatch(typeof(SE_Poison), "AddDamage")]
	internal static class ObservePoison
	{
		private static void Prefix(SE_Poison __instance, float __0, out DamageCollector.DotState __state)
		{
			__state = Plugin.Collector.BeforeDot((StatusEffect)(object)__instance, __0, 8);
		}

		private static void Postfix(SE_Poison __instance, DamageCollector.DotState __state)
		{
			Plugin.Collector.AddedDot((StatusEffect)(object)__instance, __state, __state.Amount >= __state.Before);
		}
	}
	[HarmonyPatch(typeof(SEMan), "RemoveStatusEffect", new Type[]
	{
		typeof(StatusEffect),
		typeof(bool)
	})]
	internal static class ObserveEffectRemoval
	{
		private static void Postfix(StatusEffect __0, bool __result)
		{
			if (__result)
			{
				Plugin.Collector.RemovedDot(__0);
			}
		}
	}
}
namespace CombatStats.Net
{
	internal sealed class PlayerIdentity
	{
		public int Protocol = 1;

		public long Id;

		public long Peer;

		public string Name = "";

		public string Character = "";

		public float X;

		public float Y;

		public float Z;

		public double Sent;
	}
	internal sealed class ClockReply
	{
		public int Protocol = 1;

		public string Session = "";

		public double Echo;

		public double Time;
	}
	internal sealed class CombatBatch
	{
		public int Protocol = 1;

		public string Session = "";

		public List<Observation> Events = new List<Observation>();
	}
	internal sealed class CombatFeed
	{
		public int Protocol = 1;

		public string Session = "";

		public double Time;

		public List<Encounter> Fights = new List<Encounter>();

		public Dictionary<long, long> Redirects = new Dictionary<long, long>();

		public List<PlayerIdentity> Players = new List<PlayerIdentity>();
	}
	internal sealed class DamageChannel : MonoBehaviour
	{
		private const string HelloRpc = "Tenemo.CombatStats.Hello.1";

		private const string ClockRpc = "Tenemo.CombatStats.Clock.1";

		private const string EventsRpc = "Tenemo.CombatStats.Events.1";

		private const string FeedRpc = "Tenemo.CombatStats.Feed.1";

		private static readonly Stopwatch Clock = Stopwatch.StartNew();

		private static readonly Func<ZRoutedRpc, long> ServerPeerOf = AccessTools.MethodDelegate<Func<ZRoutedRpc, long>>(AccessTools.Method(typeof(ZRoutedRpc), "GetServerPeerID", (Type[])null, (Type[])null), (object)null, true);

		private static readonly JsonSerializerSettings Json = new JsonSerializerSettings
		{
			MaxDepth = 20
		};

		private static double offset;

		private ZRoutedRpc bound;

		private readonly List<Observation> pending = new List<Observation>();

		private readonly Dictionary<long, PlayerIdentity> players = new Dictionary<long, PlayerIdentity>();

		private readonly Dictionary<long, Dictionary<long, long>> published = new Dictionary<long, Dictionary<long, long>>();

		private EncounterRecorder recorder = new EncounterRecorder();

		private string session = "";

		private string historyPath = "";

		private long sequence;

		private double nextSend;

		private double nextHello;

		private bool synchronized;

		internal static double Now => Clock.Elapsed.TotalSeconds + offset;

		internal EncounterRecorder Recorder => recorder;

		internal void Queue(Observation report)
		{
			if (bound != null)
			{
				report.Sequence = ++sequence;
				pending.Add(report);
			}
		}

		internal DamageCollector.Source FindPlayer(string name)
		{
			if (string.IsNullOrEmpty(name))
			{
				return null;
			}
			List<PlayerIdentity> list = players.Values.Where((PlayerIdentity x) => x.Name == name).ToList();
			if (list.Count != 1)
			{
				return null;
			}
			return Source(list[0]);
		}

		internal DamageCollector.Source FindCharacter(string character)
		{
			PlayerIdentity playerIdentity = players.Values.FirstOrDefault((PlayerIdentity x) => x.Character == character);
			if (playerIdentity != null)
			{
				return Source(playerIdentity);
			}
			return null;
		}

		private static DamageCollector.Source Source(PlayerIdentity player)
		{
			return new DamageCollector.Source
			{
				Id = player.Id,
				Name = player.Name
			};
		}

		private void Update()
		{
			//IL_0119: Unknown result type (might be due to invalid IL or missing references)
			//IL_011e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0171: 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_018b: Unknown result type (might be due to invalid IL or missing references)
			if (!Plugin.Enabled)
			{
				return;
			}
			ZNet instance = ZNet.instance;
			ZRoutedRpc instance2 = ZRoutedRpc.instance;
			if (!Object.op_Implicit((Object)(object)instance) || instance2 == null)
			{
				if (bound != null)
				{
					EndSession();
				}
				return;
			}
			if (bound != instance2)
			{
				Bind(instance2, instance);
			}
			if (Object.op_Implicit((Object)(object)Player.m_localPlayer))
			{
				string text = Path.Combine(Paths.BepInExRootPath, "combat-stats-state", "world-" + instance.GetWorldUID() + "-player-" + DamageCollector.LocalId() + ".dat");
				if (text != historyPath)
				{
					LoadHistory(text);
				}
				if (Clock.Elapsed.TotalSeconds >= nextHello)
				{
					nextHello = Clock.Elapsed.TotalSeconds + (synchronized ? 2.0 : 0.25);
					Player localPlayer = Player.m_localPlayer;
					Vector3 position = ((Component)localPlayer).transform.position;
					PlayerIdentity value = new PlayerIdentity
					{
						Id = localPlayer.GetPlayerID(),
						Name = localPlayer.GetPlayerName(),
						Character = DamageCollector.Key((Character)(object)localPlayer),
						Peer = ZNet.GetUID(),
						Sent = Clock.Elapsed.TotalSeconds,
						X = position.x,
						Y = position.y,
						Z = position.z
					};
					if (instance.IsServer())
					{
						players[ZNet.GetUID()] = value;
					}
					else
					{
						instance2.InvokeRoutedRPC(ServerPeerOf(instance2), "Tenemo.CombatStats.Hello.1", new object[1] { Encode(value) });
					}
				}
			}
			if (!(Clock.Elapsed.TotalSeconds < nextSend))
			{
				nextSend = Clock.Elapsed.TotalSeconds + 0.25;
				Plugin.Collector.Engagement();
				Flush(instance, instance2);
				if (instance.IsServer())
				{
					recorder.Tick(Now);
					Publish(instance, instance2);
				}
				else if (synchronized)
				{
					Plugin.View.Now = Now;
				}
			}
		}

		private void Bind(ZRoutedRpc rpc, ZNet net)
		{
			EndSession();
			bound = rpc;
			session = (net.IsServer() ? Guid.NewGuid().ToString("N") : "");
			synchronized = net.IsServer();
			offset = 0.0;
			rpc.Register<string>("Tenemo.CombatStats.Hello.1", (Action<long, string>)Hello);
			rpc.Register<string>("Tenemo.CombatStats.Clock.1", (Action<long, string>)SetClock);
			rpc.Register<string>("Tenemo.CombatStats.Events.1", (Action<long, string>)Events);
			rpc.Register<string>("Tenemo.CombatStats.Feed.1", (Action<long, string>)Feed);
			Plugin.Debug("Combat channel registered for a new world.");
		}

		private void EndSession()
		{
			SaveHistory();
			historyPath = "";
			Plugin.View.Changed -= SaveHistory;
			Plugin.View = new CombatHistory();
			recorder = new EncounterRecorder();
			pending.Clear();
			players.Clear();
			published.Clear();
			sequence = 0L;
			bound = null;
			synchronized = false;
			nextHello = 0.0;
			nextSend = 0.0;
			Plugin.Collector?.Clear();
			DetailWindow.Instance?.Close();
		}

		private void Hello(long sender, string json)
		{
			if (ZNet.instance.IsServer())
			{
				PlayerIdentity playerIdentity = Decode<PlayerIdentity>(json);
				ZNetPeer peer = ZNet.instance.GetPeer(sender);
				if (playerIdentity != null && playerIdentity.Protocol == 1 && peer != null && playerIdentity.Id != 0L && playerIdentity.Name != null && playerIdentity.Name.Length <= 160 && !(playerIdentity.Character != ((object)Unsafe.As<ZDOID, ZDOID>(ref peer.m_characterID)/*cast due to .constrained prefix*/).ToString()))
				{
					playerIdentity.Peer = sender;
					players[sender] = playerIdentity;
					bound.InvokeRoutedRPC(sender, "Tenemo.CombatStats.Clock.1", new object[1] { Encode(new ClockReply
					{
						Session = session,
						Echo = playerIdentity.Sent,
						Time = Now
					}) });
				}
			}
		}

		private void SetClock(long sender, string json)
		{
			if (ZNet.instance.IsServer() || sender != ServerPeerOf(bound))
			{
				return;
			}
			ClockReply clockReply = Decode<ClockReply>(json);
			if (clockReply == null || clockReply.Protocol != 1 || string.IsNullOrEmpty(clockReply.Session))
			{
				return;
			}
			double totalSeconds = Clock.Elapsed.TotalSeconds;
			double num = totalSeconds - clockReply.Echo;
			if (num < 0.0 || num > 5.0 || double.IsNaN(clockReply.Time) || double.IsInfinity(clockReply.Time))
			{
				return;
			}
			double num2 = clockReply.Time + num * 0.5 - totalSeconds;
			foreach (Observation item in pending)
			{
				item.Time += num2 - offset;
			}
			offset = num2;
			session = clockReply.Session;
			synchronized = true;
		}

		private void Flush(ZNet net, ZRoutedRpc rpc)
		{
			if (pending.Count == 0 || (!net.IsServer() && !synchronized))
			{
				return;
			}
			while (pending.Count > 0)
			{
				CombatBatch combatBatch = new CombatBatch
				{
					Session = session,
					Events = pending.Take(128).ToList()
				};
				pending.RemoveRange(0, combatBatch.Events.Count);
				if (net.IsServer())
				{
					recorder.Receive(ZNet.GetUID(), combatBatch.Events, Now);
					continue;
				}
				rpc.InvokeRoutedRPC(ServerPeerOf(rpc), "Tenemo.CombatStats.Events.1", new object[1] { Encode(combatBatch) });
			}
		}

		private void Events(long sender, string json)
		{
			if (ZNet.instance.IsServer() && players.ContainsKey(sender) && ZNet.instance.GetPeer(sender) != null)
			{
				CombatBatch combatBatch = Decode<CombatBatch>(json);
				if (combatBatch != null && combatBatch.Protocol == 1 && !(combatBatch.Session != session) && combatBatch.Events != null && combatBatch.Events.Count <= 128 && !combatBatch.Events.Any((Observation x) => x == null))
				{
					recorder.Receive(sender, combatBatch.Events, Now);
				}
			}
		}

		private void Publish(ZNet net, ZRoutedRpc rpc)
		{
			HashSet<long> connected = new HashSet<long>(from x in net.GetPeers()
				select x.m_uid) { ZNet.GetUID() };
			foreach (long item in players.Keys.Where((long x) => !connected.Contains(x)).ToList())
			{
				players.Remove(item);
				published.Remove(item);
				recorder.ForgetPeer(item);
			}
			foreach (KeyValuePair<long, PlayerIdentity> item2 in players.ToList())
			{
				PlayerIdentity player = item2.Value;
				if (!published.TryGetValue(item2.Key, out var revisions))
				{
					published[item2.Key] = (revisions = new Dictionary<long, long>());
				}
				long value;
				CombatFeed combatFeed = new CombatFeed
				{
					Session = session,
					Time = Now,
					Redirects = recorder.Redirects,
					Players = players.Values.ToList(),
					Fights = recorder.Fights.Where((Encounter x) => (x.Participants.ContainsKey(player.Id) || revisions.ContainsKey(x.Id) || (x.Active && EncounterRecorder.Distance(x.X, x.Y, x.Z, player.X, player.Y, player.Z) <= recorder.Radius * recorder.Radius)) && (x.Active || !revisions.TryGetValue(x.Id, out value) || value != x.Revision)).ToList()
				};
				string text = Encode(combatFeed);
				if (item2.Key == ZNet.GetUID())
				{
					AcceptFeed(Decode<CombatFeed>(text));
				}
				else
				{
					rpc.InvokeRoutedRPC(item2.Key, "Tenemo.CombatStats.Feed.1", new object[1] { text });
				}
				foreach (Encounter fight in combatFeed.Fights)
				{
					revisions[fight.Id] = fight.Revision;
				}
				foreach (long item3 in revisions.Keys.Where((long x) => recorder.Fights.All((Encounter f) => f.Id != x)).ToList())
				{
					revisions.Remove(item3);
				}
			}
		}

		private void Feed(long sender, string json)
		{
			if (!ZNet.instance.IsServer() && sender == ServerPeerOf(bound) && synchronized)
			{
				CombatFeed combatFeed = Decode<CombatFeed>(json);
				if (combatFeed != null && combatFeed.Protocol == 1 && !(combatFeed.Session != session))
				{
					AcceptFeed(combatFeed);
				}
			}
		}

		private void AcceptFeed(CombatFeed feed)
		{
			if (feed == null || historyPath.Length == 0)
			{
				return;
			}
			if (!ZNet.instance.IsServer())
			{
				players.Clear();
				foreach (PlayerIdentity player in feed.Players)
				{
					players[player.Peer] = player;
				}
			}
			Plugin.View.Receive(feed.Session, feed.Time, feed.Fights, feed.Redirects);
		}

		private void LoadHistory(string path)
		{
			//IL_005b: Expected O, but got Unknown
			SaveHistory();
			Plugin.View.Changed -= SaveHistory;
			Plugin.View = new CombatHistory();
			historyPath = path;
			if (File.Exists(path))
			{
				try
				{
					Plugin.View.Saved = JsonConvert.DeserializeObject<SavedHistory>(File.ReadAllText(path), Json) ?? new SavedHistory();
				}
				catch (JsonException ex)
				{
					JsonException ex2 = ex;
					string text = path + ".invalid-" + DateTime.UtcNow.ToString("yyyyMMdd-HHmmss");
					File.Copy(path, text);
					Plugin.Log.LogError((object)("Could not read combat history; preserved it at " + text + ": " + ((Exception)(object)ex2).Message));
				}
			}
			Plugin.View.Changed += SaveHistory;
		}

		private void SaveHistory()
		{
			if (historyPath.Length > 0)
			{
				Storage.Write(historyPath, Encode(Plugin.View.Saved));
			}
		}

		internal static string Encode(object value)
		{
			return JsonConvert.SerializeObject(value, Json);
		}

		private static T Decode<T>(string value) where T : class
		{
			//IL_002a: Expected O, but got Unknown
			if (value == null || value.Length > 1048576)
			{
				return null;
			}
			try
			{
				return JsonConvert.DeserializeObject<T>(value, Json);
			}
			catch (JsonException ex)
			{
				JsonException ex2 = ex;
				Plugin.Log.LogWarning((object)("Invalid combat packet: " + ((Exception)(object)ex2).Message));
				return null;
			}
		}
	}
}
namespace CombatStats.Ui
{
	internal sealed class CompactMeter : MonoBehaviour
	{
		private const float RefreshInterval = 0.25f;

		private const float FadeSeconds = 0.4f;

		private const float PadLeft = 11f;

		private const float PadTop = 7f;

		private const float PadBottom = 9f;

		private const float CaptionHeight = 14f;

		private readonly List<MeterRow> _rows = new List<MeterRow>();

		private RectTransform _root;

		private RectTransform _list;

		private CanvasGroup _group;

		private Image _background;

		private TMP_Text _caption;

		private float _nextRefresh;

		private float _alpha;

		private bool _failed;

		public bool IsVisible => _alpha > 0.01f;

		private void Update()
		{
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			if (Plugin.Settings != null)
			{
				if (Shortcut.IsPressed(Plugin.Settings.CompactKey.Value))
				{
					Cycle();
				}
				bool flag = Wanted();
				if (flag && Time.time >= _nextRefresh)
				{
					_nextRefresh = Time.time + 0.25f;
					Refresh();
				}
				Fade(flag);
			}
		}

		private void Cycle()
		{
			ConfigEntryCycle();
			string text = Plugin.Settings.CompactMode.Value switch
			{
				MeterMode.Always => "Combat meter: always shown", 
				MeterMode.Auto => "Combat meter: shows the latest fight", 
				_ => "Combat meter: off", 
			};
			MessageHud instance = MessageHud.instance;
			if ((Object)(object)instance != (Object)null)
			{
				instance.ShowMessage((MessageType)1, text, 0, (Sprite)null, false, true);
			}
			Plugin.Debug(text);
		}

		private void ConfigEntryCycle()
		{
			switch (Plugin.Settings.CompactMode.Value)
			{
			case MeterMode.Auto:
				Plugin.Settings.CompactMode.Value = MeterMode.Always;
				break;
			case MeterMode.Always:
				Plugin.Settings.CompactMode.Value = MeterMode.Off;
				break;
			default:
				Plugin.Settings.CompactMode.Value = MeterMode.Auto;
				break;
			}
		}

		private bool Wanted()
		{
			if (!Plugin.Enabled || Plugin.Settings.CompactMode.Value == MeterMode.Off)
			{
				return false;
			}
			if ((Object)(object)Player.m_localPlayer == (Object)null || Hud.IsUserHidden() || Menu.IsVisible() || Minimap.IsOpen())
			{
				return false;
			}
			if (Plugin.Settings.CompactMode.Value == MeterMode.Always)
			{
				return true;
			}
			return Plugin.View.Current(DamageCollector.LocalId()) != null;
		}

		private void Fade(bool wanted)
		{
			float num = (wanted ? 1f : 0f);
			if (Mathf.Approximately(_alpha, num))
			{
				if ((Object)(object)_group != (Object)null && ((Component)_group).gameObject.activeSelf != _alpha > 0.01f)
				{
					((Component)_group).gameObject.SetActive(_alpha > 0.01f);
				}
				return;
			}
			_alpha = Mathf.MoveTowards(_alpha, num, Time.unscaledDeltaTime / 0.4f);
			if (!((Object)(object)_group == (Object)null))
			{
				_group.alpha = _alpha;
				bool flag = _alpha > 0.01f;
				if (((Component)_group).gameObject.activeSelf != flag)
				{
					((Component)_group).gameObject.SetActive(flag);
				}
			}
		}

		private void Refresh()
		{
			//IL_022e: Unknown result type (might be due to invalid IL or missing references)
			//IL_023e: Unknown result type (might be due to invalid IL or missing references)
			//IL_025d: Unknown result type (might be due to invalid IL or missing references)
			//IL_027e: 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_020f: Unknown result type (might be due to invalid IL or missing references)
			//IL_029c: Unknown result type (might be due to invalid IL or missing references)
			//IL_02a1: Unknown result type (might be due to invalid IL or missing references)
			//IL_02a9: Unknown result type (might be due to invalid IL or missing references)
			//IL_02b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_02b7: Unknown result type (might be due to invalid IL or missing references)
			//IL_02c9: Unknown result type (might be due to invalid IL or missing references)
			if (!EnsureUi() || (Object)(object)_root == (Object)null)
			{
				return;
			}
			ModConfig settings = Plugin.Settings;
			float num = Mathf.Max(160f, settings.CompactWidth.Value);
			bool value = settings.CompactBars.Value;
			float width = num - 22f;
			EncounterSnapshot encounterSnapshot = Snapshot();
			List<CombatantRow> list = Choose(encounterSnapshot, settings.CompactRows.Value);
			float leader = ((list.Count > 0) ? list[0].Total : 0f);
			long num2 = DamageCollector.LocalId();
			float num3 = 0f;
			if ((Object)(object)_caption != (Object)null)
			{
				bool value2 = settings.CompactCaption.Value;
				((Component)_caption).gameObject.SetActive(value2);
				if (value2)
				{
					_caption.text = Localization.instance.Localize(encounterSnapshot.Name) + " | " + Seconds(encounterSnapshot.Duration) + " | " + encounterSnapshot.Result;
					UiStyle.PlaceTopStretch(_caption.rectTransform, 0f, 0f, 0f, 14f);
					num3 += 14f;
				}
			}
			for (int i = 0; i < list.Count; i++)
			{
				MeterRow meterRow = RowAt(i);
				meterRow.SetActive(active: true);
				meterRow.Apply(list[i], leader, width, value, settings.CompactShare.Value, list[i].Id == num2, string.Empty);
				meterRow.Place(num3);
				num3 += MeterRow.Height(value);
			}
			for (int j = list.Count; j < _rows.Count; j++)
			{
				_rows[j].SetActive(active: false);
			}
			if ((Object)(object)_list != (Object)null)
			{
				_list.sizeDelta = new Vector2(_list.sizeDelta.x, num3);
			}
			_root.sizeDelta = new Vector2(num, 7f + num3 + 9f);
			((Transform)_root).localScale = Vector3.one * Mathf.Clamp(settings.CompactScale.Value, 0.5f, 2f);
			UiStyle.Anchor(_root, settings.CompactAnchor.Value, settings.CompactOffset.Value);
			if ((Object)(object)_background != (Object)null)
			{
				Color color = ((Graphic)_background).color;
				((Graphic)_background).color = new Color(color.r, color.g, color.b, settings.CompactOpacity.Value);
			}
		}

		private EncounterSnapshot Snapshot()
		{
			return Plugin.View.Snapshot(0, CombatChannel.DamageDealt, DamageCollector.LocalId());
		}

		private List<CombatantRow> Choose(EncounterSnapshot snapshot, int limit)
		{
			List<CombatantRow> list = new List<CombatantRow>();
			long num = DamageCollector.LocalId();
			bool flag = false;
			for (int i = 0; i < snapshot.Rows.Count; i++)
			{
				if (list.Count >= limit)
				{
					break;
				}
				list.Add(snapshot.Rows[i]);
				flag |= snapshot.Rows[i].Id == num;
			}
			if (flag || list.Count < limit)
			{
				return list;
			}
			for (int j = limit; j < snapshot.Rows.Count; j++)
			{
				if (snapshot.Rows[j].Id == num)
				{
					list[list.Count - 1] = snapshot.Rows[j];
					break;
				}
			}
			return list;
		}

		internal static string Seconds(double seconds)
		{
			return (int)seconds / 60 + ":" + ((int)seconds % 60).ToString("00");
		}

		private MeterRow RowAt(int index)
		{
			while (_rows.Count <= index)
			{
				_rows.Add(MeterRow.Create(_list, detailed: false, null));
			}
			return _rows[index];
		}

		private bool EnsureUi()
		{
			if (_failed)
			{
				return false;
			}
			if ((Object)(object)_root != (Object)null)
			{
				return true;
			}
			try
			{
				Build();
			}
			catch (Exception arg)
			{
				_failed = true;
				if ((Object)(object)_root != (Object)null)
				{
					Object.Destroy((Object)(object)((Component)_root).gameObject);
					_root = null;
				}
				Plugin.Log.LogError((object)$"Could not build the compact meter; it stays hidden until restart: {arg}");
				return false;
			}
			return (Object)(object)_root != (Object)null;
		}

		private void Build()
		{
			//IL_0063: Unknown result type (might be due to invalid IL or missing references)
			//IL_0069: Expected O, but got Unknown
			//IL_0070: Unknown result type (might be due to invalid IL or missing references)
			//IL_007a: Expected O, but got Unknown
			//IL_00a1: Unknown result type (might be due to invalid IL or missing references)
			//IL_0110: Unknown result type (might be due to invalid IL or missing references)
			//IL_015e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0183: Unknown result type (might be due to invalid IL or missing references)
			//IL_019d: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b7: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d1: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e0: Unknown result type (might be due to invalid IL or missing references)
			//IL_0202: Unknown result type (might be due to invalid IL or missing references)
			//IL_0208: Expected O, but got Unknown
			//IL_020f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0219: Expected O, but got Unknown
			//IL_0260: Unknown result type (might be due to invalid IL or missing references)
			_rows.Clear();
			_list = null;
			_caption = null;
			_background = null;
			_group = null;
			Transform val = UiStyle.FindCanvas();
			if (!((Object)(object)val == (Object)null) && UiStyle.ReadStyle())
			{
				GameObject val2 = new GameObject("CombatStats Compact", new Type[2]
				{
					typeof(RectTransform),
					typeof(CanvasGroup)
				});
				_root = (RectTransform)val2.transform;
				((Transform)_root).SetParent(val, false);
				_root.sizeDelta = new Vector2(Plugin.Settings.CompactWidth.Value, 80f);
				_group = val2.GetComponent<CanvasGroup>();
				_group.alpha = 0f;
				_group.interactable = false;
				_group.blocksRaycasts = false;
				val2.SetActive(false);
				_background = UiStyle.Box("Background", _root, new Color(0.03f, 0.028f, 0.024f, Plugin.Settings.CompactOpacity.Value));
				_background.sprite = UiStyle.Fade();
				UiStyle.Stretch(((Graphic)_background).rectTransform, 0f, 0f, 0f, 0f);
				Image obj = UiStyle.Box("Hairline", _root, UiStyle.Hairline);
				obj.sprite = UiStyle.Flat();
				((Graphic)obj).rectTransform.anchorMin = new Vector2(0f, 1f);
				((Graphic)obj).rectTransform.anchorMax = new Vector2(1f, 1f);
				((Graphic)obj).rectTransform.pivot = new Vector2(0.5f, 1f);
				((Graphic)obj).rectTransform.sizeDelta = new Vector2(0f, 1f);
				((Graphic)obj).rectTransform.anchoredPosition = Vector2.zero;
				GameObject val3 = new GameObject("List", new Type[1] { typeof(RectTransform) });
				_list = (RectTransform)val3.transform;
				((Transform)_list).SetParent((Transform)(object)_root, false);
				UiStyle.PlaceTopStretch(_list, 11f, 11f, 7f, 40f);
				_caption = UiStyle.Text("Caption", _list, 12f, (TextAlignmentOptions)4097, UiStyle.Dim);
				_caption.richText = false;
				Plugin.Debug("Compact meter built");
			}
		}
	}
	internal sealed class DetailWindow : MonoBehaviour
	{
		private sealed class KindLine
		{
			public const float Height = 15f;

			private readonly RectTransform _root;

			private readonly TMP_Text _name;

			private readonly TMP_Text _amount;

			private readonly TMP_Text _share;

			private KindLine(RectTransform root, TMP_Text name, TMP_Text amount, TMP_Text share)
			{
				_root = root;
				_name = name;
				_amount = amount;
				_share = share;
			}

			public static KindLine Create(RectTransform parent)
			{
				//IL_0018: Unknown result type (might be due to invalid IL or missing references)
				//IL_0022: Unknown result type (might be due to invalid IL or missing references)
				//IL_0028: Expected O, but got Unknown
				//IL_003b: Unknown result type (might be due to invalid IL or missing references)
				//IL_0050: Unknown result type (might be due to invalid IL or missing references)
				//IL_0065: Unknown result type (might be due to invalid IL or missing references)
				//IL_007a: Unknown result type (might be due to invalid IL or missing references)
				//IL_0094: Unknown result type (might be due to invalid IL or missing references)
				//IL_00b6: 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_00ea: Unknown result type (might be due to invalid IL or missing references)
				//IL_0105: Unknown result type (might be due to invalid IL or missing references)
				//IL_011f: Unknown result type (might be due to invalid IL or missing references)
				//IL_0139: 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_016d: Unknown result type (might be due to invalid IL or missing references)
				//IL_0188: Unknown result type (might be due to invalid IL or missing references)
				//IL_01a2: Unknown result type (might be due to invalid IL or missing references)
				//IL_01bc: Unknown result type (might be due to invalid IL or missing references)
				//IL_01d6: Unknown result type (might be due to invalid IL or missing references)
				RectTransform val = (RectTransform)new GameObject("Kind", new Type[1] { typeof(RectTransform) }).transform;
				((Transform)val).SetParent((Transform)(object)parent, false);
				val.anchorMin = new Vector2(0f, 1f);
				val.anchorMax = new Vector2(1f, 1f);
				val.pivot = new Vector2(0f, 1f);
				val.sizeDelta = new Vector2(0f, 15f);
				TMP_Text val2 = UiStyle.Text("Name", val, 12.5f, (TextAlignmentOptions)4097, UiStyle.Dim);
				val2.richText = false;
				val2.rectTransform.offsetMin = new Vector2(22f, 0f);
				val2.rectTransform.offsetMax = new Vector2(-250f, 0f);
				TMP_Text val3 = UiStyle.Text("Amount", val, 12.5f, (TextAlignmentOptions)4100, UiStyle.Dim);
				val3.rectTransform.anchorMin = new Vector2(1f, 0f);
				val3.rectTransform.anchorMax = new Vector2(1f, 1f);
				val3.rectTransform.offsetMin = new Vector2(-250f, 0f);
				val3.rectTransform.offsetMax = new Vector2(-152f, 0f);
				TMP_Text val4 = UiStyle.Text("Share", val, 12.5f, (TextAlignmentOptions)4100, UiStyle.Dim);
				val4.rectTransform.anchorMin = new Vector2(1f, 0f);
				val4.rectTransform.anchorMax = new Vector2(1f, 1f);
				val4.rectTransform.offsetMin = new Vector2(-68f, 0f);
				val4.rectTransform.offsetMax = new Vector2(0f, 0f);
				return new KindLine(val, val2, val3, val4);
			}

			public void ApplyText(string name, string amount, string share)
			{
				//IL_001c: Unknown result type (might be due to invalid IL or missing references)
				_name.text = Localization.instance.Localize(name);
				((Graphic)_name).color = UiStyle.Dim;
				_amount.text = amount;
				_share.text = share;
			}

			public void Apply(DamageKind kind, float amount, float share)
			{
				//IL_001d: Unknown result type (might be due to invalid IL or missing references)
				_name.text = KindName(kind);
				((Graphic)_name).color = UiStyle.Colors[(int)kind];
				_amount.text = MeterRow.Format(amount);
				_share.text = (share * 100f).ToString("0") + "%";
			}

			public void SetActive(bool active)
			{
				if (((Component)_root).gameObject.activeSelf != active)
				{
					((Component)_root).gameObject.SetActive(active);
				}
			}

			public void Place(float y)
			{
				//IL_000d: Unknown result type (might be due to invalid IL or missing references)
				_root.anchoredPosition = new Vector2(0f, 0f - y);
			}

			private static string KindName(DamageKind kind)
			{
				string text = DamageKinds.LocalizationKey(kind);
				Localization instance = Localization.instance;
				if (instance == null)
				{
					return DamageKinds.EnglishName(kind);
				}
				string text2 = instance.Localize(text);
				if (!string.IsNullOrEmpty(text2) && !text2.StartsWith("[", StringComparison.Ordinal) && !text2.Contains("$"))
				{
					return text2;
				}
				return DamageKinds.EnglishName(kind);
			}
		}

		private const float RefreshInterval = 0.25f;

		private const float Padding = 14f;

		private const float ScrollStep = 48f;

		private static readonly CombatChannel[] Channels = new CombatChannel[3]
		{
			CombatChannel.DamageDealt,
			CombatChannel.DamageTaken,
			CombatChannel.Healing
		};

		private readonly List<MeterRow> _rows = new List<MeterRow>();

		private readonly List<KindLine> _kindLines = new List<KindLine>();

		private readonly List<Button> _windowTabs = new List<Button>();

		private readonly List<Button> _channelTabs = new List<Button>();

		private readonly HashSet<long> _expanded = new HashSet<long>();

		private RectTransform _panel;

		private RectTransform _content;

		private RectTransform _viewport;

		private TMP_Text _summary;

		private TMP_Text _empty;

		private List<int> _windows = new List<int>();

		private int _window;

		private int _historyIndex;

		private int _breakdown;

		private TMP_Text _breakdownLabel;

		private TMP_Text _rateHeading;

		private CombatChannel _channel;

		private float _nextRefresh;

		private float _scrolled;

		private bool _failed;

		private bool _open;

		private bool _seededExpanded;

		public static bool IsOpen { get; private set; }

		public static DetailWindow Instance { get; private set; }

		public static void FeedScrollWheel(float amount)
		{
			DetailWindow instance = Instance;
			if (!((Object)(object)instance == (Object)null) && instance._open)
			{
				instance._scrolled = Mathf.Max(0f, instance._scrolled - Mathf.Sign(amount) * 48f);
				instance.Layout();
			}
		}

		public void Toggle()
		{
			if (_open)
			{
				Close();
			}
			else if (EnsureUi() && !((Object)(object)_panel == (Object)null))
			{
				_open = true;
				IsOpen = true;
				_scrolled = 0f;
				((Component)_panel).gameObject.SetActive(true);
				Refresh();
			}
		}

		public void Close()
		{
			_open = false;
			IsOpen = false;
			SettingsPanel.Instance?.Close();
			if ((Object)(object)_panel != (Object)null)
			{
				((Component)_panel).gameObject.SetActive(false);
			}
		}

		private void Awake()
		{
			Instance = this;
		}

		private void OnDestroy()
		{
			if ((Object)(object)Instance == (Object)(object)this)
			{
				Instance = null;
				IsOpen = false;
			}
		}

		private void Update()
		{
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			if (Plugin.Settings != null && Plugin.Enabled)
			{
				if (Shortcut.IsPressed(Plugin.Settings.DetailKey.Value))
				{
					Toggle();
				}
				if (_open && (Object)(object)_panel == (Object)null)
				{
					Close();
				}
				if (_open && Time.time >= _nextRefresh)
				{
					_nextRefresh = Time.time + 0.25f;
					Refresh();
				}
			}
		}

		private void Refresh()
		{
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)_panel == (Object)null))
			{
				Vector2 value = Plugin.Settings.DetailSize.Value;
				Vector2 val = default(Vector2);
				((Vector2)(ref val))..ctor(Mathf.Max(600f, value.x), Mathf.Max(280f, value.y));
				if (_panel.sizeDelta != val)
				{
					_panel.sizeDelta = val;
				}
				RefreshTabs();
				Layout();
			}
		}

		private void RefreshTabs()
		{
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_006c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0065: Unknown result type (might be due to invalid IL or missing references)
			for (int i = 0; i < _windowTabs.Count; i++)
			{
				((Graphic)UiStyle.LabelOf(_windowTabs[i])).color = ((i == _window) ? UiStyle.Mine : UiStyle.Dim);
			}
			for (int j = 0; j < _channelTabs.Count; j++)
			{
				((Graphic)UiStyle.LabelOf(_channelTabs[j])).color = ((Channels[j] == _channel) ? UiStyle.Mine : UiStyle.Dim);
			}
			if ((Object)(object)_rateHeading != (Object)null)
			{
				_rateHeading.text = ((_channel == CombatChannel.Healing) ? "HPS" : ((_channel == CombatChannel.DamageTaken) ? "Taken/s" : "DPS"));
			}
			if ((Object)(object)_breakdownLabel != (Object)null)
			{
				_breakdownLabel.text = (new string[4]
				{
					"Types",
					(_channel == CombatChannel.DamageTaken) ? "Sources" : "Targets",
					"Skills",
					"Pets"
				})[_breakdown];
			}
		}

		private void Layout()
		{
			//IL_0105: Unknown result type (might be due to invalid IL or missing references)
			//IL_010a: Unknown result type (might be due to invalid IL or missing references)
			//IL_04db: Unknown result type (might be due to invalid IL or missing references)
			//IL_04e0: Unknown result type (might be due to invalid IL or missing references)
			//IL_04fd: Unknown result type (might be due to invalid IL or missing references)
			//IL_0520: Unknown result type (might be due to invalid IL or missing references)
			//IL_0525: Unknown result type (might be due to invalid IL or missing references)
			//IL_054f: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)_content == (Object)null || (Object)(object)_viewport == (Object)null || (Object)(object)_summary == (Object)null)
			{
				return;
			}
			EncounterSnapshot encounterSnapshot = Plugin.View.Snapshot(_window, _channel, DamageCollector.LocalId(), _historyIndex);
			_summary.richText = false;
			_summary.text = Localization.instance.Localize(encounterSnapshot.Name) + " | " + encounterSnapshot.Result + " | " + CompactMeter.Seconds(encounterSnapshot.Duration) + "\nTotal " + MeterRow.Format(encounterSnapshot.Total) + " | " + ((_channel == CombatChannel.Healing) ? "HPS received " : ((_channel == CombatChannel.DamageTaken) ? "Taken/s " : "DPS ")) + MeterRow.Format(encounterSnapshot.Dps);
			Rect rect = _viewport.rect;
			float width = ((Rect)(ref rect)).width;
			float leader = ((encounterSnapshot.Rows.Count > 0) ? encounterSnapshot.Rows[0].Total : 0f);
			long num = DamageCollector.LocalId();
			if (!_seededExpanded && num != 0L)
			{
				_seededExpanded = true;
				_expanded.Add(num);
			}
			int num2 = 0;
			int num3 = 0;
			float num4 = 0f;
			foreach (CombatantRow row in encounterSnapshot.Rows)
			{
				MeterRow meterRow = RowAt(num2++);
				bool flag = _expanded.Contains(row.Id);
				meterRow.SetActive(active: true);
				meterRow.Apply(row, leader, width, bars: true, showShare: false, row.Id == num, flag ? "- " : "+ ");
				meterRow.Place(num4);
				num4 += MeterRow.Height(bars: true);
				if (!flag)
				{
					continue;
				}
				KindLine kindLine = KindAt(num3++);
				kindLine.SetActive(active: true);
				kindLine.ApplyText("Hits " + row.Hits + " | Ticks " + row.Ticks + " | Avg/event " + MeterRow.Format(row.Average), "Overkill " + MeterRow.Format(row.Overkill), "");
				kindLine.Place(num4);
				num4 += 15f;
				if (_breakdown == 0)
				{
					for (int i = 0; i < 11; i++)
					{
						float num5 = row.ByKind[i];
						if (!(num5 <= 0f))
						{
							KindLine kindLine2 = KindAt(num3++);
							kindLine2.SetActive(active: true);
							kindLine2.Apply((DamageKind)i, num5, (row.Total > 0f) ? (num5 / row.Total) : 0f);
							kindLine2.Place(num4);
							num4 += 15f;
						}
					}
				}
				else
				{
					foreach (KeyValuePair<string, float> item in from x in (_breakdown == 1) ? row.Targets : ((_breakdown == 2) ? row.Skills : row.Pets)
						where x.Value > 0f
						orderby x.Value descending
						select x)
					{
						KindLine kindLine3 = KindAt(num3++);
						kindLine3.SetActive(active: true);
						kindLine3.ApplyText(item.Key, MeterRow.Format(item.Value), (row.Total > 0f) ? ((100f * item.Value / row.Total).ToString("0") + "%") : "");
						kindLine3.Place(num4);
						num4 += 15f;
					}
				}
				num4 += 4f;
			}
			for (int num6 = num2; num6 < _rows.Count; num6++)
			{
				_rows[num6].SetActive(active: false);
			}
			for (int num7 = num3; num7 < _kindLines.Count; num7++)
			{
				_kindLines[num7].SetActive(active: false);
			}
			if ((Object)(object)_empty != (Object)null)
			{
				((Component)_empty).gameObject.SetActive(num2 == 0);
			}
			float num8 = num4;
			rect = _viewport.rect;
			float num9 = Mathf.Max(num8, ((Rect)(ref rect)).height);
			_content.sizeDelta = new Vector2(0f, num9);
			float scrolled = _scrolled;
			rect = _viewport.rect;
			_scrolled = Mathf.Clamp(scrolled, 0f, Mathf.Max(0f, num9 - ((Rect)(ref rect)).height));
			_content.anchoredPosition = new Vector2(0f, _scrolled);
		}

		private void OnRowClicked(long id)
		{
			if (!_expanded.Remove(id))
			{
				_expanded.Add(id);
			}
			Layout();
		}

		private MeterRow RowAt(int index)
		{
			while (_rows.Count <= index)
			{
				_rows.Add(MeterRow.Create(_content, detailed: true, OnRowClicked));
			}
			return _rows[index];
		}

		private KindLine KindAt(int index)
		{
			while (_kindLines.Count <= index)
			{
				_kindLines.Add(KindLine.Create(_content));
			}
			return _kindLines[index];
		}

		private bool EnsureUi()
		{
			if (_failed)
			{
				return false;
			}
			if ((Object)(object)_panel != (Object)null)
			{
				return true;
			}
			try
			{
				Build();
			}
			catch (Exception arg)
			{
				_failed = true;
				if ((Object)(object)_panel != (Object)null)
				{
					Object.Destroy((Object)(object)((Component)_panel).gameObject);
					_panel = null;
				}
				Plugin.Log.LogError((object)$"Could not build the detail window; it stays closed until restart: {arg}");
				return false;
			}
			return (Object)(object)_panel != (Object)null;
		}

		private void Build()
		{
			//IL_007e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0083: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c9: Expected O, but got Unknown
			//IL_00d0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00da: Expected O, but got Unknown
			//IL_00f7: Unknown result type (might be due to invalid IL or missing references)
			//IL_0111: Unknown result type (might be due to invalid IL or missing references)
			//IL_012b: Unknown result type (might be due to invalid IL or missing references)
			//IL_013b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0150: Unknown result type (might be due to invalid IL or missing references)
			//IL_0160: Unknown result type (might be due to invalid IL or missing references)
			//IL_016b: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a9: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f1: Unknown result type (might be due to invalid IL or missing references)
			//IL_020c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0211: Unknown result type (might be due to invalid IL or missing references)
			//IL_021c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0226: Unknown result type (might be due to invalid IL or missing references)
			//IL_0231: Unknown result type (might be due to invalid IL or missing references)
			//IL_023b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0246: Unknown result type (might be due to invalid IL or missing references)
			//IL_0250: Unknown result type (might be due to invalid IL or missing references)
			//IL_025b: Unknown result type (might be due to invalid IL or missing references)
			//IL_026f: Unknown result type (might be due to invalid IL or missing references)
			//IL_029b: Unknown result type (might be due to invalid IL or missing references)
			//IL_02e3: Unknown result type (might be due to invalid IL or missing references)
			//IL_038f: Unknown result type (might be due to invalid IL or missing references)
			//IL_03de: Unknown result type (might be due to invalid IL or missing references)
			//IL_0420: Unknown result type (might be due to invalid IL or missing references)
			//IL_0435: Unknown result type (might be due to invalid IL or missing references)
			_rows.Clear();
			_kindLines.Clear();
			_windowTabs.Clear();
			_channelTabs.Clear();
			_windows = new List<int>();
			_content = null;
			_viewport = null;
			_summary = null;
			_empty = null;
			Transform val = UiStyle.FindCanvas();
			if ((Object)(object)val == (Object)null || !UiStyle.ReadStyle())
			{
				Plugin.Debug("The detail window waits for the game UI");
				return;
			}
			Vector2 value = Plugin.Settings.DetailSize.Value;
			GameObject val2 = new GameObject("CombatStats Detail", new Type[4]
			{
				typeof(RectTransform),
				typeof(Canvas),
				typeof(GraphicRaycaster),
				typeof(Image)
			});
			_panel = (RectTransform)val2.transform;
			((Transform)_panel).SetParent(val, false);
			_panel.anchorMin = new Vector2(0.5f, 0.5f);
			_panel.anchorMax = new Vector2(0.5f, 0.5f);
			_panel.pivot = new Vector2(0.5f, 0.5f);
			_panel.anchoredPosition = Vector2.zero;
			_panel.sizeDelta = new Vector2(Mathf.Max(600f, value.x), Mathf.Max(280f, value.y));
			Canvas component = val2.GetComponent<Canvas>();
			component.overrideSorting = true;
			component.sortingOrder = 100;
			UiStyle.StylePanel(val2.GetComponent<Image>());
			TMP_Text obj = UiStyle.Text("Title", _panel, 20f, (TextAlignmentOptions)4097, UiStyle.FontColor);
			UiStyle.PlaceTopLeft(obj.rectTransform, 14f, 10f, 300f, 26f);
			obj.text = "Combat stats";
			RectTransform val3 = (RectTransform)((Component)UiStyle.TextButton("Settings", _panel, "settings", 14f, UiStyle.Dim, OpenSettings)).transform;
			val3.anchorMin = new Vector2(1f, 1f);
			val3.anchorMax = new Vector2(1f, 1f);
			val3.pivot = new Vector2(1f, 1f);
			val3.anchoredPosition = new Vector2(-14f, -12f);
			val3.sizeDelta = new Vector2(84f, 22f);
			BuildWindowTabs();
			BuildChannelTabs();
			_summary = UiStyle.Text("Summary", _panel, 13f, (TextAlignmentOptions)4097, UiStyle.Dim);
			UiStyle.PlaceTopStretch(_summary.rectTransform, 14f, 14f, 100f, 36f);
			TMP_Text obj2 = UiStyle.Text("Header", _panel, 11f, (TextAlignmentOptions)4097, UiStyle.Dim);
			UiStyle.PlaceTopStretch(obj2.rectTransform, 14f, 14f, 148f, 14f);
			obj2.text = "Player";
			Column("Total", 250f, 152f);
			Column("DPS", 145f, 75f);
			_rateHeading = ((Component)((Transform)_panel).Find("ColumnDPS")).GetComponent<TMP_Text>();
			Column("Share", 68f, 0f);
			Image obj3 = UiStyle.Box("Separator", _panel, new Color(0.45f, 0.38f, 0.25f, 0.5f));
			obj3.sprite = UiStyle.Flat();
			UiStyle.PlaceTopStretch(((Graphic)obj3).rectTransform, 14f, 14f, 164f, 1f);
			BuildScroll();
			_empty = UiStyle.Text("Empty", _viewport, 14f, (TextAlignmentOptions)514, UiStyle.Dim);
			_empty.text = "No combat recorded for this selection.";
			((Component)_empty).gameObject.SetActive(false);
			val2.SetActive(false);
			Plugin.Debug($"Detail window built ({_panel.sizeDelta.x}x{_panel.sizeDelta.y})");
		}

		private void BuildScroll()
		{
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Expected O, but got Unknown
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: Expected O, but got Unknown
			//IL_0095: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c3: Expected O, but got Unknown
			//IL_00ca: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d4: Expected O, but got Unknown
			//IL_00f6: Unknown result type (might be due to invalid IL or missing references)
			//IL_0110: Unknown result type (might be due to invalid IL or missing references)
			//IL_012a: Unknown result type (might be due to invalid IL or missing references)
			//IL_013a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0154: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = new GameObject("Viewport", new Type[3]
			{
				typeof(RectTransform),
				typeof(RectMask2D),
				typeof(Image)
			});
			_viewport = (RectTransform)val.transform;
			((Transform)_viewport).SetParent((Transform)(object)_panel, false);
			UiStyle.Stretch(_viewport, 14f, 14f, 172f, 14f);
			Image component = val.GetComponent<Image>();
			((Graphic)component).color = new Color(0f, 0f, 0f, 0f);
			((Graphic)component).raycastTarget = true;
			GameObject val2 = new GameObject("Content", new Type[1] { typeof(RectTransform) });
			_content = (RectTransform)val2.transform;
			((Transform)_content).SetParent((Transform)(object)_viewport, false);
			_content.anchorMin = new Vector2(0f, 1f);
			_content.anchorMax = new Vector2(1f, 1f);
			_content.pivot = new Vector2(0f, 1f);
			_content.anchoredPosition = Vector2.zero;
			_content.sizeDelta = new Vector2(0f, 0f);
		}

		private void Column(string text, float from, float to)
		{
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0051: 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_007e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0095: Unknown result type (might be due to invalid IL or missing references)
			TMP_Text obj = UiStyle.Text("Column" + text, _panel, 11f, (TextAlignmentOptions)4100, UiStyle.Dim);
			obj.text = text;
			RectTransform rectTransform = obj.rectTransform;
			rectTransform.anchorMin = new Vector2(1f, 1f);
			rectTransform.anchorMax = new Vector2(1f, 1f);
			rectTransform.pivot = new Vector2(1f, 1f);
			rectTransform.offsetMin = new Vector2(0f - (from + 14f), -162f);
			rectTransform.offsetMax = new Vector2(0f - (to + 14f), -148f);
		}

		private void BuildWindowTabs()
		{
			//IL_00d1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f1: Unknown result type (might be due to invalid IL or missing references)
			//IL_010b: Expected O, but got Unknown
			//IL_0151: Unknown result type (might be due to invalid IL or missing references)
			//IL_016c: Unknown result type (might be due to invalid IL or missing references)
			//IL_018c: Expected O, but got Unknown
			//IL_01a1: Unknown result type (might be due to invalid IL or missing references)
			//IL_01bc: Unknown result type (might be due to invalid IL or missing references)
			//IL_01dc: Expected O, but got Unknown
			//IL_01f1: Unknown result type (might be due to invalid IL or missing references)
			//IL_020c: Unknown result type (might be due to invalid IL or missing references)
			//IL_022c: Expected O, but got Unknown
			foreach (Button windowTab in _windowTabs)
			{
				Object.Destroy((Object)(object)((Component)windowTab).gameObject);
			}
			_windowTabs.Clear();
			if ((Object)(object)_panel == (Object)null)
			{
				return;
			}
			_windows = new List<int> { 0, 1, 2, 3 };
			float num = 14f;
			foreach (int window in _windows)
			{
				int captured = window;
				Button val = UiStyle.TextButton("Selection" + window, _panel, Label(window), 14f, UiStyle.Dim, delegate
				{
					_window = captured;
					_scrolled = 0f;
					Refresh();
				});
				UiStyle.PlaceTopLeft((RectTransform)((Component)val).transform, num, 42f, 72f, 22f);
				num += 74f;
				_windowTabs.Add(val);
			}
			UiStyle.PlaceTopLeft((RectTransform)((Component)UiStyle.TextButton("Earlier", _panel, "Earlier", 13f, UiStyle.Dim, delegate
			{
				_window = 3;
				_historyIndex = Math.Min(Plugin.View.HistoryCount - 1, _historyIndex + 1);
				_scrolled = 0f;
				Refresh();
			})).transform, num + 8f, 42f, 65f, 22f);
			UiStyle.PlaceTopLeft((RectTransform)((Component)UiStyle.TextButton("Later", _panel, "Later", 13f, UiStyle.Dim, delegate
			{
				_window = 3;
				_historyIndex = Math.Max(0, _historyIndex - 1);
				_scrolled = 0f;
				Refresh();
			})).transform, num + 76f, 42f, 65f, 22f);
			UiStyle.PlaceTopLeft((RectTransform)((Component)UiStyle.TextButton("Reset", _panel, "Reset", 13f, UiStyle.Dim, delegate
			{
				if (!Plugin.View.Reset() && Object.op_Implicit((Object)(object)MessageHud.instance))
				{
					MessageHud.instance.ShowMessage((MessageType)1, "Finish combat before resetting history.", 0, (Sprite)null, false, true);
				}
				_historyIndex = 0;
				Refresh();
			})).transform, num + 150f, 42f, 65f, 22f);
		}

		private void BuildChannelTabs()
		{
			//IL_0068: Unknown result type (might be due to invalid IL or missing references)
			//IL_0088: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a2: Expected O, but got Unknown
			//IL_00d9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f6: Unknown result type (might be due to invalid IL or missing references)
			//IL_0116: Expected O, but got Unknown
			if ((Object)(object)_panel == (Object)null)
			{
				return;
			}
			float num = 14f;
			CombatChannel[] channels = Channels;
			for (int i = 0; i < channels.Length; i++)
			{
				CombatChannel combatChannel = channels[i];
				CombatChannel captured = combatChannel;
				Button val = UiStyle.TextButton("Channel" + combatChannel, _panel, Label(combatChannel), 13f, UiStyle.Dim, delegate
				{
					_channel = captured;
					_scrolled = 0f;
					Refresh();
				});
				UiStyle.PlaceTopLeft((RectTransform)((Component)val).transform, num, 70f, 130f, 20f);
				num += 132f;
				_channelTabs.Add(val);
			}
			Button val2 = UiStyle.TextButton("Breakdown", _panel, "Types", 13f, UiStyle.Dim, delegate
			{
				_breakdown = (_breakdown + 1) % 4;
				_scrolled = 0f;
				Refresh();
			});
			UiStyle.PlaceTopLeft((RectTransform)((Component)val2).transform, num + 10f, 70f, 100f, 20f);
			_breakdownLabel = UiStyle.LabelOf(val2);
		}

		private void OpenSettings()
		{
			(SettingsPanel.Instance ?? ((Component)this).gameObject.AddComponent<SettingsPanel>()).Toggle();
		}

		private static string Label(int selection)
		{
			return (new string[4] { "Current", "Last", "Overall", "History" })[selection];
		}

		private static string Label(CombatChannel channel)
		{
			return channel switch
			{
				CombatChannel.Healing => "Healing received", 
				CombatChannel.DamageTaken => "Damage taken", 
				_ => "Damage done", 
			};
		}
	}
	internal sealed class MeterRow
	{
		public const float LineHeight = 17f;

		public const float BarHeight = 6f;

		private const float BarGap = 2f;

		private const float RowGap = 4f;

		private static readonly NumberFormatInfo Numbers = BuildNumberFormat();

		private readonly TMP_Text _name;

		private readonly TMP_Text _value;

		private readonly TMP_Text _share;

		private readonly TMP_Text _rate;

		private readonly RectTransform _bar;

		private readonly Image[] _segments = (Image[])(object)new Image[11];

		private readonly bool _detailed;

		public RectTransform Root { get; }

		public long Id { get; private set; }

		private MeterRow(RectTransform root, bool detailed, TMP_Text name, TMP_Text value, TMP_Text share, TMP_Text rate, RectTransform bar)
		{
			Root = root;
			_detailed = detailed;
			_name = name;
			_value = value;
			_share = share;
			_rate = rate;
			_bar = bar;
		}

		public static float Height(bool bars)
		{
			if (!bars)
			{
				return 21f;
			}
			return 29f;
		}

		public static MeterRow Create(RectTransform parent, bool detailed, Action<long> onClick)
		{
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Expected O, but got Unknown
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Expected O, but got Unknown
			//IL_004a: 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_0074: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d3: 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_0113: Unknown result type (might be due to invalid IL or missing references)
			//IL_0133: 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_018b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0195: Expected O, but got Unknown
			GameObject val = new GameObject("Row", new Type[1] { typeof(RectTransform) });
			RectTransform val2 = (RectTransform)val.transform;
			((Transform)val2).SetParent((Transform)(object)parent, false);
			val2.anchorMin = new Vector2(0f, 1f);
			val2.anchorMax = new Vector2(1f, 1f);
			val2.pivot = new Vector2(0f, 1f);
			if (onClick != null)
			{
				Image val3 = val.AddComponent<Image>();
				((Graphic)val3).color = new Color(1f, 1f, 1f, 0f);
				Button obj = val.AddComponent<Button>();
				ColorBlock colors = ((Selectable)obj).colors;
				((ColorBlock)(ref colors)).normalColor = new Color(1f, 1f, 1f, 0f);
				((ColorBlock)(ref colors)).highlightedColor = new Color(1f, 1f, 1f, 0.1f);
				((ColorBlock)(ref colors)).pressedColor = new Color(1f, 1f, 1f, 0.18f);
				((ColorBlock)(ref colors)).selectedColor = new Color(1f, 1f, 1f, 0f);
				((ColorBlock)(ref colors)).disabledColor = new Color(1f, 1f, 1f, 0f);
				((ColorBlock)(ref colors)).fadeDuration = 0.05f;
				((Selectable)obj).colors = colors;
				((Selectable)obj).targetGraphic = (Graphic)(object)val3;
				MeterRow created = null;
				((UnityEvent)obj.onClick).AddListener((UnityAction)delegate
				{
					if (created != null)
					{
						onClick(created.Id);
					}
				});
				return created = Build(val2, detailed);
			}
			return Build(val2, detailed);
		}

		public void Apply(CombatantRow row, float leader, float width, bool bars, bool showShare, bool local, string prefix)
		{
			//IL_006f: 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_013a: Unknown result type (might be due to invalid IL or missing references)
			//IL_019e: Unknown result type (might be due to invalid IL or missing references)
			Id = row.Id;
			string text = (string.IsNullOrEmpty(row.Name) ? "..." : row.Name);
			if (row.Id < -1099511627776L && Localization.instance != null)
			{
				text = Localization.instance.Localize(text);
			}
			_name.text = prefix + text;
			((Graphic)_name).color = (local ? UiStyle.Mine : UiStyle.FontColor);
			string text2 = Format(row.Total);
			if (_detailed)
			{
				_value.text = text2;
				if ((Object)(object)_share != (Object)null)
				{
					_share.text = Percent(row.Share);
				}
				if ((Object)(object)_rate != (Object)null)
				{
					_rate.text = Format(row.Dps);
				}
			}
			else
			{
				_value.text = (showShare ? (text2 + "  " + Format(row.Dps) + "/s  <color=#" + ColorUtility.ToHtmlStringRGB(UiStyle.Dim) + ">" + Percent(row.Share) + "</color>") : (text2 + "  " + Format(row.Dps) + "/s"));
			}
			((Component)_bar).gameObject.SetActive(bars);
			if (bars)
			{
				Layout(row, leader, width);
			}
			Root.sizeDelta = new Vector2(0f, Height(bars));
		}

		public void SetActive(bool active)
		{
			if (((Component)Root).gameObject.activeSelf != active)
			{
				((Component)Root).gameObject.SetActive(active);
			}
		}

		public void Place(float y)
		{
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			Root.anchoredPosition = new Vector2(0f, 0f - y);
		}

		private static MeterRow Build(RectTransform root, bool detailed)
		{
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			//IL_006c: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ec: Unknown result type (might be due to invalid IL or missing references)
			//IL_0146: Unknown result type (might be due to invalid IL or missing references)
			//IL_0150: Unknown result type (might be due to invalid IL or missing references)
			//IL_0157: Expected O, but got Unknown
			//IL_016c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0182: Unknown result type (might be due to invalid IL or missing references)
			//IL_0198: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ae: 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_01e9: Unknown result type (might be due to invalid IL or missing references)
			//IL_0246: Unknown result type (might be due to invalid IL or missing references)
			//IL_0270: Unknown result type (might be due to invalid IL or missing references)
			//IL_0285: Unknown result type (might be due to invalid IL or missing references)
			//IL_029a: Unknown result type (might be due to invalid IL or missing references)
			//IL_02a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_02b5: Unknown result type (might be due to invalid IL or missing references)
			//IL_02c0: Unknown result type (might be due to invalid IL or missing references)
			//IL_02cf: Unknown result type (might be due to invalid IL or missing references)
			TMP_Text val = UiStyle.Text("Name", root, 15f, (TextAlignmentOptions)4097, UiStyle.FontColor);
			val.richText = false;
			Line(val.rectTransform);
			val.rectTransform.offsetMax = new Vector2(detailed ? (-250f) : (-185f), val.rectTransform.offsetMax.y);
			TMP_Text val2 = UiStyle.Text("Value", root, 15f, (TextAlignmentOptions)4100, UiStyle.FontColor);
			Line(val2.rectTransform);
			TMP_Text val3 = null;
			TMP_Text val4 = null;
			if (detailed)
			{
				PinRight(val2.rectTransform, 250f, 152f);
				val3 = UiStyle.Text("Share", root, 13f, (TextAlignmentOptions)4100, UiStyle.Dim);
				Line(val3.rectTransform);
				PinRight(val3.rectTransform, 68f, 0f);
				val4 = UiStyle.Text("Rate", root, 13f, (TextAlignmentOptions)4100, UiStyle.Dim);
				Line(val4.rectTransform);
				PinRight(val4.rectTransform, 145f, 75f);
			}
			else
			{
				PinRight(val2.rectTransform, 185f, 0f);
			}
			RectTransform val5 = (RectTransform)new GameObject("Bar", new Type[1] { typeof(RectTransform) }).transform;
			((Transform)val5).SetParent((Transform)(object)root, false);
			val5.anchorMin = new Vector2(0f, 1f);
			val5.anchorMax = new Vector2(1f, 1f);
			val5.pivot = new Vector2(0f, 1f);
			val5.anchoredPosition = new Vector2(0f, -19f);
			val5.sizeDelta = new Vector2(0f, 6f);
			Image obj = UiStyle.Box("Back", val5, new Color(0f, 0f, 0f, 0.45f));
			obj.sprite = UiStyle.Flat();
			UiStyle.Stretch(((Graphic)obj).rectTransform, 0f, 0f, 0f, 0f);
			MeterRow meterRow = new MeterRow(root, detailed, val, val2, val3, val4, val5);
			for (int i = 0; i < 11; i++)
			{
				Image val6 = UiStyle.Box("Kind" + i, val5, Color.white);
				val6.sprite = UiStyle.Flat();
				RectTransform rectTransform = ((Graphic)val6).rectTransform;
				rectTransform.anchorMin = new Vector2(0f, 0f);
				rectTransform.anchorMax = new Vector2(0f, 1f);
				rectTransform.pivot = new Vector2(0f, 0.5f);
				rectTransform.offsetMin = new Vector2(rectTransform.offsetMin.x, 0f);
				rectTransform.offsetMax = new Vector2(rectTransform.offsetMax.x, 0f);
				((Component)val6).gameObject.SetActive(false);
				meterRow._segments[i] = val6;
			}
			return meterRow;
		}

		private static void Line(RectTransform rect)
		{
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			rect.anchorMin = new Vector2(0f, 1f);
			rect.anchorMax = new Vector2(1f, 1f);
			rect.pivot = new Vector2(0.5f, 1f);
			rect.offsetMin = new Vector2(0f, -17f);
			rect.offsetMax = new Vector2(0f, 0f);
		}

		private static void PinRight(RectTransform rect, float from, float to)
		{
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: 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_0059: Unknown result type (might be due to invalid IL or missing references)
			rect.anchorMin = new Vector2(1f, 1f);
			rect.anchorMax = new Vector2(1f, 1f);
			rect.pivot = new Vector2(1f, 1f);
			rect.offsetMin = new Vector2(0f - from, -17f);
			rect.offsetMax = new Vector2(0f - to, 0f);
		}

		private static NumberFormatInfo BuildNumberFormat()
		{
			NumberFormatInfo obj = (NumberFormatInfo)CultureInfo.InvariantCulture.NumberFormat.Clone();
			obj.NumberGroupSeparator = " ";
			return obj;
		}

		public static string Format(float value)
		{
			if (!(value >= 10f))
			{
				return value.ToString("0.#", CultureInfo.InvariantCulture);
			}
			return value.ToString("#,0", Numbers);
		}

		private static string Percent(float share)
		{
			return (share * 100f).ToString("0", CultureInfo.InvariantCulture) + "%";
		}

		private void Layout(CombatantRow row, float leader, float width)
		{
			//IL_009c: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ad: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bb: Unknown result type (might be due to invalid IL or missing references)
			float num = ((leader > 0f) ? (Mathf.Clamp01(row.Total / leader) * width) : 0f);
			Color[] colors = UiStyle.Colors;
			float num2 = 0f;
			for (int i = 0; i < 11; i++)
			{
				Image val = _segments[i];
				float num3 = row.ByKind[i];
				if (num3 <= 0f || row.Total <= 0f || num <= 0f)
				{
					if (((Component)val).gameObject.activeSelf)
					{
						((Component)val).gameObject.SetActive(false);
					}
					continue;
				}
				float num4 = num * (num3 / row.Total);
				RectTransform rectTransform = ((Graphic)val).rectTransform;
				rectTransform.anchoredPosition = new Vector2(num2, 0f);
				rectTransform.sizeDelta = new Vector2(num4, 0f);
				((Graphic)val).color = colors[i];
				if (!((Component)val).gameObject.activeSelf)
				{
					((Component)val).gameObject.SetActive(true);
				}
				num2 += num4;
			}
		}
	}
	internal sealed class SettingsPanel : MonoBehaviour
	{
		private sealed class Control
		{
			private readonly TMP_Text _value;

			private readonly Func<string> _read;

			public Control(TMP_Text value, Func<string> read)
			{
				_value = value;
				_read = read;
			}

			public void Refresh()
			{
				_value.text = _read();
			}
		}

		private const float Padding = 16f;

		private const float RowHeight = 24f;

		private const float ColumnWidth = 300f;

		private readonly List<Control> _rows = new List<Control>();

		private RectTransform _panel;

		private bool _failed;

		private bool _open;

		public static SettingsPanel Instance { get; private set; }

		public static bool IsOpen { get; private set; }

		public void Toggle()
		{
			if (_open)
			{
				Close();
			}
			else if (EnsureUi() && !((Object)(object)_panel == (Object)null))
			{
				_open = true;
				IsOpen = true;
				((Component)_panel).gameObject.SetActive(true);
				RefreshValues();
			}
		}

		public void Close()
		{
			_open = false;
			IsOpen = false;
			if ((Object)(object)_panel != (Object)null)
			{
				((Component)_panel).gameObject.SetActive(false);
			}
		}

		private void Awake()
		{
			Instance = this;
		}

		private void Update()
		{
			if (_open && (Object)(object)_panel == (Object)null)
			{
				Close();
			}
		}

		private void OnDestroy()
		{
			if ((Object)(object)Instance == (Object)(object)this)
			{
				Instance = null;
				IsOpen = false;
			}
		}

		private void RefreshValues()
		{
			foreach (Control row in _rows)
			{
				row.Refresh();
			}
		}

		private bool EnsureUi()
		{
			if (_failed)
			{
				return false;
			}
			if ((Object)(object)_panel != (Object)null)
			{
				return true;
			}
			try
			{
				Build();
			}
			catch (Exception arg)
			{
				_failed = true;
				if ((Object)(object)_panel != (Object)null)
				{
					Object.Destroy((Object)(object)((Component)_panel).gameObject);
					_panel = null;
				}
				Plugin.Log.LogError((object)$"Could not build the settings panel; the config file still works: {arg}");
				return false;
			}
			return (Object)(object)_panel != (Object)null;
		}

		private void Build()
		{
			//IL_0067: Unknown result type (might be due to invalid IL or missing references)
			//IL_006d: Expected O, but got Unknown
			//IL_0074: Unknown result type (might be due to invalid IL or missing references)
			//IL_007e: Expected O, but got Unknown
			//IL_009b: 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_00cf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00df: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f9: Unknown result type (might be due to invalid IL or missing references)
			//IL_0137: Unknown result type (might be due to invalid IL or missing references)
			//IL_017f: Unknown result type (might be due to invalid IL or missing references)
			//IL_019a: Unknown result type (might be due to invalid IL or missing references)
			//IL_019f: 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_01b4: 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)
			//IL_01c9: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d4: 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_01e9: Unknown result type (might be due to invalid IL or missing references)
			//IL_01fd: Unknown result type (might be due to invalid IL or missing references)
			//IL_0479: Unknown result type (might be due to invalid IL or missing references)
			//IL_04bb: Unknown result type (might be due to invalid IL or missing references)
			//IL_04d6: Unknown result type (might be due to invalid IL or missing references)
			//IL_04ec: Unknown result type (might be due to invalid IL or missing references)
			_rows.Clear();
			Transform val = UiStyle.FindCanvas();
			if (!((Object)(object)val == (Object)null) && UiStyle.ReadStyle())
			{
				GameObject val2 = new GameObject("CombatStats Settings", new Type[4]
				{
					typeof(RectTransform),
					typeof(Canvas),
					typeof(GraphicRaycaster),
					typeof(Image)
				});
				_panel = (RectTransform)val2.transform;
				((Transform)_panel).SetParent(val, false);
				_panel.anchorMin = new Vector2(0.5f, 0.5f);
				_panel.anchorMax = new Vector2(0.5f, 0.5f);
				_panel.pivot = new Vector2(0.5f, 0.5f);
				_panel.anchoredPosition = Vector2.zero;
				_panel.sizeDelta = new Vector2(680f, 470f);
				Canvas component = val2.GetComponent<Canvas>();
				component.overrideSorting = true;
				component.sortingOrder = 110;
				UiStyle.StylePanel(val2.GetComponent<Image>());
				TMP_Text obj = UiStyle.Text("Title", _panel, 19f, (TextAlignmentOptions)4097, UiStyle.FontColor);
				UiStyle.PlaceTopLeft(obj.rectTransform, 16f, 10f, 400f, 24f);
				obj.text = "Combat Stats - settings";
				RectTransform val3 = (RectTransform)((Component)UiStyle.TextButton("Close", _panel, "close", 14f, UiStyle.Dim, Close)).transform;
				val3.anchorMin = new Vector2(1f, 1f);
				val3.anchorMax = new Vector2(1f, 1f);
				val3.pivot = new Vector2(1f, 1f);
				val3.anchoredPosition = new Vector2(-16f, -12f);
				val3.sizeDelta = new Vector2(70f, 22f);
				ModConfig settings = Plugin.Settings;
				float x = 16f;
				float x2 = 356f;
				float y = 46f;
				y = Heading("Compact window", x, y);
				y = Choice(x, y, "When to show", () => Word(settings.CompactMode.Value), delegate(int step)
				{
					settings.CompactMode.Value = (MeterMode)Wrap((int)(settings.CompactMode.Value + step), 3);
				});
				y = Number(x, y, "Rows", () => settings.CompactRows.Value.ToString(CultureInfo.InvariantCulture), delegate(int step)
				{
					settings.CompactRows.Value = Mathf.Clamp(settings.CompactRows.Value + step, 1, 12);
				});
				y = Choice(x, y, "Cor