Decompiled source of StageDebrief v1.0.0

plugins/StageDebrief/StageDebrief.dll

Decompiled 10 hours ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HG.Reflection;
using RoR2;
using RoR2.Stats;
using RoR2.UI;
using StageDebrief.Core;
using StageDebrief.Data;
using StageDebrief.Tracking;
using StageDebrief.UI;
using TMPro;
using UnityEngine;
using UnityEngine.AddressableAssets;
using UnityEngine.Networking;
using UnityEngine.ResourceManagement.AsyncOperations;
using UnityEngine.SceneManagement;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: OptIn]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("StageDebrief")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("StageDebrief")]
[assembly: AssemblyTitle("StageDebrief")]
[assembly: AssemblyVersion("1.0.0.0")]
namespace StageDebrief
{
	[BepInPlugin("com.stagedebrief.stagedebrief", "StageDebrief", "1.0.0")]
	public sealed class StageDebriefPlugin : BaseUnityPlugin
	{
		public const string PluginGuid = "com.stagedebrief.stagedebrief";

		public const string PluginName = "StageDebrief";

		public const string PluginVersion = "1.0.0";

		private DebriefPanel panel;

		internal static PluginConfig Settings { get; private set; }

		internal static RunTracker Tracker { get; private set; }

		private void Awake()
		{
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Expected O, but got Unknown
			//IL_0065: Unknown result type (might be due to invalid IL or missing references)
			Log.Init(((BaseUnityPlugin)this).Logger);
			Settings = new PluginConfig(((BaseUnityPlugin)this).Config);
			Tracker = new RunTracker();
			Tracker.Enable();
			GameObject val = new GameObject("StageDebrief.UI");
			Object.DontDestroyOnLoad((Object)(object)val);
			panel = val.AddComponent<DebriefPanel>();
			Log.Info(string.Format("{0} {1} loaded. Shows automatically when you leave a stage; {2} to peek.", "StageDebrief", "1.0.0", Settings.ShowKey.Value));
		}

		private void Update()
		{
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				Tracker.Tick(Time.unscaledTime);
				if (Object.op_Implicit((Object)(object)panel))
				{
					KeyboardShortcut value = Settings.ShowKey.Value;
					if (((KeyboardShortcut)(ref value)).IsDown())
					{
						panel.ToggleManual();
					}
				}
			}
			catch (Exception e)
			{
				Log.ErrorOnce("plugin-update", e);
			}
		}

		private void OnDestroy()
		{
			Tracker?.Disable();
			if (Object.op_Implicit((Object)(object)panel))
			{
				Object.Destroy((Object)(object)((Component)panel).gameObject);
			}
		}
	}
}
namespace StageDebrief.UI
{
	internal static class AssetCache<T> where T : Object
	{
		private static readonly Dictionary<string, T> cache = new Dictionary<string, T>();

		private static readonly Dictionary<string, List<Action<T>>> waiting = new Dictionary<string, List<Action<T>>>();

		public static T Get(string key, AssetReference reference, T direct, Action<T> onLoaded)
		{
			//IL_00ec: 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)
			if (string.IsNullOrEmpty(key))
			{
				return direct;
			}
			if (cache.TryGetValue(key, out var value) && Object.op_Implicit((Object)(object)value))
			{
				return value;
			}
			if (reference == null || !reference.RuntimeKeyIsValid())
			{
				if (Object.op_Implicit((Object)(object)direct))
				{
					cache[key] = direct;
				}
				return direct;
			}
			if (waiting.TryGetValue(key, out var callbacks))
			{
				if (onLoaded != null)
				{
					callbacks.Add(onLoaded);
				}
				return direct;
			}
			callbacks = new List<Action<T>>();
			if (onLoaded != null)
			{
				callbacks.Add(onLoaded);
			}
			waiting[key] = callbacks;
			try
			{
				AsyncOperationHandle<T> val = Addressables.LoadAssetAsync<T>(reference.RuntimeKey);
				val.Completed += delegate(AsyncOperationHandle<T> op)
				{
					//IL_0013: Unknown result type (might be due to invalid IL or missing references)
					//IL_0019: Invalid comparison between Unknown and I4
					waiting.Remove(key);
					T val2 = (((int)op.Status == 1) ? op.Result : default(T));
					if (!Object.op_Implicit((Object)(object)val2))
					{
						val2 = direct;
					}
					if (!Object.op_Implicit((Object)(object)val2))
					{
						Log.WarnOnce("asset-" + key, "Could not load " + typeof(T).Name + " for '" + key + "'.");
						return;
					}
					cache[key] = val2;
					foreach (Action<T> item in callbacks)
					{
						try
						{
							item(val2);
						}
						catch (Exception e2)
						{
							Log.ErrorOnce("asset-callback", e2);
						}
					}
				};
			}
			catch (Exception e)
			{
				waiting.Remove(key);
				Log.ErrorOnce("asset-load-" + key, e);
			}
			return direct;
		}
	}
	internal sealed class DebriefPanel : MonoBehaviour
	{
		private enum Mode
		{
			Hidden,
			Auto,
			Manual
		}

		private const int CanvasSortingOrder = 10;

		private const float Pad = 20f;

		private const float MapWidth = 264f;

		private const float MapHeight = 160f;

		private const int MaxItemIcons = 15;

		private const int MaxThumbs = 10;

		private const float LootRowHeight = 28f;

		private const float LootChipWidth = 112f;

		private static readonly (string key, string label)[] TileStats = new(string, string)[5]
		{
			("totalKills", "Kills"),
			("totalDamageDealt", "Damage"),
			("totalGoldCollected", "Gold"),
			("totalDamageTaken", "Taken"),
			("totalDeaths", "Deaths")
		};

		private GameObject canvasObject;

		private CanvasGroup visibilityGroup;

		private CanvasGroup showGroup;

		private RectTransform card;

		private Image cardBackground;

		private RawImage mapImage;

		private TextMeshProUGUI mapFallback;

		private TextMeshProUGUI header;

		private TextMeshProUGUI title;

		private TextMeshProUGUI subtitle;

		private StatTile[] tiles;

		private RectTransform itemsRow;

		private TextMeshProUGUI itemsEmpty;

		private RectTransform runStrip;

		private TextMeshProUGUI hint;

		private RectTransform lootRow;

		private TextMeshProUGUI lootMissed;

		private string lootSignature = "";

		private Mode mode;

		private StageRecord shown;

		private bool shownAsComplete;

		private string itemsSignature = "";

		private float autoHideAt;

		private Coroutine fade;

		private float baseY;

		private static readonly Dictionary<LootCategory, string> LootLabels = new Dictionary<LootCategory, string>
		{
			{
				LootCategory.Chests,
				"CHESTS"
			},
			{
				LootCategory.Shops,
				"SHOPS"
			},
			{
				LootCategory.Drones,
				"DRONES"
			},
			{
				LootCategory.Shrines,
				"SHRINES"
			},
			{
				LootCategory.Barrels,
				"BARRELS"
			}
		};

		private void OnEnable()
		{
			RunTracker tracker = StageDebriefPlugin.Tracker;
			if (tracker != null)
			{
				tracker.ExitStarted += OnExitStarted;
				tracker.StageOpened += OnStageOpened;
				tracker.RunEnded += OnRunEnded;
				tracker.Changed += OnTrackerChanged;
			}
		}

		private void OnDisable()
		{
			RunTracker tracker = StageDebriefPlugin.Tracker;
			if (tracker != null)
			{
				tracker.ExitStarted -= OnExitStarted;
				tracker.StageOpened -= OnStageOpened;
				tracker.RunEnded -= OnRunEnded;
				tracker.Changed -= OnTrackerChanged;
				tracker.FastRefresh = false;
			}
		}

		private void OnDestroy()
		{
			if (Object.op_Implicit((Object)(object)canvasObject))
			{
				Object.Destroy((Object)(object)canvasObject);
			}
		}

		private void OnExitStarted(StageRecord stage)
		{
			if (StageDebriefPlugin.Settings.AutoShowOnExit.Value)
			{
				Show(stage, Mode.Auto, complete: true);
			}
			else if (mode == Mode.Manual && shown == stage)
			{
				shownAsComplete = true;
				Populate(animate: false);
			}
		}

		private void OnStageOpened(StageRecord stage)
		{
			Hide();
		}

		private void OnRunEnded(RunRecord run)
		{
			Hide();
		}

		private void OnTrackerChanged()
		{
			if (mode != Mode.Hidden && shown != null)
			{
				Populate(animate: false);
			}
		}

		public void ToggleManual()
		{
			if (mode != Mode.Hidden)
			{
				Hide();
				return;
			}
			StageRecord stageRecord = ((!Object.op_Implicit((Object)(object)Run.instance)) ? null : StageDebriefPlugin.Tracker?.CurrentRun?.CurrentStage);
			if (stageRecord == null)
			{
				Log.Debug("Peek key ignored: not in a run.");
				return;
			}
			LootScanner.Scan(stageRecord, final: false);
			Show(stageRecord, Mode.Manual, stageRecord.Closed);
		}

		private void Update()
		{
			if (Object.op_Implicit((Object)(object)canvasObject) && mode != Mode.Hidden)
			{
				bool flag = GameCompat.IsConsoleOpen() || Time.timeScale < 0.01f;
				visibilityGroup.alpha = (flag ? 0f : 1f);
				if (!Object.op_Implicit((Object)(object)Run.instance) || (mode == Mode.Auto && Time.unscaledTime >= autoHideAt))
				{
					Hide();
				}
			}
		}

		private void Show(StageRecord stage, Mode newMode, bool complete)
		{
			//IL_0099: Unknown result type (might be due to invalid IL or missing references)
			//IL_009e: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				EnsureBuilt();
			}
			catch (Exception e)
			{
				Log.ErrorOnce("panel-build", e);
				return;
			}
			bool num = mode == Mode.Hidden;
			mode = newMode;
			shown = stage;
			shownAsComplete = complete;
			itemsSignature = "";
			lootSignature = "";
			autoHideAt = Time.unscaledTime + StageDebriefPlugin.Settings.AutoHideAfter.Value;
			StageDebriefPlugin.Tracker.FastRefresh = true;
			ApplyLayoutSettings();
			canvasObject.SetActive(true);
			((TMP_Text)hint).text = ((newMode == Mode.Auto) ? "Closes when the next stage loads" : (((object)StageDebriefPlugin.Settings.ShowKey.Value/*cast due to .constrained prefix*/).ToString() + " to close"));
			Populate(animate: true);
			LogSummary(stage);
			if (num)
			{
				Animate(0.24f, 0f, 1f, Tween.MotionEnabled ? (-24f) : 0f, 0f, null);
			}
		}

		private void Hide()
		{
			if (mode == Mode.Hidden)
			{
				return;
			}
			mode = Mode.Hidden;
			if (StageDebriefPlugin.Tracker != null)
			{
				StageDebriefPlugin.Tracker.FastRefresh = false;
			}
			if (!Object.op_Implicit((Object)(object)canvasObject))
			{
				return;
			}
			Animate(0.18f, showGroup.alpha, 0f, 0f, 0f, delegate
			{
				if (mode == Mode.Hidden && Object.op_Implicit((Object)(object)canvasObject))
				{
					canvasObject.SetActive(false);
					shown = null;
				}
			});
		}

		private void Animate(float seconds, float fromAlpha, float toAlpha, float fromOffset, float toOffset, Action onDone)
		{
			if (fade != null)
			{
				((MonoBehaviour)this).StopCoroutine(fade);
			}
			fade = ((MonoBehaviour)this).StartCoroutine(Tween.Run(seconds, delegate(float t)
			{
				//IL_0050: Unknown result type (might be due to invalid IL or missing references)
				showGroup.alpha = Mathf.Lerp(fromAlpha, toAlpha, t);
				card.anchoredPosition = new Vector2(0f, baseY + Mathf.Lerp(fromOffset, toOffset, t));
			}, onDone));
		}

		private void ApplyLayoutSettings()
		{
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			PluginConfig settings = StageDebriefPlugin.Settings;
			baseY = settings.VerticalOffset.Value * 1080f;
			((Transform)card).localScale = Vector3.one * settings.UiScale.Value;
			Color panel = Theme.Panel;
			panel.a = settings.PanelOpacity.Value;
			((Graphic)cardBackground).color = panel;
		}

		private void Populate(bool animate)
		{
			try
			{
				RunRecord displayRun = StageDebriefPlugin.Tracker.DisplayRun;
				StageRecord stageRecord = shown;
				if (displayRun == null || stageRecord == null)
				{
					return;
				}
				PlayerInfo playerInfo = displayRun.LocalPlayer ?? displayRun.Players.Values.FirstOrDefault();
				string text = (stageRecord.IsIntermission ? "" : ("STAGE " + stageRecord.StageNumber));
				if (stageRecord.IsIntermission)
				{
					((TMP_Text)header).text = (shownAsComplete ? "LEAVING" : "IN PROGRESS");
				}
				else
				{
					((TMP_Text)header).text = (shownAsComplete ? (text + " COMPLETE") : (text + "  ·  IN PROGRESS"));
				}
				((TMP_Text)title).text = stageRecord.DisplayName;
				List<string> list = new List<string>();
				if (!string.IsNullOrEmpty(stageRecord.Subtitle))
				{
					list.Add(stageRecord.Subtitle);
				}
				list.Add(TrackedStats.Duration(stageRecord.Duration) + " on stage");
				list.Add(TrackedStats.Duration(stageRecord.EndTime) + " total");
				((TMP_Text)subtitle).text = string.Join("   ·   ", list);
				SetPicture(mapImage, mapFallback, stageRecord);
				bool flag = playerInfo != null && stageRecord.EndOf(playerInfo.Key).StatsAvailable;
				for (int i = 0; i < tiles.Length; i++)
				{
					(string, string) tuple = TileStats[i];
					string key = tuple.Item1;
					string item = tuple.Item2;
					TrackedStat trackedStat = TrackedStats.All.FirstOrDefault((TrackedStat s) => s.Key == key);
					tiles[i].SetLabel(item);
					if (!flag || trackedStat == null || !trackedStat.Available)
					{
						tiles[i].SetUnavailable();
					}
					else
					{
						tiles[i].SetValue(stageRecord.StatDelta(playerInfo.Key, key), trackedStat.Format, animate);
					}
				}
				if (playerInfo != null)
				{
					RebuildItems(stageRecord, playerInfo.Key);
				}
				RebuildLoot(stageRecord);
				if (animate)
				{
					RebuildRunStrip(displayRun, stageRecord);
				}
			}
			catch (Exception e)
			{
				Log.ErrorOnce("panel-populate", e);
			}
		}

		private static void LogSummary(StageRecord stage)
		{
			try
			{
				RunRecord displayRun = StageDebriefPlugin.Tracker.DisplayRun;
				PlayerInfo playerInfo = displayRun?.LocalPlayer ?? displayRun?.Players.Values.FirstOrDefault();
				if (playerInfo == null)
				{
					Log.Info($"Debrief shown for stage {stage.Index + 1}: no player recorded.");
					return;
				}
				PlayerSnapshot playerSnapshot = stage.EndOf(playerInfo.Key);
				List<string> list = new List<string>();
				(string, string)[] tileStats = TileStats;
				for (int i = 0; i < tileStats.Length; i++)
				{
					(string, string) tuple = tileStats[i];
					string key = tuple.Item1;
					string item = tuple.Item2;
					TrackedStat trackedStat = TrackedStats.All.FirstOrDefault((TrackedStat s) => s.Key == key);
					string text = ((trackedStat != null && trackedStat.Available && playerSnapshot.StatsAvailable) ? TrackedStats.Format(trackedStat.Format, stage.StatDelta(playerInfo.Key, key)) : "n/a");
					list.Add(item.ToLowerInvariant() + " " + text);
				}
				list.Add($"items +{stage.ItemsGained(playerInfo.Key).Values.Sum()}");
				List<(LootCategory, int, int)> list2 = stage.LootSummary();
				list.Add((list2.Count == 0) ? "loot none" : ("loot " + string.Join(", ", list2.Select<(LootCategory, int, int), string>(((LootCategory category, int used, int total) l) => $"{l.category.ToString().ToLowerInvariant()} {l.used}/{l.total}")) + $", missed {stage.LootMissed()}"));
				Log.Info(string.Format("Debrief shown for stage {0} ({1}), player {2}: {3}", stage.Index + 1, stage.DisplayName, playerInfo.DisplayName, string.Join(" | ", list)));
			}
			catch (Exception e)
			{
				Log.ErrorOnce("panel-logsummary", e);
			}
		}

		private void SetPicture(RawImage image, TextMeshProUGUI fallback, StageRecord stage)
		{
			Texture val = MapPreviews.Get(stage.Scene, delegate(Texture loaded)
			{
				if (Object.op_Implicit((Object)(object)image) && shown != null)
				{
					ApplyPicture(image, fallback, loaded);
				}
			});
			ApplyPicture(image, fallback, val);
			if (Object.op_Implicit((Object)(object)fallback) && !Object.op_Implicit((Object)(object)val))
			{
				((TMP_Text)fallback).text = stage.DisplayName;
			}
		}

		private static void ApplyPicture(RawImage image, TextMeshProUGUI fallback, Texture texture)
		{
			//IL_0017: 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)
			MapPreviews.FitCrop(image, texture);
			((Graphic)image).color = (Object.op_Implicit((Object)(object)texture) ? Color.white : Color.clear);
			if (Object.op_Implicit((Object)(object)fallback))
			{
				((Component)fallback).gameObject.SetActive(!Object.op_Implicit((Object)(object)texture));
			}
		}

		private void RebuildItems(StageRecord stage, int playerKey)
		{
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ae: Expected I4, but got Unknown
			//IL_01cb: Unknown result type (might be due to invalid IL or missing references)
			Dictionary<ItemIndex, int> dictionary = stage.ItemsGained(playerKey);
			List<ItemIndex> itemOrder = stage.EndOf(playerKey).ItemOrder;
			List<(ItemDef, int)> list = new List<(ItemDef, int)>();
			foreach (ItemIndex item in itemOrder)
			{
				if (dictionary.TryGetValue(item, out var value))
				{
					ItemDef itemDef = ItemCatalog.GetItemDef(item);
					if (Object.op_Implicit((Object)(object)itemDef) && !itemDef.hidden)
					{
						list.Add((itemDef, value));
					}
				}
			}
			StringBuilder stringBuilder = new StringBuilder();
			foreach (var (val, value2) in list)
			{
				stringBuilder.Append((int)val.itemIndex).Append(':').Append(value2)
					.Append(';');
			}
			if (!(stringBuilder.ToString() == itemsSignature))
			{
				itemsSignature = stringBuilder.ToString();
				ClearChildren(itemsRow);
				((Component)itemsEmpty).gameObject.SetActive(list.Count == 0);
				float num = 0f;
				int num2 = Math.Min(list.Count, 15);
				for (int i = 0; i < num2; i++)
				{
					CreateItemSlot(itemsRow, list[i].Item1, list[i].Item2, num);
					num += 46f;
				}
				if (list.Count > 15)
				{
					UIFactory.Text(UIFactory.Rect("More", (Transform)(object)itemsRow).Place(num + 4f, 0f, 60f, 40f), "+" + (list.Count - 15), 13.5f, Theme.TextSecondary, (TextAlignmentOptions)4097, (FontStyles)0);
				}
			}
		}

		private static void CreateItemSlot(RectTransform parent, ItemDef def, int count, float x)
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: 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_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ed: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e6: 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_014e: Unknown result type (might be due to invalid IL or missing references)
			Color val = TierColor(def);
			RectTransform val2 = UIFactory.Rect("Item_" + ((Object)def).name, (Transform)(object)parent).Place(x, 0f, 40f, 40f);
			Color color = val;
			color.a = 0.14f;
			UIFactory.Panel(val2, color, 6);
			Color color2 = val;
			color2.a = 0.8f;
			UIFactory.Outline(val2, color2, 6);
			RectTransform val3 = UIFactory.Rect("Icon", (Transform)(object)val2).Stretch(4f, 4f, 4f, 4f);
			Image icon = ((Component)val3).gameObject.AddComponent<Image>();
			icon.preserveAspect = true;
			((Graphic)icon).raycastTarget = false;
			Sprite val4 = MapPreviews.ItemIcon(def, delegate(Sprite loaded)
			{
				//IL_001f: Unknown result type (might be due to invalid IL or missing references)
				if (Object.op_Implicit((Object)(object)icon))
				{
					icon.sprite = loaded;
					((Graphic)icon).color = Color.white;
				}
			});
			icon.sprite = val4;
			((Graphic)icon).color = (Object.op_Implicit((Object)(object)val4) ? Color.white : Color.clear);
			if (count > 1)
			{
				TextMeshProUGUI obj = UIFactory.Text(UIFactory.Rect("Count", (Transform)(object)val2).Stretch(0f, 0f, 3f, 1f), count.ToString(), 11.5f, Theme.TextPrimary, (TextAlignmentOptions)1028, (FontStyles)1);
				((TMP_Text)obj).outlineWidth = 0.25f;
				((TMP_Text)obj).outlineColor = new Color32((byte)0, (byte)0, (byte)0, (byte)220);
			}
		}

		private void RebuildLoot(StageRecord stage)
		{
			//IL_0158: Unknown result type (might be due to invalid IL or missing references)
			//IL_0136: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ef: 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_0270: Unknown result type (might be due to invalid IL or missing references)
			//IL_02d0: Unknown result type (might be due to invalid IL or missing references)
			//IL_0308: Unknown result type (might be due to invalid IL or missing references)
			List<(LootCategory, int, int)> list = stage.LootSummary();
			int num = stage.LootMissed();
			StringBuilder stringBuilder = new StringBuilder();
			stringBuilder.Append(stage.IsIntermission).Append(stage.LootScanned).Append(num)
				.Append('|');
			foreach (var (value, value2, value3) in list)
			{
				stringBuilder.Append((int)value).Append(':').Append(value2)
					.Append('/')
					.Append(value3)
					.Append(';');
			}
			if (stringBuilder.ToString() == lootSignature)
			{
				return;
			}
			lootSignature = stringBuilder.ToString();
			ClearChildren(lootRow);
			if (stage.IsIntermission || list.Count == 0)
			{
				string text = (stage.IsIntermission ? "No loot on this stage" : (stage.LootScanned ? "No chests or shrines found" : "Counting..."));
				UIFactory.Text(UIFactory.Rect("Empty", (Transform)(object)lootRow).Place(0f, 0f, 400f, 28f), text, 13.5f, Theme.TextMuted, (TextAlignmentOptions)4097, (FontStyles)0);
				((TMP_Text)lootMissed).text = "";
				return;
			}
			string text2 = Theme.ToHex(Theme.TextSecondary);
			float num2 = 0f;
			foreach (var item4 in list)
			{
				LootCategory item = item4.Item1;
				int item2 = item4.Item2;
				int item3 = item4.Item3;
				string text3 = Theme.ToHex((item2 >= item3) ? Theme.Good : Theme.Warn);
				RectTransform val = UIFactory.Rect("Loot_" + item, (Transform)(object)lootRow).Place(num2, 0f, 112f, 28f);
				UIFactory.Panel(val, Theme.PanelRaised, 6);
				UIFactory.Outline(val, Theme.Border, 6);
				TextMeshProUGUI obj = UIFactory.Text(UIFactory.Rect("Text", (Transform)(object)val).Stretch(8f, 0f, 8f), $"<color=#{text2}><size=80%>{LootLabels[item]}</size></color>  <color=#{text3}><b>{item2}/{item3}</b></color>", 13.5f, Theme.TextPrimary, (TextAlignmentOptions)514, (FontStyles)0);
				((TMP_Text)obj).enableWordWrapping = false;
				((TMP_Text)obj).characterSpacing = 1f;
				num2 += 120f;
			}
			if (num == 0)
			{
				((TMP_Text)lootMissed).text = "Nothing missed";
				((Graphic)lootMissed).color = Theme.Good;
			}
			else
			{
				((TMP_Text)lootMissed).text = ((num == 1) ? "1 missed" : (num + " missed"));
				((Graphic)lootMissed).color = Theme.Warn;
			}
		}

		private static Color TierColor(ItemDef def)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				ItemTierDef itemTierDef = ItemTierCatalog.GetItemTierDef(def.tier);
				if (Object.op_Implicit((Object)(object)itemTierDef))
				{
					Color result = Color32.op_Implicit(ColorCatalog.GetColor(itemTierDef.colorIndex));
					result.a = 1f;
					return result;
				}
			}
			catch (Exception e)
			{
				Log.ErrorOnce("panel-tiercolor", e);
			}
			return Theme.TextSecondary;
		}

		private void RebuildRunStrip(RunRecord run, StageRecord current)
		{
			//IL_0081: Unknown result type (might be due to invalid IL or missing references)
			//IL_00eb: Unknown result type (might be due to invalid IL or missing references)
			//IL_01da: Unknown result type (might be due to invalid IL or missing references)
			//IL_0192: Unknown result type (might be due to invalid IL or missing references)
			//IL_0237: Unknown result type (might be due to invalid IL or missing references)
			//IL_0230: Unknown result type (might be due to invalid IL or missing references)
			//IL_0255: Unknown result type (might be due to invalid IL or missing references)
			//IL_024e: Unknown result type (might be due to invalid IL or missing references)
			ClearChildren(runStrip);
			int index = current.Index;
			int num = Math.Max(0, index - 9);
			float num2 = 0f;
			for (int i = num; i <= index && i < run.Stages.Count; i++)
			{
				StageRecord stageRecord = run.Stages[i];
				bool flag = i == index;
				RectTransform parent = UIFactory.Rect("Stage_" + (i + 1), (Transform)(object)runStrip).Place(num2, 0f, 64f, 40f);
				RectTransform val = UIFactory.RoundedMask("Mask", (Transform)(object)parent, Theme.PanelRaised, 6);
				val.Stretch();
				RawImage picture = UIFactory.Picture(val);
				RectTransform rect = UIFactory.Rect("Fallback", (Transform)(object)val).Stretch(4f, 2f, 4f, 2f);
				TextMeshProUGUI fallback = UIFactory.Text(rect, "", 9.5f, Theme.TextMuted, (TextAlignmentOptions)514, (FontStyles)0);
				Texture val2 = MapPreviews.Get(stageRecord.Scene, delegate(Texture loaded)
				{
					if (Object.op_Implicit((Object)(object)picture))
					{
						ApplyPicture(picture, fallback, loaded);
					}
				});
				ApplyPicture(picture, fallback, val2);
				if (!Object.op_Implicit((Object)(object)val2))
				{
					((TMP_Text)fallback).text = stageRecord.DisplayName;
				}
				if (!flag && Object.op_Implicit((Object)(object)picture))
				{
					UIFactory.Line(UIFactory.Rect("Dim", (Transform)(object)val).Stretch(), new Color(0f, 0f, 0f, 0.35f));
				}
				RectTransform val3 = UIFactory.Rect("Badge", (Transform)(object)parent).Place(3f, 23f, 18f, 14f);
				UIFactory.Panel(val3, new Color(0f, 0f, 0f, 0.7f), 4);
				UIFactory.Text(UIFactory.Rect("Number", (Transform)(object)val3).Stretch(), stageRecord.IsIntermission ? "·" : stageRecord.StageNumber.ToString(), 10f, flag ? Theme.Accent : Theme.TextPrimary, (TextAlignmentOptions)514, (FontStyles)1);
				UIFactory.Outline(parent, flag ? Theme.Accent : Theme.Border, 6, (!flag) ? 1 : 2);
				num2 += 72f;
			}
		}

		private static void ClearChildren(RectTransform parent)
		{
			for (int num = ((Transform)parent).childCount - 1; num >= 0; num--)
			{
				Object.Destroy((Object)(object)((Component)((Transform)parent).GetChild(num)).gameObject);
			}
		}

		private void EnsureBuilt()
		{
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Expected O, but got Unknown
			//IL_0080: Unknown result type (might be due to invalid IL or missing references)
			if (!Object.op_Implicit((Object)(object)canvasObject))
			{
				canvasObject = new GameObject("StageDebrief.Canvas");
				canvasObject.transform.SetParent(((Component)this).transform, false);
				canvasObject.layer = LayerIndex.ui.intVal;
				Canvas obj = canvasObject.AddComponent<Canvas>();
				obj.renderMode = (RenderMode)0;
				obj.sortingOrder = 10;
				CanvasScaler obj2 = canvasObject.AddComponent<CanvasScaler>();
				obj2.uiScaleMode = (ScaleMode)1;
				obj2.referenceResolution = new Vector2(1920f, 1080f);
				obj2.screenMatchMode = (ScreenMatchMode)0;
				obj2.matchWidthOrHeight = 0.5f;
				visibilityGroup = canvasObject.AddComponent<CanvasGroup>();
				visibilityGroup.interactable = false;
				visibilityGroup.blocksRaycasts = false;
				RectTransform root = UIFactory.Rect("Root", canvasObject.transform).Stretch();
				BuildCard(root);
				canvasObject.SetActive(false);
			}
		}

		private void BuildCard(RectTransform root)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0005: 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_003b: 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_0062: 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_0108: Unknown result type (might be due to invalid IL or missing references)
			//IL_011a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0153: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b9: Unknown result type (might be due to invalid IL or missing references)
			//IL_01cf: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e3: Unknown result type (might be due to invalid IL or missing references)
			//IL_021d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0277: Unknown result type (might be due to invalid IL or missing references)
			//IL_02c1: Unknown result type (might be due to invalid IL or missing references)
			//IL_0388: Unknown result type (might be due to invalid IL or missing references)
			//IL_03bc: Unknown result type (might be due to invalid IL or missing references)
			//IL_03dc: Unknown result type (might be due to invalid IL or missing references)
			//IL_0432: Unknown result type (might be due to invalid IL or missing references)
			//IL_0465: Unknown result type (might be due to invalid IL or missing references)
			//IL_0492: Unknown result type (might be due to invalid IL or missing references)
			//IL_04e1: Unknown result type (might be due to invalid IL or missing references)
			//IL_0514: Unknown result type (might be due to invalid IL or missing references)
			//IL_054e: Unknown result type (might be due to invalid IL or missing references)
			Vector2 debriefSize = Theme.DebriefSize;
			card = UIFactory.Rect("Card", (Transform)(object)root);
			RectTransform obj = card;
			RectTransform obj2 = card;
			Vector2 val = default(Vector2);
			((Vector2)(ref val))..ctor(0.5f, 0.5f);
			obj2.anchorMax = val;
			obj.anchorMin = val;
			card.pivot = new Vector2(0.5f, 0.5f);
			card.sizeDelta = debriefSize;
			showGroup = ((Component)card).gameObject.AddComponent<CanvasGroup>();
			showGroup.alpha = 0f;
			showGroup.interactable = false;
			showGroup.blocksRaycasts = false;
			UIFactory.Panel(UIFactory.Rect("Shadow", (Transform)(object)card).Stretch(-10f, -6f, -10f, -14f), Theme.Shadow, 18);
			RectTransform val2 = UIFactory.Rect("Surface", (Transform)(object)card).Stretch();
			cardBackground = UIFactory.Panel(val2, Theme.Panel, 10);
			UIFactory.Outline(val2, Theme.Border, 10);
			RectTransform parent = UIFactory.Rect("Map", (Transform)(object)val2).Place(20f, 20f, 264f, 160f);
			RectTransform val3 = UIFactory.RoundedMask("Mask", (Transform)(object)parent, Theme.PanelRaised, 8);
			val3.Stretch();
			mapImage = UIFactory.Picture(val3);
			RectTransform rect = UIFactory.Rect("Fallback", (Transform)(object)val3).Stretch(12f, 12f, 12f, 12f);
			mapFallback = UIFactory.Text(rect, "", 15f, Theme.TextMuted, (TextAlignmentOptions)514, (FontStyles)0);
			UIFactory.Outline(parent, Theme.Border, 8);
			float num = 304f;
			float num2 = debriefSize.x - num - 20f;
			header = UIFactory.Text(UIFactory.Rect("Header", (Transform)(object)val2).Place(num, 22f, num2, 16f), "", 12f, Theme.Accent, (TextAlignmentOptions)4097, (FontStyles)1);
			((TMP_Text)header).characterSpacing = 6f;
			((TMP_Text)header).enableWordWrapping = false;
			title = UIFactory.Text(UIFactory.Rect("Title", (Transform)(object)val2).Place(num, 40f, num2, 38f), "", 30f, Theme.TextPrimary, (TextAlignmentOptions)4097, (FontStyles)1);
			((TMP_Text)title).enableWordWrapping = false;
			subtitle = UIFactory.Text(UIFactory.Rect("Subtitle", (Transform)(object)val2).Place(num, 80f, num2, 20f), "", 13.5f, Theme.TextSecondary, (TextAlignmentOptions)4097, (FontStyles)0);
			((TMP_Text)subtitle).enableWordWrapping = false;
			float y = 108f;
			float num3 = (num2 - 8f * (float)(TileStats.Length - 1)) / (float)TileStats.Length;
			tiles = new StatTile[TileStats.Length];
			for (int i = 0; i < TileStats.Length; i++)
			{
				tiles[i] = StatTile.Create(val2, num + (float)i * (num3 + 8f), y, num3, 72f);
			}
			float num4 = 198f;
			Caption(val2, "ITEMS THIS STAGE", 20f, num4);
			itemsRow = UIFactory.Rect("Items", (Transform)(object)val2).Place(20f, num4 + 20f, debriefSize.x - 40f, 40f);
			itemsEmpty = UIFactory.Text(UIFactory.Rect("NoItems", (Transform)(object)val2).Place(20f, num4 + 20f, debriefSize.x - 40f, 40f), "No new items this stage", 13.5f, Theme.TextMuted, (TextAlignmentOptions)4097, (FontStyles)0);
			float num5 = num4 + 20f + 40f + 16f;
			Caption(val2, "LOOT", 20f, num5 + 7f);
			lootRow = UIFactory.Rect("Loot", (Transform)(object)val2).Place(84f, num5, debriefSize.x - 40f - 64f - 140f, 28f);
			lootMissed = UIFactory.Text(UIFactory.Rect("Missed", (Transform)(object)val2).Place(debriefSize.x - 20f - 140f, num5, 140f, 28f), "", 15f, Theme.Warn, (TextAlignmentOptions)4100, (FontStyles)1);
			((TMP_Text)lootMissed).enableWordWrapping = false;
			float num6 = num5 + 28f + 18f;
			Caption(val2, "THIS RUN", 20f, num6);
			hint = UIFactory.Text(UIFactory.Rect("Hint", (Transform)(object)val2).Place(debriefSize.x - 20f - 360f, num6 - 1f, 360f, 16f), "", 11.5f, Theme.TextMuted, (TextAlignmentOptions)4100, (FontStyles)0);
			((TMP_Text)hint).enableWordWrapping = false;
			runStrip = UIFactory.Rect("Run", (Transform)(object)val2).Place(20f, num6 + 20f, debriefSize.x - 40f, 40f);
		}

		private static void Caption(RectTransform parent, string text, float x, float y)
		{
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			TextMeshProUGUI obj = UIFactory.Text(UIFactory.Rect("Caption", (Transform)(object)parent).Place(x, y, 400f, 14f), text, 11f, Theme.TextMuted, (TextAlignmentOptions)4097, (FontStyles)1);
			((TMP_Text)obj).characterSpacing = 4f;
			((TMP_Text)obj).enableWordWrapping = false;
		}
	}
	internal static class MapPreviews
	{
		public static Texture Get(SceneDef sceneDef, Action<Texture> onLoaded)
		{
			if (!Object.op_Implicit((Object)(object)sceneDef))
			{
				return null;
			}
			Texture previewTexture = sceneDef.previewTexture;
			return AssetCache<Texture>.Get("scene:" + sceneDef.cachedName, (AssetReference)(object)sceneDef.previewTextureReference, previewTexture, onLoaded);
		}

		public static Sprite ItemIcon(ItemDef itemDef, Action<Sprite> onLoaded)
		{
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Expected I4, but got Unknown
			if (!Object.op_Implicit((Object)(object)itemDef))
			{
				return null;
			}
			Sprite icon = itemDef.GetIcon();
			return AssetCache<Sprite>.Get("item:" + (int)itemDef.itemIndex, (AssetReference)(object)itemDef.GetIconReference(), icon, onLoaded);
		}

		public static void FitCrop(RawImage image, Texture texture)
		{
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b5: Unknown result type (might be due to invalid IL or missing references)
			//IL_008a: Unknown result type (might be due to invalid IL or missing references)
			image.texture = texture;
			if (Object.op_Implicit((Object)(object)texture))
			{
				Rect rect = ((Graphic)image).rectTransform.rect;
				Vector2 size = ((Rect)(ref rect)).size;
				float num = ((size.y > 0f) ? (size.x / size.y) : 1f);
				float num2 = ((texture.height > 0) ? ((float)texture.width / (float)texture.height) : 1f);
				if (num2 > num)
				{
					float num3 = num / num2;
					image.uvRect = new Rect((1f - num3) / 2f, 0f, num3, 1f);
				}
				else
				{
					float num4 = num2 / num;
					image.uvRect = new Rect(0f, (1f - num4) / 2f, 1f, num4);
				}
			}
		}
	}
	internal static class RecapText
	{
		public static string ForLog(RunRecord run)
		{
			if (run == null)
			{
				return "StageDebrief: no run recorded.";
			}
			StringBuilder stringBuilder = new StringBuilder();
			stringBuilder.AppendLine(string.Format("StageDebrief dump - {0}, {1}, {2}, {3} stage(s), {4} player(s)", run.DifficultyName, run.IsHost ? "host/solo" : "client", run.Ended ? "ended" : "in progress", run.Stages.Count, run.Players.Count));
			foreach (PlayerInfo value in run.Players.Values)
			{
				stringBuilder.AppendLine(string.Format("  Player: {0} ({1}){2}{3} key={4}", value.DisplayName, value.SurvivorName, value.IsLocal ? " [local]" : "", value.HasLeft ? " [left]" : "", value.Key));
			}
			foreach (StageRecord stage in run.Stages)
			{
				stringBuilder.AppendLine(string.Format("== #{0} {1}: {2} ({3}) ", stage.Index + 1, stage.IsIntermission ? "Intermission" : ("Stage " + stage.StageNumber), stage.DisplayName, stage.SceneName) + TrackedStats.Duration(stage.Duration) + " " + (stage.Closed ? "closed" : "open"));
				List<(LootCategory, int, int)> list = stage.LootSummary();
				if (list.Count > 0)
				{
					stringBuilder.AppendLine("   loot: " + string.Join(", ", list.Select<(LootCategory, int, int), string>(((LootCategory category, int used, int total) l) => $"{l.category} {l.used}/{l.total}")) + $" (missed {stage.LootMissed()})");
				}
				foreach (PlayerInfo value2 in run.Players.Values)
				{
					if (!stage.Start.ContainsKey(value2.Key) && !stage.End.ContainsKey(value2.Key))
					{
						continue;
					}
					PlayerSnapshot playerSnapshot = stage.EndOf(value2.Key);
					stringBuilder.AppendLine(string.Format("   {0}: items {1} (+{2}), money ${3}, stats {4}", value2.DisplayName, playerSnapshot.TotalItems, stage.ItemsGained(value2.Key).Values.Sum(), playerSnapshot.Money, playerSnapshot.StatsAvailable ? "yes" : "no"));
					Dictionary<ItemIndex, int> dictionary = stage.ItemsGained(value2.Key);
					if (dictionary.Count > 0)
					{
						stringBuilder.AppendLine("     gained: " + string.Join(", ", dictionary.Select((KeyValuePair<ItemIndex, int> g) => $"{ItemName(g.Key)} x{g.Value}")));
					}
					if (playerSnapshot.Equipment.Count > 0)
					{
						stringBuilder.AppendLine("     equipment: " + string.Join(", ", playerSnapshot.Equipment.Select(EquipmentName)));
					}
					List<string> list2 = new List<string>();
					foreach (TrackedStat item in TrackedStats.All)
					{
						if (item.Available)
						{
							list2.Add(item.Label + " " + TrackedStats.Format(item.Format, stage.StatDelta(value2.Key, item.Key)));
						}
					}
					if (list2.Count > 0)
					{
						stringBuilder.AppendLine("     this stage: " + string.Join(" | ", list2));
					}
				}
			}
			return stringBuilder.ToString();
		}

		public unsafe static string ItemName(ItemIndex index)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			ItemDef itemDef = ItemCatalog.GetItemDef(index);
			if (!Object.op_Implicit((Object)(object)itemDef))
			{
				return ((object)(*(ItemIndex*)(&index))/*cast due to .constrained prefix*/).ToString();
			}
			return Language.GetString(itemDef.nameToken);
		}

		public unsafe static string EquipmentName(EquipmentIndex index)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			EquipmentDef equipmentDef = EquipmentCatalog.GetEquipmentDef(index);
			if (!Object.op_Implicit((Object)(object)equipmentDef))
			{
				return ((object)(*(EquipmentIndex*)(&index))/*cast due to .constrained prefix*/).ToString();
			}
			return Language.GetString(equipmentDef.nameToken);
		}
	}
	internal static class SpriteFactory
	{
		private static readonly Dictionary<int, Sprite> filled = new Dictionary<int, Sprite>();

		private static readonly Dictionary<int, Sprite> outlined = new Dictionary<int, Sprite>();

		public static Sprite Rounded(int radius)
		{
			if (!filled.TryGetValue(radius, out var value) || !Object.op_Implicit((Object)(object)value))
			{
				value = Build(radius, 0f);
				filled[radius] = value;
			}
			return value;
		}

		public static Sprite RoundedOutline(int radius, int stroke = 1)
		{
			int key = radius * 10 + stroke;
			if (!outlined.TryGetValue(key, out var value) || !Object.op_Implicit((Object)(object)value))
			{
				value = Build(radius, stroke);
				outlined[key] = value;
			}
			return value;
		}

		private static Sprite Build(int radius, float strokeWidth)
		{
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: Expected O, but got Unknown
			//IL_0198: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a7: 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)
			//IL_00c1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c6: Unknown result type (might be due to invalid IL or missing references)
			//IL_0142: Unknown result type (might be due to invalid IL or missing references)
			//IL_0147: Unknown result type (might be due to invalid IL or missing references)
			radius = Mathf.Clamp(radius, 1, 64);
			int num = radius * 2 + 6;
			Texture2D val = new Texture2D(num, num, (TextureFormat)4, false)
			{
				wrapMode = (TextureWrapMode)1,
				filterMode = (FilterMode)1,
				name = "StageDebrief_Rounded_" + radius + ((strokeWidth > 0f) ? "_outline" : "")
			};
			Color32[] array = (Color32[])(object)new Color32[num * num];
			float num2 = (float)num / 2f;
			float num3 = num2 - 2f - (float)radius;
			for (int i = 0; i < num; i++)
			{
				for (int j = 0; j < num; j++)
				{
					float num4 = Mathf.Abs((float)j + 0.5f - num2) - num3;
					float num5 = Mathf.Abs((float)i + 0.5f - num2) - num3;
					Vector2 val2 = new Vector2(Mathf.Max(num4, 0f), Mathf.Max(num5, 0f));
					float magnitude = ((Vector2)(ref val2)).magnitude;
					float num6 = Mathf.Min(Mathf.Max(num4, num5), 0f);
					float num7 = magnitude + num6 - (float)radius;
					float num8 = Mathf.Clamp01(0.5f - num7);
					if (strokeWidth > 0f)
					{
						float num9 = Mathf.Clamp01(0.5f - (num7 + strokeWidth));
						num8 = Mathf.Clamp01(num8 - num9);
					}
					array[i * num + j] = new Color32(byte.MaxValue, byte.MaxValue, byte.MaxValue, (byte)(num8 * 255f));
				}
			}
			val.SetPixels32(array);
			val.Apply(false, true);
			((Object)val).hideFlags = (HideFlags)61;
			float num10 = (float)radius + 2f;
			Sprite obj = Sprite.Create(val, new Rect(0f, 0f, (float)num, (float)num), new Vector2(0.5f, 0.5f), 100f, 0u, (SpriteMeshType)0, new Vector4(num10, num10, num10, num10));
			((Object)obj).name = ((Object)val).name;
			((Object)obj).hideFlags = (HideFlags)61;
			return obj;
		}
	}
	internal sealed class StatTile : MonoBehaviour
	{
		private TextMeshProUGUI valueText;

		private TextMeshProUGUI labelText;

		private StatFormat format;

		private double from;

		private double target;

		private float startTime;

		private float duration;

		private bool unavailable;

		public static StatTile Create(RectTransform parent, float x, float y, float width, float height)
		{
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_006f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f2: Unknown result type (might be due to invalid IL or missing references)
			RectTransform val = UIFactory.Rect("Tile", (Transform)(object)parent).Place(x, y, width, height);
			UIFactory.Panel(val, Theme.PanelRaised, 8);
			UIFactory.Outline(val, Theme.Border, 8);
			StatTile statTile = ((Component)val).gameObject.AddComponent<StatTile>();
			RectTransform rect = UIFactory.Rect("Value", (Transform)(object)val).Place(10f, 12f, width - 20f, 30f);
			statTile.valueText = UIFactory.Text(rect, "0", 20f, Theme.TextPrimary, (TextAlignmentOptions)4097, (FontStyles)1);
			((TMP_Text)statTile.valueText).enableWordWrapping = false;
			((TMP_Text)statTile.valueText).enableAutoSizing = true;
			((TMP_Text)statTile.valueText).fontSizeMin = 14f;
			((TMP_Text)statTile.valueText).fontSizeMax = 20f;
			RectTransform rect2 = UIFactory.Rect("Label", (Transform)(object)val).Place(10f, height - 26f, width - 20f, 16f);
			statTile.labelText = UIFactory.Text(rect2, "", 11.5f, Theme.TextMuted, (TextAlignmentOptions)4097, (FontStyles)0);
			((TMP_Text)statTile.labelText).enableWordWrapping = false;
			((TMP_Text)statTile.labelText).characterSpacing = 2f;
			return statTile;
		}

		public void SetLabel(string label)
		{
			((TMP_Text)labelText).text = label.ToUpperInvariant();
		}

		public void SetValue(double value, StatFormat valueFormat, bool animate)
		{
			unavailable = false;
			format = valueFormat;
			target = value;
			if (animate && Tween.MotionEnabled)
			{
				from = 0.0;
				startTime = Time.unscaledTime;
				duration = Tween.ScaledDuration(0.6f);
			}
			else
			{
				from = value;
				duration = 0f;
			}
			Render((duration <= 0f) ? 1f : 0f);
		}

		public void SetUnavailable()
		{
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			unavailable = true;
			((TMP_Text)valueText).text = "—";
			((Graphic)valueText).color = Theme.TextMuted;
		}

		private void Update()
		{
			if (!unavailable && !(duration <= 0f))
			{
				float num = (Time.unscaledTime - startTime) / duration;
				Render(Tween.EaseOutCubic(num));
				if (num >= 1f)
				{
					duration = 0f;
				}
			}
		}

		private void Render(float t)
		{
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			double num = from + (target - from) * (double)Mathf.Clamp01(t);
			if (format == StatFormat.Count)
			{
				num = Math.Round(num);
			}
			((TMP_Text)valueText).text = TrackedStats.Format(format, num);
			((Graphic)valueText).color = Theme.TextPrimary;
		}
	}
	internal static class Theme
	{
		public static readonly Color Panel = Hex(856084);

		public static readonly Color PanelRaised = Hex(1448480);

		public static readonly Color Border = new Color(1f, 1f, 1f, 0.08f);

		public static readonly Color Divider = new Color(1f, 1f, 1f, 0.06f);

		public static readonly Color Shadow = new Color(0f, 0f, 0f, 0.45f);

		public static readonly Color TextPrimary = Hex(15263457);

		public static readonly Color TextSecondary = Hex(10133672);

		public static readonly Color TextMuted = Hex(6580850);

		public static readonly Color Accent = Hex(14858327);

		public static readonly Color Good = Hex(8111742);

		public static readonly Color Warn = Hex(15116618);

		public static readonly Color Bad = Hex(14705243);

		public const float TitleSize = 30f;

		public const float HeadingSize = 15f;

		public const float BodySize = 13.5f;

		public const float CaptionSize = 11.5f;

		public const float CornerRadius = 10f;

		public const float Padding = 24f;

		public const float Gap = 16f;

		public static readonly Vector2 DebriefSize = new Vector2(780f, 400f);

		public const float TileHeight = 72f;

		public const float ItemSlot = 40f;

		public const float ThumbWidth = 64f;

		public const float ThumbHeight = 40f;

		public const float OpenDuration = 0.24f;

		public const float CloseDuration = 0.18f;

		public const float SlideDistance = 24f;

		public const float CountUpDuration = 0.6f;

		public static string ToHex(Color c)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			return ColorUtility.ToHtmlStringRGB(c);
		}

		private static Color Hex(int rgb)
		{
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			return new Color((float)((rgb >> 16) & 0xFF) / 255f, (float)((rgb >> 8) & 0xFF) / 255f, (float)(rgb & 0xFF) / 255f, 1f);
		}
	}
	internal static class Tween
	{
		public static bool MotionEnabled
		{
			get
			{
				if (StageDebriefPlugin.Settings != null)
				{
					return StageDebriefPlugin.Settings.Animations.Value == AnimationMode.Full;
				}
				return true;
			}
		}

		public static float EaseOutCubic(float t)
		{
			t = 1f - Mathf.Clamp01(t);
			return 1f - t * t * t;
		}

		public static float ScaledDuration(float seconds)
		{
			PluginConfig settings = StageDebriefPlugin.Settings;
			if (settings == null)
			{
				return seconds;
			}
			if (settings.Animations.Value == AnimationMode.Off)
			{
				return 0f;
			}
			return seconds / Mathf.Max(0.1f, settings.AnimationSpeed.Value);
		}

		public static IEnumerator Run(float seconds, Action<float> step, Action onDone = null)
		{
			float duration = ScaledDuration(seconds);
			if (duration <= 0f)
			{
				step(1f);
				onDone?.Invoke();
				yield break;
			}
			float elapsed = 0f;
			while (elapsed < duration)
			{
				elapsed += Time.unscaledDeltaTime;
				step(EaseOutCubic(elapsed / duration));
				yield return null;
			}
			step(1f);
			onDone?.Invoke();
		}
	}
	internal static class UIFactory
	{
		public static RectTransform Rect(string name, Transform parent)
		{
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: Expected O, but got Unknown
			RectTransform val = (RectTransform)new GameObject(name, new Type[1] { typeof(RectTransform) })
			{
				layer = LayerIndex.ui.intVal
			}.transform;
			((Transform)val).SetParent(parent, false);
			return val;
		}

		public static RectTransform Stretch(this RectTransform rect, float left = 0f, float top = 0f, float right = 0f, float bottom = 0f)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			rect.anchorMin = Vector2.zero;
			rect.anchorMax = Vector2.one;
			rect.offsetMin = new Vector2(left, bottom);
			rect.offsetMax = new Vector2(0f - right, 0f - top);
			return rect;
		}

		public static RectTransform TopBand(this RectTransform rect, float top, float height, float left = 0f, float right = 0f)
		{
			//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_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			rect.anchorMin = new Vector2(0f, 1f);
			rect.anchorMax = new Vector2(1f, 1f);
			rect.pivot = new Vector2(0.5f, 1f);
			rect.offsetMin = new Vector2(left, 0f - top - height);
			rect.offsetMax = new Vector2(0f - right, 0f - top);
			return rect;
		}

		public static Image Panel(RectTransform rect, Color color, int radius)
		{
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			Image obj = ((Component)rect).gameObject.AddComponent<Image>();
			obj.sprite = SpriteFactory.Rounded(radius);
			obj.type = (Type)1;
			((Graphic)obj).color = color;
			((Graphic)obj).raycastTarget = false;
			return obj;
		}

		public static Image Outline(RectTransform parent, Color color, int radius, int stroke = 1)
		{
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			Image obj = ((Component)Rect("Border", (Transform)(object)parent).Stretch()).gameObject.AddComponent<Image>();
			obj.sprite = SpriteFactory.RoundedOutline(radius, stroke);
			obj.type = (Type)1;
			((Graphic)obj).color = color;
			((Graphic)obj).raycastTarget = false;
			return obj;
		}

		public static Image Line(RectTransform parent, Color color)
		{
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			Image obj = ((Component)parent).gameObject.AddComponent<Image>();
			((Graphic)obj).color = color;
			((Graphic)obj).raycastTarget = false;
			return obj;
		}

		public static RectTransform Place(this RectTransform rect, float x, float y, float width, float height)
		{
			//IL_0013: 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_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			Vector2 val = default(Vector2);
			((Vector2)(ref val))..ctor(0f, 1f);
			rect.anchorMax = val;
			rect.anchorMin = val;
			rect.pivot = new Vector2(0f, 1f);
			rect.anchoredPosition = new Vector2(x, 0f - y);
			rect.sizeDelta = new Vector2(width, height);
			return rect;
		}

		public static RectTransform RoundedMask(string name, Transform parent, Color background, int radius)
		{
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			RectTransform obj = Rect(name, parent);
			Image obj2 = ((Component)obj).gameObject.AddComponent<Image>();
			obj2.sprite = SpriteFactory.Rounded(radius);
			obj2.type = (Type)1;
			((Graphic)obj2).color = background;
			((Graphic)obj2).raycastTarget = false;
			((Component)obj).gameObject.AddComponent<Mask>().showMaskGraphic = true;
			return obj;
		}

		public static RawImage Picture(RectTransform parent)
		{
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			RawImage obj = ((Component)Rect("Picture", (Transform)(object)parent).Stretch()).gameObject.AddComponent<RawImage>();
			((Graphic)obj).raycastTarget = false;
			((Graphic)obj).color = Color.clear;
			return obj;
		}

		public static TextMeshProUGUI Text(RectTransform rect, string text, float size, Color color, TextAlignmentOptions align = (TextAlignmentOptions)257, FontStyles style = (FontStyles)0)
		{
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			HGTextMeshProUGUI obj = ((Component)rect).gameObject.AddComponent<HGTextMeshProUGUI>();
			((TMP_Text)obj).text = text;
			((TMP_Text)obj).fontSize = size;
			((Graphic)obj).color = color;
			((TMP_Text)obj).alignment = align;
			((TMP_Text)obj).fontStyle = style;
			((TMP_Text)obj).enableWordWrapping = true;
			((TMP_Text)obj).overflowMode = (TextOverflowModes)1;
			((TMP_Text)obj).richText = true;
			((Graphic)obj).raycastTarget = false;
			return (TextMeshProUGUI)(object)obj;
		}
	}
}
namespace StageDebrief.Tracking
{
	internal static class LootScanner
	{
		private const float ShopClusterRadius = 12f;

		private static readonly string[] Ignored = new string[14]
		{
			"DUPLICATOR", "SCRAPPER", "SHRINE_CLEANSE", "NEWT", "TELEPORTER", "PORTAL", "MOON", "BAZAAR", "LUNAR_TERMINAL", "LUNAR_REROLL",
			"SEER", "COLOSSUS", "ACCESS_CODES", "FAN"
		};

		public static void Scan(StageRecord stage, bool final)
		{
			if (stage == null || stage.IsIntermission)
			{
				return;
			}
			try
			{
				HashSet<int> hashSet = new HashSet<int>();
				List<PurchaseInteraction> list = new List<PurchaseInteraction>();
				PurchaseInteraction[] array = Object.FindObjectsOfType<PurchaseInteraction>();
				foreach (PurchaseInteraction val in array)
				{
					if (!Object.op_Implicit((Object)(object)val) || !((Component)val).gameObject.activeInHierarchy)
					{
						continue;
					}
					string text = (val.displayNameToken ?? "").ToUpperInvariant();
					if (!IsIgnored(text))
					{
						if (text.Contains("TERMINAL"))
						{
							list.Add(val);
							continue;
						}
						int instanceID = ((Object)val).GetInstanceID();
						hashSet.Add(instanceID);
						Record(stage, instanceID, Classify(val, text), text, !val.available);
					}
				}
				foreach (List<PurchaseInteraction> item in ClusterShops(list))
				{
					int num = ((Object)item[0]).GetInstanceID();
					foreach (PurchaseInteraction item2 in item)
					{
						num = Math.Min(num, ((Object)item2).GetInstanceID());
					}
					bool flag = false;
					foreach (PurchaseInteraction item3 in item)
					{
						flag |= item3.available;
					}
					hashSet.Add(num);
					Record(stage, num, LootCategory.Shops, (item[0].displayNameToken ?? "").ToUpperInvariant(), !flag);
				}
				BarrelInteraction[] array2 = Object.FindObjectsOfType<BarrelInteraction>();
				foreach (BarrelInteraction val2 in array2)
				{
					if (Object.op_Implicit((Object)(object)val2) && ((Component)val2).gameObject.activeInHierarchy)
					{
						int instanceID2 = ((Object)val2).GetInstanceID();
						hashSet.Add(instanceID2);
						Record(stage, instanceID2, LootCategory.Barrels, "BARREL", val2.Networkopened);
					}
				}
				foreach (KeyValuePair<int, LootEntry> item4 in stage.Loot)
				{
					if (!hashSet.Contains(item4.Key))
					{
						item4.Value.Used = true;
					}
				}
				stage.LootScanned = true;
				if (final)
				{
					LogOtherTokens(stage);
				}
			}
			catch (Exception e)
			{
				Log.ErrorOnce("loot-scan", e);
			}
		}

		private static void Record(StageRecord stage, int id, LootCategory category, string token, bool used)
		{
			if (!stage.Loot.TryGetValue(id, out var value))
			{
				value = new LootEntry
				{
					Category = category,
					Token = token
				};
				stage.Loot[id] = value;
			}
			value.Used |= used;
		}

		private static bool IsIgnored(string token)
		{
			string[] ignored = Ignored;
			foreach (string value in ignored)
			{
				if (token.Contains(value))
				{
					return true;
				}
			}
			return false;
		}

		private static LootCategory Classify(PurchaseInteraction interaction, string token)
		{
			if (interaction.isDrone || token.Contains("DRONE") || token.Contains("TURRET"))
			{
				return LootCategory.Drones;
			}
			if (interaction.isShrine || interaction.isGoldShrine || token.Contains("SHRINE"))
			{
				return LootCategory.Shrines;
			}
			if (token.Contains("CHEST") || token.Contains("LOCKBOX") || token.Contains("BARREL") || token.Contains("CACHE"))
			{
				return LootCategory.Chests;
			}
			return LootCategory.Other;
		}

		private static List<List<PurchaseInteraction>> ClusterShops(List<PurchaseInteraction> terminals)
		{
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_0063: 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_0069: Unknown result type (might be due to invalid IL or missing references)
			//IL_006e: Unknown result type (might be due to invalid IL or missing references)
			List<List<PurchaseInteraction>> list = new List<List<PurchaseInteraction>>();
			foreach (PurchaseInteraction terminal in terminals)
			{
				Vector3 position = ((Component)terminal).transform.position;
				List<PurchaseInteraction> list2 = null;
				foreach (List<PurchaseInteraction> item in list)
				{
					if (item[0].displayNameToken == terminal.displayNameToken)
					{
						Vector3 val = ((Component)item[0]).transform.position - position;
						if (((Vector3)(ref val)).sqrMagnitude <= 144f)
						{
							list2 = item;
							break;
						}
					}
				}
				if (list2 == null)
				{
					list2 = new List<PurchaseInteraction>();
					list.Add(list2);
				}
				list2.Add(terminal);
			}
			return list;
		}

		private static void LogOtherTokens(StageRecord stage)
		{
			SortedSet<string> sortedSet = new SortedSet<string>();
			foreach (LootEntry value in stage.Loot.Values)
			{
				if (value.Category == LootCategory.Other)
				{
					sortedSet.Add(value.Token);
				}
			}
			if (sortedSet.Count > 0)
			{
				Log.Info("Uncategorised interactables on " + stage.SceneName + " (not counted): " + string.Join(", ", sortedSet));
			}
		}
	}
	internal sealed class RunTracker
	{
		private const float RefreshInterval = 2f;

		private const float FastRefreshInterval = 0.5f;

		private const float LootScanDelay = 5f;

		private float nextRefreshAt;

		private int openStageSceneHandle = -1;

		private bool exitAnnounced;

		private bool enabled;

		public RunRecord CurrentRun { get; private set; }

		public RunRecord LastRun { get; private set; }

		public RunRecord DisplayRun => CurrentRun ?? LastRun;

		public bool FastRefresh { get; set; }

		public event Action Changed;

		public event Action<StageRecord> ExitStarted;

		public event Action<StageRecord> StageOpened;

		public event Action<RunRecord> RunEnded;

		public void Enable()
		{
			if (!enabled)
			{
				Run.onRunStartGlobal += OnRunStart;
				Run.onServerGameOver += OnServerGameOver;
				SceneCatalog.onMostRecentSceneDefChanged += OnSceneDefChanged;
				SceneExitController.onBeginExit += OnBeginExit;
				enabled = true;
			}
		}

		public void Disable()
		{
			if (enabled)
			{
				Run.onRunStartGlobal -= OnRunStart;
				Run.onServerGameOver -= OnServerGameOver;
				SceneCatalog.onMostRecentSceneDefChanged -= OnSceneDefChanged;
				SceneExitController.onBeginExit -= OnBeginExit;
				enabled = false;
			}
		}

		public void Tick(float unscaledTime)
		{
			if (CurrentRun == null)
			{
				return;
			}
			if (!Object.op_Implicit((Object)(object)Run.instance))
			{
				EndRun("run object destroyed");
				return;
			}
			if (!exitAnnounced && SceneExitController.isRunning)
			{
				AnnounceExit("exit controller running");
			}
			StageRecord currentStage = CurrentRun.CurrentStage;
			if (currentStage != null && !currentStage.Closed && !currentStage.LootTimedScanDone && unscaledTime >= currentStage.OpenedAtUnscaled + 5f)
			{
				currentStage.LootTimedScanDone = true;
				LootScanner.Scan(currentStage, final: false);
			}
			if (unscaledTime >= nextRefreshAt)
			{
				nextRefreshAt = unscaledTime + (FastRefresh ? 0.5f : 2f);
				RefreshOpenStage();
			}
		}

		private void OnRunStart(Run run)
		{
			try
			{
				if (CurrentRun != null)
				{
					EndRun("new run started");
				}
				CurrentRun = new RunRecord
				{
					StartedUtc = DateTime.UtcNow,
					IsHost = NetworkServer.active,
					DifficultyName = DescribeDifficulty(run)
				};
				openStageSceneHandle = -1;
				Log.Info("Run started (" + CurrentRun.DifficultyName + ", " + (CurrentRun.IsHost ? "host or solo" : "client") + ").");
				SceneDef sceneDefForCurrentScene = SceneCatalog.GetSceneDefForCurrentScene();
				if (GameCompat.IsPlayableScene(sceneDefForCurrentScene))
				{
					OpenStage(sceneDefForCurrentScene);
				}
				this.Changed?.Invoke();
			}
			catch (Exception e)
			{
				Log.ErrorOnce("tracker-runstart", e);
			}
		}

		private void OnSceneDefChanged(SceneDef sceneDef)
		{
			try
			{
				if (CurrentRun != null && Object.op_Implicit((Object)(object)Run.instance))
				{
					CloseOpenStage();
					if (GameCompat.IsPlayableScene(sceneDef))
					{
						OpenStage(sceneDef);
					}
					this.Changed?.Invoke();
				}
			}
			catch (Exception e)
			{
				Log.ErrorOnce("tracker-scene", e);
			}
		}

		private void OnServerGameOver(Run run, GameEndingDef ending)
		{
			try
			{
				RefreshOpenStage();
			}
			catch (Exception e)
			{
				Log.ErrorOnce("tracker-gameover", e);
			}
		}

		private void OnBeginExit(SceneExitController controller)
		{
			AnnounceExit("onBeginExit");
		}

		private void AnnounceExit(string source)
		{
			try
			{
				StageRecord stageRecord = CurrentRun?.CurrentStage;
				if (!exitAnnounced && stageRecord != null && !stageRecord.Closed && Object.op_Implicit((Object)(object)Run.instance))
				{
					exitAnnounced = true;
					RefreshOpenStage();
					LootScanner.Scan(stageRecord, final: true);
					Log.Info($"Leaving stage {stageRecord.Index + 1} ({source}).");
					this.ExitStarted?.Invoke(stageRecord);
				}
			}
			catch (Exception e)
			{
				Log.ErrorOnce("tracker-exit", e);
			}
		}

		private void OpenStage(SceneDef sceneDef)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			Scene activeScene = SceneManager.GetActiveScene();
			int handle = ((Scene)(ref activeScene)).handle;
			StageRecord currentStage = CurrentRun.CurrentStage;
			if (currentStage != null && !currentStage.Closed && handle == openStageSceneHandle)
			{
				return;
			}
			bool flag = GameCompat.IsIntermission(sceneDef);
			int num = currentStage?.StageNumber ?? 0;
			StageRecord stageRecord = new StageRecord
			{
				Index = CurrentRun.Stages.Count,
				StageNumber = (flag ? Math.Max(num, 0) : (num + 1)),
				Scene = sceneDef,
				SceneName = sceneDef.cachedName,
				NameToken = sceneDef.nameToken,
				DisplayName = Localize(sceneDef.nameToken, sceneDef.cachedName),
				Subtitle = Localize(sceneDef.subtitleToken, ""),
				IsIntermission = flag,
				StageClearCountAtStart = (Object.op_Implicit((Object)(object)Run.instance) ? Run.instance.stageClearCount : 0),
				StartTime = GameCompat.GetRunTime()
			};
			stageRecord.EndTime = stageRecord.StartTime;
			stageRecord.OpenedAtUnscaled = Time.unscaledTime;
			CurrentRun.Stages.Add(stageRecord);
			openStageSceneHandle = handle;
			exitAnnounced = false;
			foreach (PlayerCharacterMasterController instance in PlayerCharacterMasterController.instances)
			{
				CaptureInto(stageRecord, instance, isStart: true);
			}
			Log.Info(string.Format("Stage {0} opened: {1} ({2}){3}.", stageRecord.Index + 1, stageRecord.DisplayName, stageRecord.SceneName, flag ? " [intermission]" : ""));
			this.StageOpened?.Invoke(stageRecord);
		}

		private void CloseOpenStage()
		{
			StageRecord stageRecord = CurrentRun?.CurrentStage;
			if (stageRecord != null && !stageRecord.Closed)
			{
				RefreshOpenStage();
				stageRecord.Closed = true;
				Log.Info($"Stage {stageRecord.Index + 1} closed after {TrackedStats.Duration(stageRecord.Duration)}.");
			}
		}

		private void RefreshOpenStage()
		{
			StageRecord stageRecord = CurrentRun?.CurrentStage;
			if (stageRecord == null || stageRecord.Closed)
			{
				return;
			}
			HashSet<int> hashSet = new HashSet<int>();
			foreach (PlayerCharacterMasterController instance in PlayerCharacterMasterController.instances)
			{
				int num = CaptureInto(stageRecord, instance, isStart: false);
				if (num != 0)
				{
					hashSet.Add(num);
				}
			}
			foreach (PlayerInfo value in CurrentRun.Players.Values)
			{
				if (!hashSet.Contains(value.Key))
				{
					value.HasLeft = true;
				}
			}
			if (Object.op_Implicit((Object)(object)Run.instance))
			{
				stageRecord.EndTime = GameCompat.GetRunTime();
			}
			this.Changed?.Invoke();
		}

		private int CaptureInto(StageRecord stage, PlayerCharacterMasterController controller, bool isStart)
		{
			int num = SnapshotBuilder.KeyOf(controller);
			if (num == 0)
			{
				return 0;
			}
			PlayerSnapshot playerSnapshot = SnapshotBuilder.Capture(controller);
			if (playerSnapshot == null)
			{
				return 0;
			}
			if (!CurrentRun.Players.TryGetValue(num, out var value))
			{
				value = SnapshotBuilder.DescribePlayer(controller, num, stage.Index);
				CurrentRun.Players[num] = value;
				Log.Debug("Player seen: " + value.DisplayName + " (" + value.SurvivorName + ")" + (value.IsLocal ? " [local]" : "") + ".");
			}
			else
			{
				SnapshotBuilder.Refresh(value, controller);
			}
			if (isStart)
			{
				stage.Start[num] = playerSnapshot;
			}
			else
			{
				if (!stage.Start.ContainsKey(num))
				{
					stage.Start[num] = ((stage.Index == 0) ? PlayerSnapshot.Empty : playerSnapshot);
				}
				stage.End[num] = playerSnapshot;
			}
			return num;
		}

		private void EndRun(string reason)
		{
			if (CurrentRun != null)
			{
				try
				{
					CloseOpenStage();
				}
				catch (Exception e)
				{
					Log.ErrorOnce("tracker-endrun", e);
				}
				CurrentRun.Ended = true;
				LastRun = CurrentRun;
				CurrentRun = null;
				openStageSceneHandle = -1;
				Log.Info($"Run ended ({reason}). {LastRun.Stages.Count} stage(s) recorded.");
				this.RunEnded?.Invoke(LastRun);
				this.Changed?.Invoke();
			}
		}

		private static string DescribeDifficulty(Run run)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				DifficultyDef difficultyDef = DifficultyCatalog.GetDifficultyDef(run.selectedDifficulty);
				return (difficultyDef != null) ? Localize(difficultyDef.nameToken, "Unknown difficulty") : "Unknown difficulty";
			}
			catch (Exception e)
			{
				Log.ErrorOnce("tracker-difficulty", e);
				return "Unknown difficulty";
			}
		}

		private static string Localize(string token, string fallback)
		{
			if (string.IsNullOrEmpty(token))
			{
				return fallback;
			}
			string text = Language.GetString(token);
			if (!string.IsNullOrEmpty(text) && !(text == token))
			{
				return text;
			}
			return fallback;
		}
	}
	internal static class SnapshotBuilder
	{
		public static PlayerSnapshot Capture(PlayerCharacterMasterController controller)
		{
			if (!Object.op_Implicit((Object)(object)controller))
			{
				return null;
			}
			CharacterMaster master = controller.master;
			if (!Object.op_Implicit((Object)(object)master))
			{
				return null;
			}
			PlayerSnapshot playerSnapshot = new PlayerSnapshot
			{
				Money = master.money,
				RunTime = GameCompat.GetRunTime()
			};
			CaptureInventory(master.inventory, playerSnapshot);
			CaptureStats(controller, playerSnapshot);
			return playerSnapshot;
		}

		private static void CaptureInventory(Inventory inventory, PlayerSnapshot snapshot)
		{
			//IL_008e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0093: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: 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_009c: Unknown result type (might be due to invalid IL or missing references)
			//IL_00af: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: 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_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ce: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00dd: Invalid comparison between Unknown and I4
			//IL_00e5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e7: Unknown result type (might be due to invalid IL or missing references)
			if (!Object.op_Implicit((Object)(object)inventory))
			{
				return;
			}
			try
			{
				List<ItemIndex> itemAcquisitionOrder = inventory.itemAcquisitionOrder;
				for (int i = 0; i < itemAcquisitionOrder.Count; i++)
				{
					ItemIndex val = itemAcquisitionOrder[i];
					int itemCountPermanent = inventory.GetItemCountPermanent(val);
					int itemCountTemp = inventory.GetItemCountTemp(val);
					if (itemCountPermanent > 0 || itemCountTemp > 0)
					{
						if (!snapshot.Items.ContainsKey(val))
						{
							snapshot.ItemOrder.Add(val);
						}
						snapshot.Items[val] = new ItemCounts(itemCountPermanent, itemCountTemp);
					}
				}
			}
			catch (Exception e)
			{
				Log.ErrorOnce("snapshot-items", e);
			}
			try
			{
				int equipmentSlotCount = inventory.GetEquipmentSlotCount();
				EquipmentLocation activeEquipmentLocation = inventory.activeEquipmentLocation;
				for (uint num = 0u; num < equipmentSlotCount; num++)
				{
					EquipmentLocation val2 = new EquipmentLocation
					{
						slot = num,
						set = ((num == activeEquipmentLocation.slot) ? activeEquipmentLocation.set : 0u)
					};
					EquipmentState equipment = inventory.GetEquipment(ref val2);
					if ((int)equipment.equipmentIndex != -1)
					{
						snapshot.Equipment.Add(equipment.equipmentIndex);
					}
				}
			}
			catch (Exception e2)
			{
				Log.ErrorOnce("snapshot-equipment", e2);
			}
		}

		private static void CaptureStats(PlayerCharacterMasterController controller, PlayerSnapshot snapshot)
		{
			try
			{
				PlayerStatsComponent component = ((Component)controller).GetComponent<PlayerStatsComponent>();
				StatSheet val = (Object.op_Implicit((Object)(object)component) ? component.currentStats : null);
				if (val == null)
				{
					return;
				}
				foreach (TrackedStat item in TrackedStats.All)
				{
					if (item.Available)
					{
						snapshot.Stats[item.Key] = GameCompat.ReadStat(val, item.Def);
					}
				}
				snapshot.StatsAvailable = true;
			}
			catch (Exception e)
			{
				Log.ErrorOnce("snapshot-stats", e);
			}
		}

		public static PlayerInfo DescribePlayer(PlayerCharacterMasterController controller, int key, int stageIndex)
		{
			PlayerInfo obj = new PlayerInfo
			{
				Key = key,
				FirstStageIndex = stageIndex
			};
			Refresh(obj, controller);
			return obj;
		}

		public static void Refresh(PlayerInfo info, PlayerCharacterMasterController controller)
		{
			if (!Object.op_Implicit((Object)(object)controller))
			{
				info.HasLeft = true;
				return;
			}
			NetworkUser networkUser = controller.networkUser;
			if (Object.op_Implicit((Object)(object)networkUser))
			{
				info.DisplayName = (string.IsNullOrEmpty(networkUser.userName) ? info.DisplayName : networkUser.userName);
				info.IsLocal = NetworkUser.readOnlyLocalPlayersList.Contains(networkUser);
				info.HasLeft = false;
			}
			else
			{
				info.HasLeft = true;
			}
			if (string.IsNullOrEmpty(info.DisplayName))
			{
				info.DisplayName = "Player";
			}
			CharacterMaster master = controller.master;
			if (Object.op_Implicit((Object)(object)master) && Object.op_Implicit((Object)(object)master.bodyPrefab))
			{
				CharacterBody component = master.bodyPrefab.GetComponent<CharacterBody>();
				if (Object.op_Implicit((Object)(object)component))
				{
					info.SurvivorName = component.GetDisplayName();
				}
			}
		}

		public static int KeyOf(PlayerCharacterMasterController controller)
		{
			CharacterMaster val = (Object.op_Implicit((Object)(object)controller) ? controller.master : null);
			if (!Object.op_Implicit((Object)(object)val))
			{
				return 0;
			}
			return ((Object)val).GetInstanceID();
		}
	}
	internal enum StatFormat
	{
		Count,
		Damage,
		Gold,
		Seconds,
		Distance
	}
	internal sealed class TrackedStat
	{
		public string Key;

		public string Label;

		public StatFormat Format;

		public StatDef Def;

		public bool Available => Def != null;
	}
	internal static class TrackedStats
	{
		private static List<TrackedStat> all;

		public static IReadOnlyList<TrackedStat> All
		{
			get
			{
				if (all == null)
				{
					Resolve();
				}
				return all;
			}
		}

		private static void Resolve()
		{
			all = new List<TrackedStat>
			{
				Make("totalDamageDealt", "Damage dealt", StatFormat.Damage),
				Make("totalKills", "Kills", StatFormat.Count),
				Make("totalEliteKills", "Elite kills", StatFormat.Count),
				Make("totalMinionKills", "Minion kills", StatFormat.Count),
				Make("highestDamageDealt", "Biggest hit", StatFormat.Damage),
				Make("totalDamageTaken", "Damage taken", StatFormat.Damage),
				Make("totalHealthHealed", "Healing", StatFormat.Damage),
				Make("totalDeaths", "Deaths", StatFormat.Count),
				Make("totalGoldCollected", "Gold earned", StatFormat.Gold),
				Make("totalGoldPurchases", "Gold purchases", StatFormat.Count),
				Make("totalPurchases", "Purchases", StatFormat.Count),
				Make("totalItemsCollected", "Items picked up", StatFormat.Count),
				Make("totalDistanceTraveled", "Distance", StatFormat.Distance),
				Make("totalTimeAlive", "Time alive", StatFormat.Seconds)
			};
			List<string> list = new List<string>();
			foreach (TrackedStat item in all)
			{
				if (!item.Available)
				{
					list.Add(item.Key);
				}
			}
			if (list.Count > 0)
			{
				Log.Warn("These stats were not found and will be hidden: " + string.Join(", ", list) + ". Run 'debrief_statnames' in the console to see the names the game uses.");
			}
		}

		private static TrackedStat Make(string key, string label, StatFormat format)
		{
			StatDef def = null;
			try
			{
				def = StatDef.Find(key);
			}
			catch (Exception e)
			{
				Log.ErrorOnce("stat-find-" + key, e);
			}
			return new TrackedStat
			{
				Key = key,
				Label = label,
				Format = format,
				Def = def
			};
		}

		public static string Format(StatFormat format, double value)
		{
			switch (format)
			{
			case StatFormat.Damage:
				return Compact(value);
			case StatFormat.Gold:
				return "$" + Compact(value);
			case StatFormat.Seconds:
				return Duration((float)value);
			case StatFormat.Distance:
				if (!(value >= 1000.0))
				{
					return value.ToString("0") + " m";
				}
				return (value / 1000.0).ToString("0.0") + " km";
			default:
				return value.ToString("N0");
			}
		}

		public static string Compact(double value)
		{
			if (value >= 1000000000.0)
			{
				return (value / 1000000000.0).ToString("0.##") + "B";
			}
			if (value >= 1000000.0)
			{
				return (value / 1000000.0).ToString("0.##") + "M";
			}
			if (value >= 10000.0)
			{
				return (value / 1000.0).ToString("0.#") + "K";
			}
			return value.ToString("N0");
		}

		public static string Duration(float seconds)
		{
			if (seconds < 0f)
			{
				seconds = 0f;
			}
			int num = (int)seconds;
			int num2 = num / 3600;
			int num3 = num % 3600 / 60;
			int num4 = num % 60;
			if (num2 <= 0)
			{
				return $"{num3:00}:{num4:00}";
			}
			return $"{num2}:{num3:00}:{num4:00}";
		}
	}
}
namespace StageDebrief.Debugging
{
	public static class Commands
	{
		[ConCommand(/*Could not decode attribute arguments.*/)]
		public static void Dump(ConCommandArgs args)
		{
			Write(RecapText.ForLog(StageDebriefPlugin.Tracker?.DisplayRun));
		}

		[ConCommand(/*Could not decode attribute arguments.*/)]
		public static void StatNames(ConCommandArgs args)
		{
			StringBuilder stringBuilder = new StringBuilder("Stat names defined by the game:\n");
			foreach (StatDef item in StatDef.allStatDefs.Where((StatDef d) => d != null))
			{
				stringBuilder.Append("  ").Append(item.name).Append('\n');
			}
			Write(stringBuilder.ToString());
		}

		[ConCommand(/*Could not decode attribute arguments.*/)]
		public static void SelfTest(ConCommandArgs args)
		{
			StringBuilder stringBuilder = new StringBuilder("StageDebrief self-test\n");
			stringBuilder.Append($"Run timer: {GameCompat.GetRunTime():0.0}s\n");
			stringBuilder.Append($"Console open: {GameCompat.IsConsoleOpen()}\n");
			stringBuilder.Append("Run active: " + (Object.op_Implicit((Object)(object)Run.instance) ? "yes" : "no") + "\n");
			foreach (TrackedStat item in TrackedStats.All)
			{
				stringBuilder.Append("  stat " + item.Key + ": " + (item.Available ? "found" : "MISSING") + "\n");
			}
			Write(stringBuilder.ToString());
		}

		private static void Write(string text)
		{
			Log.Info(text);
			Debug.Log((object)text);
		}
	}
}
namespace StageDebrief.Data
{
	public struct ItemCounts
	{
		public int Permanent;

		public int Temporary;

		public int Total => Permanent + Temporary;

		public ItemCounts(int permanent, int temporary)
		{
			Permanent = permanent;
			Temporary = temporary;
		}
	}
	public sealed class PlayerInfo
	{
		public int Key;

		public string DisplayName;

		public string SurvivorName;

		public bool IsLocal;

		public bool HasLeft;

		public int FirstStageIndex;
	}
	public sealed class PlayerSnapshot
	{
		public readonly Dictionary<ItemIndex, ItemCounts> Items = new Dictionary<ItemIndex, ItemCounts>();

		public readonly List<ItemIndex> ItemOrder = new List<ItemIndex>();

		public readonly List<EquipmentIndex> Equipment = new List<EquipmentIndex>();

		public readonly Dictionary<string, double> Stats = new Dictionary<string, double>();

		public uint Money;

		public bool StatsAvailable;

		public float RunTime;

		public static readonly PlayerSnapshot Empty = new PlayerSnapshot();

		public int TotalItems
		{
			get
			{
				int num = 0;
				foreach (ItemCounts value in Items.Values)
				{
					num += value.Total;
				}
				return num;
			}
		}

		public double Stat(string key)
		{
			if (!Stats.TryGetValue(key, out var value))
			{
				return 0.0;
			}
			return value;
		}

		public int ItemCount(ItemIndex index)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			if (!Items.TryGetValue(index, out var value))
			{
				return 0;
			}
			return value.Total;
		}
	}
	public enum LootCategory
	{
		Chests,
		Shops,
		Drones,
		Shrines,
		Barrels,
		Other
	}
	public sealed class LootEntry
	{
		public LootCategory Category;

		public string Token;

		public bool Used;
	}
	public sealed class StageRecord
	{
		public int Index;

		public int StageNumber;

		public SceneDef Scene;

		public string SceneName;

		public string NameToken;

		public string DisplayName;

		public string Subtitle;

		public bool IsIntermission;

		public int StageClearCountAtStart;

		public float StartTime;

		public float EndTime;

		public bool Closed;

		public readonly Dictionary<int, PlayerSnapshot> Start = new Dictionary<int, PlayerSnapshot>();

		public readonly Dictionary<int, PlayerSnapshot> End = new Dictionary<int, PlayerSnapshot>();

		public readonly Dictionary<int, LootEntry> Loot = new Dictionary<int, LootEntry>();

		public bool LootScanned;

		public bool LootTimedScanDone;

		public float OpenedAtUnscaled;

		public float Duration => Math.Max(0f, EndTime - StartTime);

		public List<(LootCategory category, int used, int total)> LootSummary()
		{
			List<(LootCategory, int, int)> list = new List<(LootCategory, int, int)>();
			foreach (LootCategory value in Enum.GetValues(typeof(LootCategory)))
			{
				if (value == LootCategory.Other)
				{
					continue;
				}
				int num = 0;
				int num2 = 0;
				foreach (LootEntry value2 in Loot.Values)
				{
					if (value2.Category == value)
					{
						num2++;
						if (value2.Used)
						{
							num++;
						}
					}
				}
				if (num2 > 0)
				{
					list.Add((value, num, num2));
				}
			}
			return list;
		}

		public int LootMissed()
		{
			int num = 0;
			foreach (LootEntry value in Loot.Values)
			{
				if (value.Category != LootCategory.Other && !value.Used)
				{
					num++;
				}
			}
			return num;
		}

		public PlayerSnapshot StartOf(int playerKey)
		{
			if (!Start.TryGetValue(playerKey, out var value))
			{
				return PlayerSnapshot.Empty;
			}
			return value;
		}

		public PlayerSnapshot EndOf(int playerKey)
		{
			if (!End.TryGetValue(playerKey, out var value))
			{
				return StartOf(playerKey);
			}
			return value;
		}

		public double StatDelta(int playerKey, string statKey)
		{
			return Math.Max(0.0, EndOf(playerKey).Stat(statKey) - StartOf(playerKey).Stat(statKey));
		}

		public Dictionary<ItemIndex, int> ItemsGained(int playerKey)
		{
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			PlayerSnapshot playerSnapshot = StartOf(playerKey);
			Dictionary<ItemIndex, int> dictionary = new Dictionary<ItemIndex, int>();
			foreach (KeyValuePair<ItemIndex, ItemCounts> item in EndOf(playerKey).Items)
			{
				int num = item.Value.Total - playerSnapshot.ItemCount(item.Key);
				if (num > 0)
				{
					dictionary[item.Key] = num;
				}
			}
			return dictionary;
		}
	}
	public sealed class RunRecord
	{
		public DateTime StartedUtc = DateTime.UtcNow;

		public string DifficultyName = "";

		public bool Ended;

		public bool IsHost;

		public readonly List<StageRecord> Stages = new List<StageRecord>();

		public readonly Dictionary<int, PlayerInfo> Players = new Dictionary<int, PlayerInfo>();

		public StageRecord CurrentStage
		{
			get
			{
				if (Stages.Count <= 0)
				{
					return null;
				}
				return Stages[Stages.Count - 1];
			}
		}

		public PlayerInfo LocalPlayer
		{
			get
			{
				foreach (PlayerInfo value in Players.Values)
				{
					if (value.IsLocal)
					{
						return value;
					}
				}
				return null;
			}
		}
	}
}
namespace StageDebrief.Core
{
	internal static class GameCompat
	{
		public static float GetRunTime()
		{
			Run instance = Run.instance;
			if (!Object.op_Implicit((Object)(object)instance))
			{
				return 0f;
			}
			return instance.GetRunStopwatch();
		}

		public static double ReadStat(StatSheet sheet, StatDef def)
		{
			if (sheet == null || def == null)
			{
				return 0.0;
			}
			return sheet.GetStatValueAsDouble(def);
		}

		public static bool IsConsoleOpen()
		{
			try
			{
				return ConsoleWindow.IsShown();
			}
			catch (Exception e)
			{
				Log.ErrorOnce("compat-console", e);
				return false;
			}
		}

		public static bool IsIntermission(SceneDef sceneDef)
		{
			if (Object.op_Implicit((Object)(object)sceneDef))
			{
				return ((object)Unsafe.As<SceneType, SceneType>(ref sceneDef.sceneType)/*cast due to .constrained prefix*/).ToString().IndexOf("Intermission", StringComparison.OrdinalIgnoreCase) >= 0;
			}
			return false;
		}

		public static bool IsPlayableScene(SceneDef sceneDef)
		{
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Invalid comparison between Unknown and I4
			if (Object.op_Implicit((Object)(object)sceneDef) && (int)sceneDef.sceneType != 0)
			{
				return (int)sceneDef.sceneType != 3;
			}
			return false;
		}
	}
	internal static class Log
	{
		private static ManualLogSource source;

		private static readonly HashSet<string> reported = new HashSet<string>();

		internal static void Init(ManualLogSource logSource)
		{
			source = logSource;
		}

		internal static void Info(object message)
		{
			ManualLogSource obj = source;
			if (obj != null)
			{
				obj.LogInfo(message);
			}
		}

		internal static void Warn(object message)
		{
			ManualLogSource obj = source;
			if (obj != null)
			{
				obj.LogWarning(message);
			}
		}

		internal static void Error(object message)
		{
			ManualLogSource obj = source;
			if (obj != null)
			{
				obj.LogError(message);
			}
		}

		internal static void Debug(object message)
		{
			if (StageDebriefPlugin.Settings != null && StageDebriefPlugin.Settings.DebugLogging.Value)
			{
				ManualLogSource obj = source;
				if (obj != null)
				{
					obj.LogInfo((object)("[debug] " + message));
				}
			}
		}

		internal static void ErrorOnce(string key, Exception e)
		{
			if (reported.Add(key))
			{
				ManualLogSource obj = source;
				if (obj != null)
				{
					obj.LogError((object)$"[{key}] {e}");
				}
			}
		}

		internal static void WarnOnce(string key, string message)
		{
			if (reported.Add(key))
			{
				ManualLogSource obj = source;
				if (obj != null)
				{
					obj.LogWarning((object)message);
				}
			}
		}
	}
	internal enum AnimationMode
	{
		Full,
		Reduced,
		Off
	}
	internal sealed class PluginConfig
	{
		public readonly ConfigEntry<bool> AutoShowOnExit;

		public readonly ConfigEntry<float> AutoHideAfter;

		public readonly ConfigEntry<KeyboardShortcut> ShowKey;

		public readonly ConfigEntry<float> UiScale;

		public readonly ConfigEntry<float> VerticalOffset;

		public readonly ConfigEntry<float> PanelOpacity;

		public readonly ConfigEntry<AnimationMode> Animations;

		public readonly ConfigEntry<float> AnimationSpeed;

		public readonly ConfigEntry<bool> DebugLogging;

		public PluginConfig(ConfigFile config)
		{
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0056: Expected O, but got Unknown
			//IL_0071: Unknown result type (might be due to invalid IL or missing references)
			//IL_00af: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b9: Expected O, but got Unknown
			//IL_00e8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f2: Expected O, but got Unknown
			//IL_0121: Unknown result type (might be due to invalid IL or missing references)
			//IL_012b: Expected O, but got Unknown
			//IL_0176: Unknown result type (might be due to invalid IL or missing references)
			//IL_0180: Expected O, but got Unknown
			AutoShowOnExit = config.Bind<bool>("General", "Show when leaving a stage", true, "Shows the debrief automatically when the teleporter (or any stage exit) starts taking you out. It hides when the next stage loads.");
			AutoHideAfter = config.Bind<float>("General", "Auto-hide after seconds", 30f, new ConfigDescription("Safety limit: hides the automatic debrief after this many seconds even if the next stage has not loaded yet.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(5f, 120f), Array.Empty<object>()));
			ShowKey = config.Bind<KeyboardShortcut>("General", "Peek key", new KeyboardShortcut((KeyCode)287, Array.Empty<KeyCode>()), "Shows or hides the debrief for the current stage at any time.");
			UiScale = config.Bind<float>("Layout", "UI scale", 1f, new ConfigDescription("Overall size of the debrief.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.6f, 1.5f), Array.Empty<object>()));
			VerticalOffset = config.Bind<float>("Layout", "Vertical position", 0.08f, new ConfigDescription("Where the debrief sits: 0 is the screen centre, positive moves it up, negative down (fraction of screen height).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(-0.35f, 0.35f), Array.Empty<object>()));
			PanelOpacity = config.Bind<float>("Layout", "Panel opacity", 0.92f, new ConfigDescription("Background opacity.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.6f, 1f), Array.Empty<object>()));
			Animations = config.Bind<AnimationMode>("Animation", "Animations", AnimationMode.Full, "Full: slide, fade and count-up numbers. Reduced: fade only. Off: instant.");
			AnimationSpeed = config.Bind<float>("Animation", "Speed multiplier", 1f, new ConfigDescription("Higher is faster.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.5f, 2f), Array.Empty<object>()));
			DebugLogging = config.Bind<bool>("Debug", "Verbose logging", false, "Writes extra tracking details to the BepInEx log. Useful when reporting a bug.");
		}
	}
}