Decompiled source of Milestones v0.1.0

plugins/Milestones/Milestones.dll

Decompiled 2 days ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Milestones.Core;
using Milestones.Core.Model;
using Milestones.UI;
using TMPro;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: IgnoresAccessChecksTo("assembly_valheim")]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyCompany("Milestones")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyDescription("Real progress bars for Valheim's achievements, a pinned tracker and progress toasts.")]
[assembly: AssemblyFileVersion("0.1.0.0")]
[assembly: AssemblyInformationalVersion("0.1.0+dfa0aff01eeb973e2b7a80fc4221d5803de46124")]
[assembly: AssemblyProduct("Milestones")]
[assembly: AssemblyTitle("Milestones")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("0.1.0.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
internal sealed class ConfigurationManagerAttributes
{
	public int? Order;

	public bool? IsAdvanced;

	public bool? Browsable;

	public bool? ReadOnly;
}
namespace Milestones
{
	[BepInPlugin("com.jumpingmushroom.milestones", "Milestones", "0.1.0")]
	[BepInProcess("valheim.exe")]
	[BepInProcess("valheim.x86_64")]
	public sealed class MilestonesPlugin : BaseUnityPlugin
	{
		public const string PluginGuid = "com.jumpingmushroom.milestones";

		public const string PluginName = "Milestones";

		public const string PluginVersion = "0.1.0";

		internal static ManualLogSource Log;

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

		private Harmony _harmony;

		private void Awake()
		{
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			PluginConfig.Bind(((BaseUnityPlugin)this).Config);
			ConsoleCommands.Register();
			Pins.Init();
			PinButton.Init();
			ListMarks.Init();
			TrackerHud.Init();
			Toasts.Init();
			_harmony = new Harmony("com.jumpingmushroom.milestones");
			_harmony.PatchAll(typeof(MilestonesPlugin).Assembly);
			((BaseUnityPlugin)this).Logger.LogInfo((object)"Milestones 0.1.0 loaded.");
		}

		private void Update()
		{
			Runtime.Tick();
		}

		private void OnDestroy()
		{
			if (_harmony != null)
			{
				_harmony.UnpatchSelf();
			}
		}

		internal static void WarnOnce(string key, Exception e)
		{
			if (Warned.Add(key))
			{
				Log.LogWarning((object)(key + ": " + e));
			}
		}
	}
	public enum TrackerAnchor
	{
		UnderStatusEffects,
		TopLeft,
		TopRight,
		LeftMiddle,
		RightMiddle,
		BottomLeft,
		BottomRight
	}
	public enum ToastPosition
	{
		TopLeft,
		Center
	}
	public static class PluginConfig
	{
		public static ConfigEntry<bool> Enabled;

		public static ConfigEntry<OverallMode> Overall;

		public static ConfigEntry<bool> SortObjectives;

		public static ConfigEntry<bool> HideUnmetNames;

		public static ConfigEntry<bool> ShowTracker;

		public static ConfigEntry<TrackerAnchor> Anchor;

		public static ConfigEntry<float> OffsetX;

		public static ConfigEntry<float> OffsetY;

		public static ConfigEntry<float> Scale;

		public static ConfigEntry<float> Opacity;

		public static ConfigEntry<float> Width;

		public static ConfigEntry<int> MaxRowsPerAchievement;

		public static ConfigEntry<bool> HideWhenInventoryOpen;

		public static ConfigEntry<bool> AutoUnpinCompleted;

		public static ConfigEntry<PinnedToastMode> PinnedToasts;

		public static ConfigEntry<UnpinnedToastMode> UnpinnedToasts;

		public static ConfigEntry<string> Thresholds;

		public static ConfigEntry<ToastPosition> ToastAt;

		public static ConfigEntry<Color> ColorNotStarted;

		public static ConfigEntry<Color> ColorInProgress;

		public static ConfigEntry<Color> ColorDone;

		public static ConfigEntry<bool> Verbose;

		private static ConfigurationManagerAttributes Attr(int order, bool advanced = false)
		{
			return new ConfigurationManagerAttributes
			{
				Order = order,
				IsAdvanced = advanced
			};
		}

		private static Color Hex(string hex)
		{
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			Color result = default(Color);
			ColorUtility.TryParseHtmlString(hex, ref result);
			return result;
		}

		public static void Bind(ConfigFile cfg)
		{
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Expected O, but got Unknown
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			//IL_005f: Expected O, but got Unknown
			//IL_0087: Unknown result type (might be due to invalid IL or missing references)
			//IL_0091: Expected O, but got Unknown
			//IL_00b9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c3: Expected O, but got Unknown
			//IL_00eb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f5: Expected O, but got Unknown
			//IL_011d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0127: Expected O, but got Unknown
			//IL_0161: Unknown result type (might be due to invalid IL or missing references)
			//IL_016b: Expected O, but got Unknown
			//IL_01a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_01af: Expected O, but got Unknown
			//IL_01e9: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f3: Expected O, but got Unknown
			//IL_022d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0237: Expected O, but got Unknown
			//IL_0271: Unknown result type (might be due to invalid IL or missing references)
			//IL_027b: Expected O, but got Unknown
			//IL_02aa: Unknown result type (might be due to invalid IL or missing references)
			//IL_02b4: Expected O, but got Unknown
			//IL_02dc: Unknown result type (might be due to invalid IL or missing references)
			//IL_02e6: Expected O, but got Unknown
			//IL_030e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0318: Expected O, but got Unknown
			//IL_0340: Unknown result type (might be due to invalid IL or missing references)
			//IL_034a: Expected O, but got Unknown
			//IL_0372: Unknown result type (might be due to invalid IL or missing references)
			//IL_037c: Expected O, but got Unknown
			//IL_03a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_03b2: Expected O, but got Unknown
			//IL_03da: Unknown result type (might be due to invalid IL or missing references)
			//IL_03e4: Expected O, but got Unknown
			//IL_03f9: Unknown result type (might be due to invalid IL or missing references)
			//IL_0415: Unknown result type (might be due to invalid IL or missing references)
			//IL_041f: Expected O, but got Unknown
			//IL_0434: Unknown result type (might be due to invalid IL or missing references)
			//IL_0450: Unknown result type (might be due to invalid IL or missing references)
			//IL_045a: Expected O, but got Unknown
			//IL_046f: Unknown result type (might be due to invalid IL or missing references)
			//IL_048b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0495: Expected O, but got Unknown
			//IL_04bc: Unknown result type (might be due to invalid IL or missing references)
			//IL_04c6: Expected O, but got Unknown
			Enabled = cfg.Bind<bool>("General", "Enabled", true, new ConfigDescription("Master switch. Off restores the vanilla details panel and hides the tracker and toasts.", (AcceptableValueBase)null, new object[1] { Attr(100) }));
			Overall = cfg.Bind<OverallMode>("General", "OverallMode", OverallMode.Auto, new ConfigDescription("How the overall bar is measured. Count: objectives met out of all (37 / 212). Average: mean fill of every objective. Auto: Count when every objective is 'do this once' (eat every food, kill every creature), Average otherwise.", (AcceptableValueBase)null, new object[1] { Attr(95) }));
			SortObjectives = cfg.Bind<bool>("General", "SortObjectives", true, new ConfigDescription("Unfinished objectives first, closest to done at the top. Off keeps the game's order.", (AcceptableValueBase)null, new object[1] { Attr(90) }));
			HideUnmetNames = cfg.Bind<bool>("General", "HideUnmetNames", false, new ConfigDescription("Spoiler guard: unmet objectives keep the game's ??? name but still show their bar and numbers. Useful for Kill every creature and Find every trophy on a first run.", (AcceptableValueBase)null, new object[1] { Attr(85) }));
			ShowTracker = cfg.Bind<bool>("Tracker", "ShowTracker", true, new ConfigDescription("Show pinned achievements on the HUD.", (AcceptableValueBase)null, new object[1] { Attr(80) }));
			Anchor = cfg.Bind<TrackerAnchor>("Tracker", "Anchor", TrackerAnchor.UnderStatusEffects, new ConfigDescription("Where the tracker sits. UnderStatusEffects follows the status-effect icons beside the minimap and moves down when they wrap into a second row.", (AcceptableValueBase)null, new object[1] { Attr(78) }));
			OffsetX = cfg.Bind<float>("Tracker", "OffsetX", 0f, new ConfigDescription("Horizontal nudge in HUD pixels (positive is right).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(-1000f, 1000f), new object[1] { Attr(76) }));
			OffsetY = cfg.Bind<float>("Tracker", "OffsetY", 0f, new ConfigDescription("Vertical nudge in HUD pixels (positive is up).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(-1000f, 1000f), new object[1] { Attr(75) }));
			Scale = cfg.Bind<float>("Tracker", "Scale", 1f, new ConfigDescription("Size of the tracker.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.5f, 2f), new object[1] { Attr(74) }));
			Opacity = cfg.Bind<float>("Tracker", "Opacity", 0.9f, new ConfigDescription("Opacity of the whole tracker.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.2f, 1f), new object[1] { Attr(73) }));
			Width = cfg.Bind<float>("Tracker", "Width", 260f, new ConfigDescription("Width in HUD pixels before scaling.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(160f, 500f), new object[1] { Attr(72) }));
			MaxRowsPerAchievement = cfg.Bind<int>("Tracker", "MaxRowsPerAchievement", 4, new ConfigDescription("Objective rows per pinned achievement. Larger ones show the unfinished objectives closest to done, then '+N more'.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 10), new object[1] { Attr(70) }));
			HideWhenInventoryOpen = cfg.Bind<bool>("Tracker", "HideWhenInventoryOpen", true, new ConfigDescription("Hide the tracker while the inventory or achievements screen is open.", (AcceptableValueBase)null, new object[1] { Attr(68) }));
			AutoUnpinCompleted = cfg.Bind<bool>("Tracker", "AutoUnpinCompleted", true, new ConfigDescription("Unpin an achievement 10 seconds after it unlocks. Off keeps it pinned, showing Done.", (AcceptableValueBase)null, new object[1] { Attr(66) }));
			PinnedToasts = cfg.Bind<PinnedToastMode>("Toasts", "PinnedToasts", PinnedToastMode.ObjectivesAndThresholds, new ConfigDescription("Toasts for pinned achievements.", (AcceptableValueBase)null, new object[1] { Attr(60) }));
			UnpinnedToasts = cfg.Bind<UnpinnedToastMode>("Toasts", "UnpinnedToasts", UnpinnedToastMode.ThresholdsOnly, new ConfigDescription("Toasts for every other achievement.", (AcceptableValueBase)null, new object[1] { Attr(58) }));
			Thresholds = cfg.Bind<string>("Toasts", "Thresholds", "25,50,75", new ConfigDescription("Overall percentages that trigger a toast, comma-separated, 1-99.", (AcceptableValueBase)null, new object[1] { Attr(56) }));
			ToastAt = cfg.Bind<ToastPosition>("Toasts", "ToastPosition", ToastPosition.TopLeft, new ConfigDescription("TopLeft uses the game's pickup-message feed; Center the big centre message.", (AcceptableValueBase)null, new object[1] { Attr(54) }));
			ColorNotStarted = cfg.Bind<Color>("Colors", "NotStarted", Hex("#7a7a7a"), new ConfigDescription("Bar colour for an objective with no progress.", (AcceptableValueBase)null, new object[1] { Attr(40) }));
			ColorInProgress = cfg.Bind<Color>("Colors", "InProgress", Hex("#e0a030"), new ConfigDescription("Bar colour for an objective under way.", (AcceptableValueBase)null, new object[1] { Attr(38) }));
			ColorDone = cfg.Bind<Color>("Colors", "Done", Hex("#5ac85a"), new ConfigDescription("Bar colour for a met objective.", (AcceptableValueBase)null, new object[1] { Attr(36) }));
			Verbose = cfg.Bind<bool>("Logging", "Verbose", false, new ConfigDescription("Log recomputes, pin changes and toasts to the BepInEx log.", (AcceptableValueBase)null, new object[1] { Attr(5, advanced: true) }));
		}
	}
}
namespace Milestones.UI
{
	internal sealed class Bar : MonoBehaviour
	{
		private const float FullSweepSeconds = 0.3f;

		private Image _fill;

		private float _target;

		private float _shown;

		public static Bar CreateFloating(RectTransform row, float height, float inset)
		{
			//IL_001a: 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_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: 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_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_0064: Unknown result type (might be due to invalid IL or missing references)
			//IL_006e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0079: Unknown result type (might be due to invalid IL or missing references)
			//IL_008b: Unknown result type (might be due to invalid IL or missing references)
			Bar bar = Build((Transform)(object)row, "MilestonesBar", new Color(1f, 1f, 1f, 0.12f));
			RectTransform val = (RectTransform)((Component)bar).transform;
			val.anchorMin = new Vector2(0f, 0f);
			val.anchorMax = new Vector2(1f, 0f);
			val.pivot = new Vector2(0.5f, 0f);
			val.anchoredPosition = new Vector2(0f, 1f);
			val.sizeDelta = new Vector2(-2f * inset, height);
			((Component)bar).gameObject.AddComponent<LayoutElement>().ignoreLayout = true;
			return bar;
		}

		public static Bar CreateInLayout(Transform parent, float height)
		{
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			Bar bar = Build(parent, "MilestonesBar", new Color(0f, 0f, 0f, 0.55f));
			LayoutElement obj = ((Component)bar).gameObject.AddComponent<LayoutElement>();
			obj.minHeight = height;
			obj.preferredHeight = height;
			obj.flexibleWidth = 1f;
			return bar;
		}

		private static Bar Build(Transform parent, string name, Color track)
		{
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			RectTransform val = UiUtil.Rect(name, parent);
			Image obj = ((Component)val).gameObject.AddComponent<Image>();
			obj.sprite = UiUtil.White;
			((Graphic)obj).color = track;
			((Graphic)obj).raycastTarget = false;
			RectTransform obj2 = UiUtil.Rect("fill", (Transform)(object)val);
			UiUtil.Stretch(obj2);
			Image val2 = ((Component)obj2).gameObject.AddComponent<Image>();
			val2.sprite = UiUtil.White;
			val2.type = (Type)3;
			val2.fillMethod = (FillMethod)0;
			val2.fillOrigin = 0;
			val2.fillAmount = 0f;
			((Graphic)val2).raycastTarget = false;
			Bar bar = ((Component)val).gameObject.AddComponent<Bar>();
			bar._fill = val2;
			return bar;
		}

		public void Set(float fraction, Color color, bool ease)
		{
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			_target = Mathf.Clamp01(fraction);
			((Graphic)_fill).color = color;
			if (!ease)
			{
				_shown = _target;
				_fill.fillAmount = _shown;
			}
		}

		private void Update()
		{
			if (_shown != _target)
			{
				_shown = Mathf.MoveTowards(_shown, _target, Time.unscaledDeltaTime / 0.3f);
				_fill.fillAmount = _shown;
			}
		}
	}
	internal static class DetailsPanel
	{
		private enum RowResult
		{
			Added,
			Stop,
			Failed
		}

		private const float ExtraRowHeight = 8f;

		private static AchievementsGui _gui;

		private static GridLayoutGroup _grid;

		private static Vector2 _vanillaCell;

		private static ProgressRow _header;

		private static readonly List<ProgressRow> Rows;

		public static Achievement Current { get; private set; }

		public static bool Failed { get; private set; }

		public static event Action<AchievementsGui, Achievement> Opened;

		static DetailsPanel()
		{
			Rows = new List<ProgressRow>();
			ProgressCache.Changed += OnChanged;
		}

		public static void Open(AchievementsGui gui, Achievement a)
		{
			_gui = gui;
			Current = a;
			Rows.Clear();
			GrowRows(gui);
			gui.m_achievementDetails.SetActive(true);
			ProgressCache.Recompute(a);
			AchievementProgress achievementProgress = ProgressCache.Get(a);
			_header = new ProgressRow(NewRow(gui), -1);
			_header.ShowOverall(a, achievementProgress, ease: false);
			List<int> list;
			if (PluginConfig.SortObjectives.Value)
			{
				list = ObjectiveOrder.Sorted(achievementProgress);
			}
			else
			{
				list = new List<int>(achievementProgress.Total);
				for (int i = 0; i < achievementProgress.Total; i++)
				{
					list.Add(i);
				}
			}
			((MonoBehaviour)gui).StartCoroutine(Populate(gui, a, list));
			if (DetailsPanel.Opened != null)
			{
				DetailsPanel.Opened(gui, a);
			}
		}

		private static IEnumerator Populate(AchievementsGui gui, Achievement a, List<int> order)
		{
			int batch = Math.Max(1, gui.m_detailStatsPerFrame);
			int n = 0;
			foreach (int item in order)
			{
				switch (AddRow(gui, a, item))
				{
				case RowResult.Failed:
					Fail(gui, a);
					yield break;
				case RowResult.Stop:
					yield break;
				}
				int num = n + 1;
				n = num;
				if (num == batch)
				{
					n = 0;
					yield return null;
				}
			}
		}

		private static RowResult AddRow(AchievementsGui gui, Achievement a, int i)
		{
			if ((Object)(object)Current != (Object)(object)a || (Object)(object)gui == (Object)null || !gui.m_achievementDetails.activeSelf)
			{
				return RowResult.Stop;
			}
			try
			{
				AchievementProgress achievementProgress = ProgressCache.Get(a);
				if (i >= achievementProgress.Total)
				{
					return RowResult.Stop;
				}
				ProgressRow progressRow = new ProgressRow(NewRow(gui), i);
				progressRow.Show(achievementProgress.Objectives[i], ease: false);
				Rows.Add(progressRow);
				return RowResult.Added;
			}
			catch (Exception e)
			{
				MilestonesPlugin.WarnOnce("details panel", e);
				return RowResult.Failed;
			}
		}

		private static void Fail(AchievementsGui gui, Achievement a)
		{
			MarkFailed();
			Abort(gui);
			gui.OnOpenAchievementDetails(a, true);
		}

		public static void MarkFailed()
		{
			Failed = true;
		}

		private static void GrowRows(AchievementsGui gui)
		{
			//IL_004f: 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_002f: Unknown result type (might be due to invalid IL or missing references)
			GridLayoutGroup component = ((Component)gui.m_achievementDetailsListRoot).GetComponent<GridLayoutGroup>();
			if (!((Object)(object)component == (Object)null))
			{
				if ((Object)(object)component != (Object)(object)_grid)
				{
					_grid = component;
					_vanillaCell = component.cellSize;
				}
				component.cellSize = new Vector2(_vanillaCell.x, _vanillaCell.y + 8f);
			}
		}

		private static void RestoreRows()
		{
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)_grid != (Object)null)
			{
				_grid.cellSize = _vanillaCell;
			}
		}

		private static AchievementDetailUnlockCondition NewRow(AchievementsGui gui)
		{
			AchievementDetailUnlockCondition obj = Object.Instantiate<AchievementDetailUnlockCondition>(gui.m_achievementDetailsElementPrefab, (Transform)(object)gui.m_achievementDetailsListRoot);
			((Component)obj).gameObject.SetActive(true);
			return obj;
		}

		private static void OnChanged(Achievement a, AchievementProgress p)
		{
			if ((Object)(object)a != (Object)(object)Current || (Object)(object)_gui == (Object)null || !_gui.m_achievementDetails.activeSelf)
			{
				return;
			}
			if (_header != null && _header.Alive)
			{
				_header.ShowOverall(a, p, ease: true);
			}
			foreach (ProgressRow row in Rows)
			{
				if (row.Alive && row.ObjectiveIndex < p.Total)
				{
					row.Show(p.Objectives[row.ObjectiveIndex], ease: true);
				}
			}
		}

		public static void Closed()
		{
			Current = null;
			_header = null;
			Rows.Clear();
			RestoreRows();
			PinButton.Hide();
		}

		public static void Abort(AchievementsGui gui)
		{
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			Current = null;
			foreach (Transform item in (Transform)gui.m_achievementDetailsListRoot)
			{
				Object.Destroy((Object)(object)((Component)item).gameObject);
			}
			gui.m_achievementDetails.SetActive(false);
			_header = null;
			Rows.Clear();
			RestoreRows();
			PinButton.Hide();
		}
	}
	internal static class ListMarks
	{
		private const string MarkName = "MilestonesPinMark";

		public static void Init()
		{
			Pins.Changed += delegate
			{
				InventoryGui instance = InventoryGui.instance;
				if ((Object)(object)instance != (Object)null && instance.IsAchievementsPanelOpen)
				{
					Apply(instance);
				}
			};
		}

		public static void Apply(InventoryGui inv)
		{
			//IL_00b3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f6: Unknown result type (might be due to invalid IL or missing references)
			//IL_011b: Unknown result type (might be due to invalid IL or missing references)
			List<GameObject> achievementsList = inv.m_achievementsPanel.m_achievementsList;
			List<Achievement> list = AchievementReader.GridOrder();
			if (list.Count != achievementsList.Count)
			{
				return;
			}
			Vector2 val3 = default(Vector2);
			for (int i = 0; i < achievementsList.Count; i++)
			{
				if (!((Object)(object)achievementsList[i] == (Object)null))
				{
					Transform val = achievementsList[i].transform.Find("icon") ?? achievementsList[i].transform;
					Transform val2 = val.Find("MilestonesPinMark");
					bool flag = Pins.Contains(list[i].m_id);
					if (flag && (Object)(object)val2 == (Object)null)
					{
						RectTransform obj = UiUtil.Rect("MilestonesPinMark", val);
						((Vector2)(ref val3))..ctor(1f, 1f);
						obj.anchorMax = val3;
						obj.anchorMin = val3;
						obj.pivot = new Vector2(0.5f, 0.5f);
						obj.anchoredPosition = new Vector2(-6f, -6f);
						obj.sizeDelta = new Vector2(14f, 14f);
						Image obj2 = ((Component)obj).gameObject.AddComponent<Image>();
						obj2.sprite = UiUtil.White;
						((Graphic)obj2).color = PluginConfig.ColorInProgress.Value;
						((Graphic)obj2).raycastTarget = false;
					}
					else if (!flag && (Object)(object)val2 != (Object)null)
					{
						Object.Destroy((Object)(object)((Component)val2).gameObject);
					}
				}
			}
		}
	}
	internal static class PinButton
	{
		[CompilerGenerated]
		private static class <>O
		{
			public static Action<AchievementsGui, Achievement> <0>__Attach;

			public static Action <1>__Refresh;

			public static Action <2>__PollGamepad;

			public static UnityAction <3>__OnClick;
		}

		private static Button _button;

		private static TMP_Text _label;

		private static Achievement _for;

		public static void Init()
		{
			DetailsPanel.Opened += Attach;
			Pins.Changed += Refresh;
			Runtime.Ticked += PollGamepad;
		}

		private static void Attach(AchievementsGui gui, Achievement a)
		{
			_for = a;
			if ((Object)(object)_button == (Object)null)
			{
				Create(gui);
			}
			if ((Object)(object)_button != (Object)null)
			{
				((Component)_button).gameObject.SetActive(true);
			}
			Refresh();
		}

		internal static void Hide()
		{
			if ((Object)(object)_button != (Object)null)
			{
				((Component)_button).gameObject.SetActive(false);
			}
		}

		private static Button FindCloseButton(AchievementsGui gui)
		{
			Button val = null;
			Button[] componentsInChildren = gui.m_achievementDetails.GetComponentsInChildren<Button>(true);
			foreach (Button val2 in componentsInChildren)
			{
				if (((Object)val2).name == "MilestonesPin")
				{
					continue;
				}
				if ((Object)(object)val == (Object)null)
				{
					val = val2;
				}
				for (int j = 0; j < ((UnityEventBase)val2.onClick).GetPersistentEventCount(); j++)
				{
					if (((UnityEventBase)val2.onClick).GetPersistentMethodName(j) == "OnCloseAchievementDetails")
					{
						return val2;
					}
				}
			}
			return val;
		}

		private static void Create(AchievementsGui gui)
		{
			//IL_00ca: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d4: Expected O, but got Unknown
			//IL_0110: Unknown result type (might be due to invalid IL or missing references)
			//IL_011b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0121: Expected O, but got Unknown
			//IL_0122: Unknown result type (might be due to invalid IL or missing references)
			//IL_0128: Unknown result type (might be due to invalid IL or missing references)
			//IL_012d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0141: Unknown result type (might be due to invalid IL or missing references)
			//IL_0146: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ee: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f9: Expected O, but got Unknown
			Button val = FindCloseButton(gui);
			if ((Object)(object)val == (Object)null)
			{
				MilestonesPlugin.Log.LogWarning((object)"Milestones: no button in the details panel to clone; pin with 'milestones pin <id>'.");
				return;
			}
			GameObject val2 = Object.Instantiate<GameObject>(((Component)val).gameObject, ((Component)val).transform.parent);
			((Object)val2).name = "MilestonesPin";
			Component[] componentsInChildren = val2.GetComponentsInChildren<Component>(true);
			foreach (Component val3 in componentsInChildren)
			{
				if (!((Object)(object)val3 == (Object)null))
				{
					if (((object)val3).GetType().Name == "UIInputHint")
					{
						Object.Destroy((Object)(object)val3.gameObject);
					}
					else if (val3 is UIGamePad || ((object)val3).GetType().Name == "Localize")
					{
						Object.Destroy((Object)(object)val3);
					}
				}
			}
			_button = val2.GetComponent<Button>();
			_button.onClick = new ButtonClickedEvent();
			ButtonClickedEvent onClick = _button.onClick;
			object obj = <>O.<3>__OnClick;
			if (obj == null)
			{
				UnityAction val4 = OnClick;
				<>O.<3>__OnClick = val4;
				obj = (object)val4;
			}
			((UnityEvent)onClick).AddListener((UnityAction)obj);
			_label = val2.GetComponentInChildren<TMP_Text>(true);
			RectTransform val5 = (RectTransform)val2.transform;
			RectTransform val6 = (RectTransform)((Component)val).transform;
			Vector2 anchoredPosition = val6.anchoredPosition;
			Rect rect = val6.rect;
			val5.anchoredPosition = anchoredPosition - new Vector2(((Rect)(ref rect)).width + 12f, 0f);
		}

		private static void OnClick()
		{
			if ((Object)(object)_for != (Object)null && (Object)(object)_for == (Object)(object)DetailsPanel.Current)
			{
				Pins.Toggle(_for);
			}
		}

		private static void Refresh()
		{
			if (!((Object)(object)_button == (Object)null) && !((Object)(object)_for == (Object)null) && !((Object)(object)_label == (Object)null))
			{
				bool flag = Pins.Contains(_for.m_id);
				_label.text = (flag ? "Unpin" : (Pins.IsFull ? "Unpin one first (3/3)" : ("Pin (" + Pins.Count + "/" + 3 + ")")));
				((Selectable)_button).interactable = flag || !Pins.IsFull;
			}
		}

		private static void PollGamepad()
		{
			if (!((Object)(object)DetailsPanel.Current == (Object)null) && !((Object)(object)_for != (Object)(object)DetailsPanel.Current) && ZInput.IsGamepadActive() && ZInput.GetButtonDown("JoyButtonX") && (Object)(object)_button != (Object)null && ((Selectable)_button).interactable)
			{
				OnClick();
			}
		}
	}
	internal sealed class ProgressRow
	{
		public const int Header = -1;

		public readonly AchievementDetailUnlockCondition Row;

		public readonly int ObjectiveIndex;

		private readonly Bar _bar;

		public bool Alive => (Object)(object)Row != (Object)null;

		public ProgressRow(AchievementDetailUnlockCondition row, int objectiveIndex)
		{
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Expected O, but got Unknown
			Row = row;
			ObjectiveIndex = objectiveIndex;
			_bar = Bar.CreateFloating((RectTransform)((Component)row).transform, (objectiveIndex == -1) ? 6f : 4f, 4f);
		}

		public void Show(Objective o, bool ease)
		{
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			//IL_0077: 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)
			bool flag = PluginConfig.HideUnmetNames.Value && !o.Met;
			((TMP_Text)Row.StatName).text = (flag ? "???" : o.Label);
			((TMP_Text)Row.Progress).text = Labels.ProgressText(o);
			Color color = UiUtil.TextColor(o.Met);
			((Graphic)Row.StatName).color = color;
			((Graphic)Row.Progress).color = color;
			_bar.Set(o.Fraction, UiUtil.BarColor(o), ease);
		}

		public void ShowOverall(Achievement a, AchievementProgress p, bool ease)
		{
			//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)
			//IL_007a: Unknown result type (might be due to invalid IL or missing references)
			//IL_008b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a9: Unknown result type (might be due to invalid IL or missing references)
			((TMP_Text)Row.StatName).text = AchievementReader.Name(a);
			((TMP_Text)Row.Progress).text = (p.ShowAsCount ? (p.MetCount + " / " + p.Total + " objectives") : Labels.Percent(p.Overall));
			Color color = UiUtil.TextColor(p.Unlocked);
			((Graphic)Row.StatName).color = color;
			((Graphic)Row.Progress).color = color;
			_bar.Set(p.Overall, UiUtil.BarColor(p.Overall, p.Unlocked), ease);
		}
	}
	internal static class Toasts
	{
		private static readonly Dictionary<string, Snapshot> Seen = new Dictionary<string, Snapshot>();

		private static ToastRules _rules;

		private static ToastRules Rules
		{
			get
			{
				if (_rules == null)
				{
					_rules = new ToastRules
					{
						Pinned = PluginConfig.PinnedToasts.Value,
						Unpinned = PluginConfig.UnpinnedToasts.Value,
						Thresholds = ToastRules.ParseThresholds(PluginConfig.Thresholds.Value)
					};
				}
				return _rules;
			}
		}

		public static void Init()
		{
			Runtime.PlayerSpawned += delegate
			{
				Reset();
			};
			ProgressCache.Changed += OnChanged;
			PluginConfig.Thresholds.SettingChanged += delegate
			{
				_rules = null;
			};
			PluginConfig.PinnedToasts.SettingChanged += delegate
			{
				_rules = null;
			};
			PluginConfig.UnpinnedToasts.SettingChanged += delegate
			{
				_rules = null;
			};
			PluginConfig.Overall.SettingChanged += delegate
			{
				ProgressCache.Clear();
				Reset();
			};
		}

		private static void Reset()
		{
			Seen.Clear();
			foreach (Achievement item in AchievementReader.All())
			{
				AchievementProgress achievementProgress = ProgressCache.Get(item);
				if (achievementProgress != null)
				{
					Seen[item.m_id] = Snapshot.Take(achievementProgress);
				}
			}
		}

		private static void OnChanged(Achievement a, AchievementProgress now)
		{
			//IL_0079: Unknown result type (might be due to invalid IL or missing references)
			//IL_007f: Unknown result type (might be due to invalid IL or missing references)
			Seen.TryGetValue(a.m_id, out var value);
			Seen[a.m_id] = Snapshot.Take(now);
			if (value == null || !PluginConfig.Enabled.Value)
			{
				return;
			}
			string text = ToastDiff.Diff(value, now, AchievementReader.Name(a), Pins.Contains(a.m_id), Rules);
			if (text != null && !((Object)(object)MessageHud.instance == (Object)null))
			{
				MessageType val = (MessageType)((PluginConfig.ToastAt.Value != ToastPosition.Center) ? 1 : 2);
				MessageHud.instance.ShowMessage(val, text, 0, (Sprite)null, false, true);
				if (PluginConfig.Verbose.Value)
				{
					MilestonesPlugin.Log.LogInfo((object)("Milestones: toast: " + text));
				}
			}
		}
	}
	internal sealed class TrackerHud : MonoBehaviour
	{
		private sealed class LineView
		{
			public RectTransform Root;

			public TextMeshProUGUI Name;

			public TextMeshProUGUI Value;

			public Bar Bar;

			public int ObjectiveIndex = -1;
		}

		private sealed class BlockView
		{
			public RectTransform Root;

			public Image Icon;

			public TextMeshProUGUI Title;

			public TextMeshProUGUI Overall;

			public Bar OverallBar;

			public readonly List<LineView> Lines = new List<LineView>();

			public TextMeshProUGUI More;
		}

		private const float StatusGap = 26f;

		private static TrackerHud _instance;

		private static bool _dirty = true;

		private RectTransform _panel;

		private CanvasGroup _group;

		private Canvas _canvas;

		private readonly Dictionary<string, BlockView> _blocks = new Dictionary<string, BlockView>();

		private TextMeshProUGUI _paused;

		private float _nextPausedCheck;

		private readonly Vector3[] _corners = (Vector3[])(object)new Vector3[4];

		public static void Init()
		{
			Runtime.Ticked += Ensure;
			Pins.Changed += delegate
			{
				_dirty = true;
			};
			ProgressCache.Changed += delegate(Achievement a, AchievementProgress p)
			{
				if (Pins.Contains(a.m_id))
				{
					_dirty = true;
				}
			};
			Runtime.PlayerSpawned += delegate
			{
				_dirty = true;
			};
			PluginConfig.MaxRowsPerAchievement.SettingChanged += delegate
			{
				Rebuild();
			};
		}

		private static void Rebuild()
		{
			if ((Object)(object)_instance != (Object)null)
			{
				foreach (BlockView value in _instance._blocks.Values)
				{
					Object.Destroy((Object)(object)((Component)value.Root).gameObject);
				}
				_instance._blocks.Clear();
			}
			_dirty = true;
		}

		private static void Ensure()
		{
			if ((Object)(object)_instance != (Object)null)
			{
				return;
			}
			Hud instance = Hud.instance;
			if (!((Object)(object)instance == (Object)null) && !((Object)(object)instance.m_rootObject == (Object)null))
			{
				RectTransform val = UiUtil.Rect("MilestonesTracker", instance.m_rootObject.transform);
				UiUtil.Stretch(val);
				TrackerHud trackerHud = ((Component)val).gameObject.AddComponent<TrackerHud>();
				try
				{
					trackerHud.Build();
				}
				catch (Exception e)
				{
					MilestonesPlugin.WarnOnce("tracker", e);
					Object.Destroy((Object)(object)((Component)val).gameObject);
					return;
				}
				_instance = trackerHud;
				_dirty = true;
			}
		}

		private void Build()
		{
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_0060: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bd: Expected O, but got Unknown
			//IL_013a: Unknown result type (might be due to invalid IL or missing references)
			_panel = UiUtil.Rect("panel", ((Component)this).transform);
			_panel.pivot = new Vector2(1f, 1f);
			Image obj = ((Component)_panel).gameObject.AddComponent<Image>();
			obj.sprite = UiUtil.White;
			((Graphic)obj).color = new Color(0f, 0f, 0f, 0.6f);
			((Graphic)obj).raycastTarget = false;
			_group = ((Component)_panel).gameObject.AddComponent<CanvasGroup>();
			_group.interactable = false;
			_group.blocksRaycasts = false;
			VerticalLayoutGroup obj2 = ((Component)_panel).gameObject.AddComponent<VerticalLayoutGroup>();
			((LayoutGroup)obj2).padding = new RectOffset(8, 8, 6, 6);
			((HorizontalOrVerticalLayoutGroup)obj2).spacing = 6f;
			((HorizontalOrVerticalLayoutGroup)obj2).childControlWidth = true;
			((HorizontalOrVerticalLayoutGroup)obj2).childControlHeight = true;
			((HorizontalOrVerticalLayoutGroup)obj2).childForceExpandWidth = true;
			((HorizontalOrVerticalLayoutGroup)obj2).childForceExpandHeight = false;
			ContentSizeFitter obj3 = ((Component)_panel).gameObject.AddComponent<ContentSizeFitter>();
			obj3.verticalFit = (FitMode)2;
			obj3.horizontalFit = (FitMode)0;
			_paused = UiUtil.Text((Transform)(object)_panel, "paused", 13f, (TextAlignmentOptions)513);
			((Graphic)_paused).color = new Color(0.7f, 0.7f, 0.7f, 0.8f);
			((Component)_paused).gameObject.SetActive(false);
			_canvas = ((Component)this).GetComponentInParent<Canvas>();
		}

		private void LateUpdate()
		{
			//IL_0103: Unknown result type (might be due to invalid IL or missing references)
			//IL_010d: Unknown result type (might be due to invalid IL or missing references)
			//IL_011d: Unknown result type (might be due to invalid IL or missing references)
			//IL_012c: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				bool flag = PluginConfig.Enabled.Value && PluginConfig.ShowTracker.Value && Pins.Count > 0 && (Object)(object)Player.m_localPlayer != (Object)null && (!PluginConfig.HideWhenInventoryOpen.Value || !InventoryGui.IsVisible());
				if (((Component)_panel).gameObject.activeSelf != flag)
				{
					((Component)_panel).gameObject.SetActive(flag);
				}
				if (!flag)
				{
					return;
				}
				if (_dirty)
				{
					_dirty = false;
					Refresh();
				}
				if (Runtime.Now >= _nextPausedCheck)
				{
					_nextPausedCheck = Runtime.Now + 1f;
					string text = CheatState.PausedReason();
					((Component)_paused).gameObject.SetActive(text != null);
					if (text != null)
					{
						((TMP_Text)_paused).text = "Achievements paused: " + text;
					}
				}
				_group.alpha = PluginConfig.Opacity.Value;
				_panel.sizeDelta = new Vector2(PluginConfig.Width.Value, _panel.sizeDelta.y);
				((Transform)_panel).localScale = Vector3.one * PluginConfig.Scale.Value;
				Place();
			}
			catch (Exception e)
			{
				MilestonesPlugin.WarnOnce("tracker", e);
			}
		}

		private void Place()
		{
			//IL_0216: Unknown result type (might be due to invalid IL or missing references)
			//IL_0217: Unknown result type (might be due to invalid IL or missing references)
			//IL_0218: Unknown result type (might be due to invalid IL or missing references)
			//IL_021f: Unknown result type (might be due to invalid IL or missing references)
			//IL_022c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0234: Unknown result type (might be due to invalid IL or missing references)
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0241: Unknown result type (might be due to invalid IL or missing references)
			//IL_0261: Unknown result type (might be due to invalid IL or missing references)
			//IL_026e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0299: Unknown result type (might be due to invalid IL or missing references)
			//IL_029a: Unknown result type (might be due to invalid IL or missing references)
			//IL_029b: Unknown result type (might be due to invalid IL or missing references)
			//IL_011d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0124: Unknown result type (might be due to invalid IL or missing references)
			//IL_013b: Unknown result type (might be due to invalid IL or missing references)
			//IL_014d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0162: Unknown result type (might be due to invalid IL or missing references)
			//IL_0171: Unknown result type (might be due to invalid IL or missing references)
			TrackerAnchor value = PluginConfig.Anchor.Value;
			Vector2 val = default(Vector2);
			((Vector2)(ref val))..ctor(PluginConfig.OffsetX.Value, PluginConfig.OffsetY.Value);
			Vector2 val3 = default(Vector2);
			Vector2 val2 = default(Vector2);
			switch (value)
			{
			case TrackerAnchor.UnderStatusEffects:
			{
				Hud instance = Hud.instance;
				float num = (((Object)(object)_canvas != (Object)null) ? _canvas.rootCanvas.scaleFactor : 1f);
				RectTransform statusEffectListRoot = instance.m_statusEffectListRoot;
				float num2 = ((Transform)statusEffectListRoot).position.x;
				float num3 = ((Transform)statusEffectListRoot).position.y;
				foreach (RectTransform statusEffect in instance.m_statusEffects)
				{
					if (!((Object)(object)statusEffect == (Object)null) && ((Component)statusEffect).gameObject.activeSelf)
					{
						statusEffect.GetWorldCorners(_corners);
						num2 = Mathf.Max(num2, _corners[2].x);
						num3 = Mathf.Min(num3, _corners[0].y);
					}
				}
				RectTransform panel = _panel;
				RectTransform panel2 = _panel;
				((Vector2)(ref val3))..ctor(0.5f, 0.5f);
				panel2.anchorMax = val3;
				panel.anchorMin = val3;
				_panel.pivot = new Vector2(1f, 1f);
				((Transform)_panel).position = new Vector3(num2 + val.x * num, num3 - 26f * num + val.y * num, 0f);
				return;
			}
			case TrackerAnchor.TopLeft:
				((Vector2)(ref val2))..ctor(0f, 1f);
				break;
			case TrackerAnchor.TopRight:
				((Vector2)(ref val2))..ctor(1f, 1f);
				break;
			case TrackerAnchor.LeftMiddle:
				((Vector2)(ref val2))..ctor(0f, 0.5f);
				break;
			case TrackerAnchor.RightMiddle:
				((Vector2)(ref val2))..ctor(1f, 0.5f);
				break;
			case TrackerAnchor.BottomLeft:
				((Vector2)(ref val2))..ctor(0f, 0f);
				break;
			default:
				((Vector2)(ref val2))..ctor(1f, 0f);
				break;
			}
			val3 = (_panel.anchorMin = (_panel.anchorMax = val2));
			_panel.pivot = val2;
			Vector2 val6 = default(Vector2);
			((Vector2)(ref val6))..ctor((val2.x < 0.5f) ? 16f : ((val2.x > 0.5f) ? (-16f) : 0f), (val2.y < 0.5f) ? 16f : ((val2.y > 0.5f) ? (-16f) : 0f));
			_panel.anchoredPosition = val6 + val;
		}

		private void Refresh()
		{
			HashSet<string> hashSet = new HashSet<string>(Pins.Ids);
			List<string> list = new List<string>();
			foreach (string key in _blocks.Keys)
			{
				if (!hashSet.Contains(key))
				{
					list.Add(key);
				}
			}
			foreach (string item in list)
			{
				Object.Destroy((Object)(object)((Component)_blocks[item].Root).gameObject);
				_blocks.Remove(item);
			}
			int num = 0;
			foreach (string id in Pins.Ids)
			{
				Achievement val = AchievementReader.ById(id);
				if ((Object)(object)val == (Object)null)
				{
					continue;
				}
				AchievementProgress achievementProgress = ProgressCache.Get(val);
				if (achievementProgress != null)
				{
					BlockView value;
					bool flag = !_blocks.TryGetValue(id, out value);
					if (flag)
					{
						value = NewBlock(val);
						_blocks[id] = value;
					}
					((Transform)value.Root).SetSiblingIndex(num++);
					Fill(value, val, achievementProgress, !flag);
				}
			}
			((TMP_Text)_paused).transform.SetAsLastSibling();
		}

		private BlockView NewBlock(Achievement a)
		{
			//IL_01de: Unknown result type (might be due to invalid IL or missing references)
			BlockView blockView = new BlockView();
			blockView.Root = UiUtil.Rect("block_" + a.m_id, (Transform)(object)_panel);
			VerticalLayoutGroup obj = ((Component)blockView.Root).gameObject.AddComponent<VerticalLayoutGroup>();
			((HorizontalOrVerticalLayoutGroup)obj).spacing = 2f;
			((HorizontalOrVerticalLayoutGroup)obj).childControlWidth = true;
			((HorizontalOrVerticalLayoutGroup)obj).childControlHeight = true;
			((HorizontalOrVerticalLayoutGroup)obj).childForceExpandWidth = true;
			((HorizontalOrVerticalLayoutGroup)obj).childForceExpandHeight = false;
			RectTransform val = UiUtil.Rect("head", (Transform)(object)blockView.Root);
			HorizontalLayoutGroup obj2 = ((Component)val).gameObject.AddComponent<HorizontalLayoutGroup>();
			((HorizontalOrVerticalLayoutGroup)obj2).spacing = 6f;
			((HorizontalOrVerticalLayoutGroup)obj2).childControlWidth = true;
			((HorizontalOrVerticalLayoutGroup)obj2).childControlHeight = true;
			((HorizontalOrVerticalLayoutGroup)obj2).childForceExpandWidth = false;
			((HorizontalOrVerticalLayoutGroup)obj2).childForceExpandHeight = false;
			((LayoutGroup)obj2).childAlignment = (TextAnchor)3;
			RectTransform val2 = UiUtil.Rect("icon", (Transform)(object)val);
			blockView.Icon = ((Component)val2).gameObject.AddComponent<Image>();
			((Graphic)blockView.Icon).raycastTarget = false;
			blockView.Icon.preserveAspect = true;
			LayoutElement obj3 = ((Component)val2).gameObject.AddComponent<LayoutElement>();
			float preferredWidth = (obj3.minWidth = 20f);
			obj3.preferredWidth = preferredWidth;
			preferredWidth = (obj3.minHeight = 20f);
			obj3.preferredHeight = preferredWidth;
			blockView.Title = UiUtil.Text((Transform)(object)val, "title", 16f, (TextAlignmentOptions)513);
			((Component)blockView.Title).gameObject.AddComponent<LayoutElement>().flexibleWidth = 1f;
			blockView.Overall = UiUtil.Text((Transform)(object)val, "overall", 14f, (TextAlignmentOptions)516);
			blockView.OverallBar = Bar.CreateInLayout((Transform)(object)blockView.Root, 6f);
			for (int i = 0; i < PluginConfig.MaxRowsPerAchievement.Value; i++)
			{
				blockView.Lines.Add(NewLine(blockView.Root, i));
			}
			blockView.More = UiUtil.Text((Transform)(object)blockView.Root, "more", 12f, (TextAlignmentOptions)513);
			((Graphic)blockView.More).color = new Color(0.7f, 0.7f, 0.7f, 1f);
			return blockView;
		}

		private static LineView NewLine(RectTransform parent, int i)
		{
			LineView lineView = new LineView();
			lineView.Root = UiUtil.Rect("line" + i, (Transform)(object)parent);
			VerticalLayoutGroup obj = ((Component)lineView.Root).gameObject.AddComponent<VerticalLayoutGroup>();
			((HorizontalOrVerticalLayoutGroup)obj).spacing = 1f;
			((HorizontalOrVerticalLayoutGroup)obj).childControlWidth = true;
			((HorizontalOrVerticalLayoutGroup)obj).childControlHeight = true;
			((HorizontalOrVerticalLayoutGroup)obj).childForceExpandWidth = true;
			((HorizontalOrVerticalLayoutGroup)obj).childForceExpandHeight = false;
			RectTransform val = UiUtil.Rect("text", (Transform)(object)lineView.Root);
			HorizontalLayoutGroup obj2 = ((Component)val).gameObject.AddComponent<HorizontalLayoutGroup>();
			((HorizontalOrVerticalLayoutGroup)obj2).childControlWidth = true;
			((HorizontalOrVerticalLayoutGroup)obj2).childControlHeight = true;
			((HorizontalOrVerticalLayoutGroup)obj2).childForceExpandWidth = false;
			lineView.Name = UiUtil.Text((Transform)(object)val, "name", 13f, (TextAlignmentOptions)513);
			((Component)lineView.Name).gameObject.AddComponent<LayoutElement>().flexibleWidth = 1f;
			lineView.Value = UiUtil.Text((Transform)(object)val, "value", 13f, (TextAlignmentOptions)516);
			lineView.Bar = Bar.CreateInLayout((Transform)(object)lineView.Root, 3f);
			return lineView;
		}

		private static void Fill(BlockView b, Achievement a, AchievementProgress p, bool ease)
		{
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0080: Unknown result type (might be due to invalid IL or missing references)
			//IL_019a: Unknown result type (might be due to invalid IL or missing references)
			//IL_019f: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b4: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ca: Unknown result type (might be due to invalid IL or missing references)
			b.Icon.sprite = (p.Unlocked ? a.m_icon : a.m_iconLocked);
			((TMP_Text)b.Title).text = AchievementReader.Name(a);
			((TMP_Text)b.Overall).text = (p.Unlocked ? "Done ✓" : Labels.OverallText(p));
			((Graphic)b.Overall).color = UiUtil.TextColor(p.Unlocked);
			b.OverallBar.Set(p.Overall, UiUtil.BarColor(p.Overall, p.Unlocked), ease);
			TrackerRows trackerRows = (p.Unlocked ? new TrackerRows() : ObjectiveOrder.Select(p, b.Lines.Count));
			for (int i = 0; i < b.Lines.Count; i++)
			{
				LineView lineView = b.Lines[i];
				bool flag = i < trackerRows.Indices.Count;
				((Component)lineView.Root).gameObject.SetActive(flag);
				if (!flag)
				{
					lineView.ObjectiveIndex = -1;
					continue;
				}
				int num = trackerRows.Indices[i];
				Objective objective = p.Objectives[num];
				((TMP_Text)lineView.Name).text = ((PluginConfig.HideUnmetNames.Value && !objective.Met) ? "???" : objective.Label);
				((TMP_Text)lineView.Value).text = (objective.HasBar ? (Labels.Amount(Math.Min(objective.Current, objective.Target)) + " / " + Labels.Amount(objective.Target)) : (objective.Met ? "met" : "not met"));
				Color color = UiUtil.TextColor(objective.Met);
				((Graphic)lineView.Name).color = color;
				((Graphic)lineView.Value).color = color;
				lineView.Bar.Set(objective.Fraction, UiUtil.BarColor(objective), ease && lineView.ObjectiveIndex == num);
				lineView.ObjectiveIndex = num;
			}
			((Component)b.More).gameObject.SetActive(trackerRows.More > 0);
			((TMP_Text)b.More).text = "+" + trackerRows.More + " more";
		}
	}
	internal static class UiUtil
	{
		private static Sprite _white;

		private static Material _outlined;

		private static TMP_FontAsset _outlinedFor;

		public static Sprite White
		{
			get
			{
				//IL_0017: Unknown result type (might be due to invalid IL or missing references)
				//IL_001d: Expected O, but got Unknown
				//IL_003f: Unknown result type (might be due to invalid IL or missing references)
				//IL_0044: Unknown result type (might be due to invalid IL or missing references)
				//IL_007d: Unknown result type (might be due to invalid IL or missing references)
				//IL_008c: Unknown result type (might be due to invalid IL or missing references)
				if ((Object)(object)_white != (Object)null)
				{
					return _white;
				}
				Texture2D val = new Texture2D(4, 4, (TextureFormat)4, false);
				Color32[] array = (Color32[])(object)new Color32[16];
				for (int i = 0; i < array.Length; i++)
				{
					array[i] = new Color32(byte.MaxValue, byte.MaxValue, byte.MaxValue, byte.MaxValue);
				}
				val.SetPixels32(array);
				val.Apply();
				((Object)val).hideFlags = (HideFlags)61;
				_white = Sprite.Create(val, new Rect(0f, 0f, 4f, 4f), new Vector2(0.5f, 0.5f), 4f);
				((Object)_white).hideFlags = (HideFlags)61;
				return _white;
			}
		}

		public static TMP_FontAsset Font
		{
			get
			{
				Hud instance = Hud.instance;
				if (!((Object)(object)instance != (Object)null) || !((Object)(object)instance.m_healthText != (Object)null))
				{
					return null;
				}
				return instance.m_healthText.font;
			}
		}

		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_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Expected O, but got Unknown
			RectTransform val = (RectTransform)new GameObject(name, new Type[1] { typeof(RectTransform) })
			{
				layer = ((Component)parent).gameObject.layer
			}.transform;
			((Transform)val).SetParent(parent, false);
			return val;
		}

		public static void Stretch(RectTransform rt)
		{
			//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_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			rt.anchorMin = Vector2.zero;
			rt.anchorMax = Vector2.one;
			rt.offsetMin = Vector2.zero;
			rt.offsetMax = Vector2.zero;
			((Transform)rt).localScale = Vector3.one;
		}

		private static Material Outlined(TMP_FontAsset font)
		{
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: Expected O, but got Unknown
			//IL_0071: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)_outlined != (Object)null && (Object)(object)_outlinedFor == (Object)(object)font)
			{
				return _outlined;
			}
			_outlined = new Material(((TMP_Asset)font).material);
			_outlined.EnableKeyword(ShaderUtilities.Keyword_Outline);
			_outlined.SetFloat(ShaderUtilities.ID_OutlineWidth, 0.25f);
			_outlined.SetColor(ShaderUtilities.ID_OutlineColor, new Color(0f, 0f, 0f, 1f));
			((Object)_outlined).hideFlags = (HideFlags)61;
			_outlinedFor = font;
			return _outlined;
		}

		public static TextMeshProUGUI Text(Transform parent, string name, float size, TextAlignmentOptions align)
		{
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_0084: Unknown result type (might be due to invalid IL or missing references)
			RectTransform obj = Rect(name, parent);
			((Component)obj).gameObject.SetActive(false);
			TextMeshProUGUI val = ((Component)obj).gameObject.AddComponent<TextMeshProUGUI>();
			TMP_FontAsset font = Font;
			if ((Object)(object)font != (Object)null)
			{
				((TMP_Text)val).font = font;
				((TMP_Text)val).fontSharedMaterial = Outlined(font);
			}
			((Component)obj).gameObject.SetActive(true);
			((TMP_Text)val).fontSize = size;
			((TMP_Text)val).alignment = align;
			((TMP_Text)val).textWrappingMode = (TextWrappingModes)0;
			((TMP_Text)val).overflowMode = (TextOverflowModes)1;
			((Graphic)val).raycastTarget = false;
			((Graphic)val).color = new Color(0.9f, 0.9f, 0.9f, 1f);
			return val;
		}

		public static Color BarColor(Objective o)
		{
			//IL_002b: 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_0015: Unknown result type (might be due to invalid IL or missing references)
			if (!o.Met)
			{
				if (!o.Started)
				{
					return PluginConfig.ColorNotStarted.Value;
				}
				return PluginConfig.ColorInProgress.Value;
			}
			return PluginConfig.ColorDone.Value;
		}

		public static Color BarColor(float fraction, bool done)
		{
			//IL_0026: 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_0010: Unknown result type (might be due to invalid IL or missing references)
			if (!done)
			{
				if (!(fraction > 0f))
				{
					return PluginConfig.ColorNotStarted.Value;
				}
				return PluginConfig.ColorInProgress.Value;
			}
			return PluginConfig.ColorDone.Value;
		}

		public static Color TextColor(bool done)
		{
			//IL_0022: 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)
			if (!done)
			{
				return new Color(0.85f, 0.85f, 0.85f, 1f);
			}
			return PluginConfig.ColorDone.Value;
		}
	}
}
namespace Milestones.Patches
{
	[HarmonyPatch(typeof(AchievementsGui), "OnOpenAchievementDetails")]
	internal static class OpenDetailsPatch
	{
		private static bool Prefix(AchievementsGui __instance, Achievement achievement, bool clickable)
		{
			if (!PluginConfig.Enabled.Value)
			{
				return true;
			}
			if (DetailsPanel.Failed)
			{
				return true;
			}
			if (__instance.m_achievementDetails.activeSelf || !clickable)
			{
				return true;
			}
			if (achievement.m_isSecret && !achievement.m_unlocked)
			{
				return true;
			}
			try
			{
				DetailsPanel.Open(__instance, achievement);
				return false;
			}
			catch (Exception e)
			{
				MilestonesPlugin.WarnOnce("details panel", e);
				DetailsPanel.MarkFailed();
				DetailsPanel.Abort(__instance);
				return true;
			}
		}
	}
	[HarmonyPatch(typeof(AchievementsGui), "CloseDetails")]
	internal static class CloseDetailsPatch
	{
		private static void Postfix()
		{
			DetailsPanel.Closed();
		}
	}
	[HarmonyPatch(typeof(InventoryGui), "UpdateAchievementsList")]
	internal static class UpdateAchievementsListPatch
	{
		private static void Postfix(InventoryGui __instance)
		{
			if (!PluginConfig.Enabled.Value)
			{
				return;
			}
			try
			{
				ListMarks.Apply(__instance);
			}
			catch (Exception e)
			{
				MilestonesPlugin.WarnOnce("grid pin marks", e);
			}
		}
	}
	[HarmonyPatch(typeof(PlayerProfile), "IncrementStat")]
	internal static class IncrementStatPatch
	{
		private static void Postfix(PlayerStatType stat)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			StatEvents.MarkStat(stat);
		}
	}
	[HarmonyPatch(typeof(PlayerProfile), "SetStat")]
	internal static class SetStatPatch
	{
		private static void Postfix(PlayerStatType stat)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			StatEvents.MarkStat(stat);
		}
	}
	[HarmonyPatch(typeof(PlayerProfile), "IncrementStatEnemy")]
	internal static class IncrementStatEnemyPatch
	{
		private static void Postfix()
		{
			StatEvents.Mark(StatKind.Enemy);
		}
	}
	[HarmonyPatch(typeof(PlayerProfile), "IncrementStatItemPickup")]
	internal static class IncrementStatItemPickupPatch
	{
		private static void Postfix()
		{
			StatEvents.Mark(StatKind.Pickup);
		}
	}
	[HarmonyPatch(typeof(PlayerProfile), "IncrementStatItemCraft")]
	internal static class IncrementStatItemCraftPatch
	{
		private static void Postfix()
		{
			StatEvents.Mark(StatKind.Craft);
		}
	}
	[HarmonyPatch(typeof(PlayerProfile), "IncrementStatPickable")]
	internal static class IncrementStatPickablePatch
	{
		private static void Postfix()
		{
			StatEvents.Mark(StatKind.Pickable);
		}
	}
	[HarmonyPatch(typeof(PlayerProfile), "IncrementStatFoodEaten")]
	internal static class IncrementStatFoodEatenPatch
	{
		private static void Postfix()
		{
			StatEvents.Mark(StatKind.Food);
		}
	}
	[HarmonyPatch(typeof(PlayerProfile), "IncrementStatBuildPiecePlaced")]
	internal static class IncrementStatBuildPiecePlacedPatch
	{
		private static void Postfix()
		{
			StatEvents.Mark(StatKind.Piece);
		}
	}
	[HarmonyPatch(typeof(Achievements), "AchievementEvent")]
	internal static class AchievementEventPatch
	{
		private static void Postfix(Achievement ach)
		{
			StatEvents.MarkUnlocked(ach);
		}
	}
}
namespace Milestones.Core
{
	internal static class AchievementReader
	{
		private static readonly Dictionary<string, string> LabelCache = new Dictionary<string, string>();

		internal static void ClearLabels()
		{
			LabelCache.Clear();
		}

		public static IEnumerable<Achievement> All()
		{
			Achievements instance = Achievements.m_instance;
			if ((Object)(object)instance == (Object)null)
			{
				yield break;
			}
			foreach (AchievementList achievementList in instance.m_achievementLists)
			{
				if ((Object)(object)achievementList == (Object)null)
				{
					continue;
				}
				foreach (Achievement achievement in achievementList.m_achievements)
				{
					if ((Object)(object)achievement != (Object)null)
					{
						yield return achievement;
					}
				}
			}
		}

		public static Achievement ById(string id)
		{
			foreach (Achievement item in All())
			{
				if (item.m_id == id)
				{
					return item;
				}
			}
			return null;
		}

		public static Achievement Find(string query)
		{
			foreach (Achievement item in All())
			{
				if (string.Equals(item.m_id, query, StringComparison.OrdinalIgnoreCase))
				{
					return item;
				}
			}
			foreach (Achievement item2 in All())
			{
				if (Name(item2).IndexOf(query, StringComparison.OrdinalIgnoreCase) >= 0)
				{
					return item2;
				}
			}
			return null;
		}

		public static string Name(Achievement a)
		{
			return Loc(a.m_name);
		}

		public static List<Achievement> GridOrder()
		{
			List<List<Achievement>> list = new List<List<Achievement>>();
			for (int i = 0; i <= 9; i++)
			{
				list.Add(new List<Achievement>());
			}
			foreach (Achievement item in All())
			{
				string text = (((Object)(object)item.m_iconLocked != (Object)null) ? ((Object)item.m_iconLocked).name : "");
				if (text.Length >= 2 && int.TryParse(text.Substring(0, 2), out var result) && result >= 0 && result <= 9)
				{
					list[result].Add(item);
				}
				else
				{
					list[9].Add(item);
				}
			}
			List<Achievement> list2 = new List<Achievement>();
			for (int j = 1; j <= 9; j++)
			{
				list2.AddRange(list[j]);
			}
			return list2;
		}

		public unsafe static List<ObjectiveInput> Read(Achievement a, PlayerProfile profile)
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Expected I4, but got Unknown
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ff: Unknown result type (might be due to invalid IL or missing references)
			//IL_0106: Expected I4, but got Unknown
			//IL_0126: Unknown result type (might be due to invalid IL or missing references)
			//IL_0144: Unknown result type (might be due to invalid IL or missing references)
			//IL_0151: Unknown result type (might be due to invalid IL or missing references)
			//IL_0158: Unknown result type (might be due to invalid IL or missing references)
			//IL_0164: Unknown result type (might be due to invalid IL or missing references)
			//IL_0166: Unknown result type (might be due to invalid IL or missing references)
			//IL_017b: Unknown result type (might be due to invalid IL or missing references)
			//IL_017d: Unknown result type (might be due to invalid IL or missing references)
			//IL_018d: Unknown result type (might be due to invalid IL or missing references)
			List<ObjectiveInput> list = new List<ObjectiveInput>();
			int num = (int)a.m_difficultyRequirement;
			if (num < 0 || num >= profile.m_playerStats.Length)
			{
				return list;
			}
			PlayerStats val = profile.m_playerStats[num];
			foreach (PlayerStatRequirement item in a.m_statTrigger)
			{
				float value;
				bool hasValue = val.m_stats.TryGetValue(item.m_stat, out value);
				PlayerStatType stat = item.m_stat;
				string text = ((object)(*(PlayerStatType*)(&stat))/*cast due to .constrained prefix*/).ToString();
				list.Add(new ObjectiveInput
				{
					Source = Source.PlayerStat,
					Key = text,
					Label = Label("$stat_" + text, text),
					Op = Op.AtLeast,
					Target = item.m_amountAboveEquals,
					HasValue = hasValue,
					Value = value
				});
			}
			foreach (EnemyStatRequirement enemyStatsTrigger in a.m_enemyStatsTriggers)
			{
				int num2 = (int)enemyStatsTrigger.m_modifier;
				if (num2 >= 0 && num2 < val.m_enemyStats.Length)
				{
					float value2;
					bool hasValue2 = val.m_enemyStats[num2].TryGetValue(enemyStatsTrigger.m_stat, out value2);
					list.Add(new ObjectiveInput
					{
						Source = Source.Enemy,
						Key = enemyStatsTrigger.m_stat,
						Label = Label(enemyStatsTrigger.m_stat, enemyStatsTrigger.m_stat) + ModifierSuffix(enemyStatsTrigger.m_modifier),
						Op = Map(enemyStatsTrigger.m_operator),
						Target = enemyStatsTrigger.m_amount,
						HasValue = hasValue2,
						Value = value2
					});
				}
			}
			AddDict(list, a.m_itemPickupTriggers, val.m_itemPickupStats, Source.ItemPickup);
			AddDict(list, a.m_itemCraftTriggers, val.m_itemCraftStats, Source.ItemCraft);
			AddDict(list, a.m_foodEatenTriggers, val.m_foodEatenStats, Source.FoodEaten);
			AddDict(list, a.m_pickableTriggers, val.m_pickableStats, Source.Pickable);
			AddDict(list, a.m_piecePlacedTriggers, val.m_piecesPlacedStats, Source.PiecePlaced);
			AddDict(list, a.m_knownWorldTriggers, val.m_knownWorlds, Source.KnownWorld);
			AddDict(list, a.m_knownWorldKeysTriggers, val.m_knownWorldKeys, Source.KnownWorldKey);
			AddDict(list, a.m_knownCommandsTriggers, val.m_knownCommands, Source.KnownCommand);
			foreach (Achievement otherAchievementTrigger in a.m_otherAchievementTriggers)
			{
				if (!((Object)(object)otherAchievementTrigger == (Object)null))
				{
					list.Add(new ObjectiveInput
					{
						Source = Source.OtherAchievement,
						Key = otherAchievementTrigger.m_id,
						Label = Loc(otherAchievementTrigger.m_name),
						Op = Op.AtLeast,
						Target = 1f,
						HasValue = true,
						Value = (otherAchievementTrigger.m_unlocked ? 1f : 0f)
					});
				}
			}
			return list;
		}

		private static void AddDict(List<ObjectiveInput> list, List<DictStatRequirement> triggers, Dictionary<string, float> dict, Source source)
		{
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: 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_0023: 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_004e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_0065: Unknown result type (might be due to invalid IL or missing references)
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			//IL_0076: Unknown result type (might be due to invalid IL or missing references)
			foreach (DictStatRequirement trigger in triggers)
			{
				float value = 0f;
				bool hasValue = trigger.m_stat != null && dict.TryGetValue(trigger.m_stat, out value);
				list.Add(new ObjectiveInput
				{
					Source = source,
					Key = trigger.m_stat,
					Label = Label(trigger.m_stat, trigger.m_stat),
					Op = Map(trigger.m_operator),
					Target = trigger.m_amount,
					HasValue = hasValue,
					Value = value
				});
			}
		}

		private static Op Map(RequirementOperator op)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0014: Expected I4, but got Unknown
			return (op - 1) switch
			{
				0 => Op.AtMost, 
				1 => Op.Exactly, 
				2 => Op.NotEqual, 
				_ => Op.AtLeast, 
			};
		}

		private static string ModifierSuffix(KillModifiers m)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Expected I4, but got Unknown
			return (m - 1) switch
			{
				0 => " (unarmed)", 
				1 => " (magic)", 
				2 => " (ranged)", 
				3 => " (melee)", 
				_ => "", 
			};
		}

		private static string Label(string token, string fallbackKey)
		{
			if (string.IsNullOrEmpty(token))
			{
				return "";
			}
			if (LabelCache.TryGetValue(token, out var value))
			{
				return value;
			}
			string text = Loc(token);
			if (text == token || text.Length == 0 || (text.StartsWith("[") && text.EndsWith("]")))
			{
				text = Labels.Humanize(fallbackKey);
			}
			LabelCache[token] = text;
			return text;
		}

		private static string Loc(string token)
		{
			if (string.IsNullOrEmpty(token))
			{
				return "";
			}
			return ((Localization.instance != null) ? Localization.instance.Localize(token) : token) ?? token;
		}
	}
	internal static class CheatState
	{
		public static string PausedReason()
		{
			if ((Object)(object)Achievements.m_instance == (Object)null || (Object)(object)Game.instance == (Object)null)
			{
				return null;
			}
			if (Achievements.CanGetAchievements(false))
			{
				return null;
			}
			if (Game.isModded)
			{
				return "a mod set Game.isModded";
			}
			if (Achievements.IsWorldCheated())
			{
				return "world modifiers";
			}
			if (Game.instance.GetPlayerProfile().m_usedCheats)
			{
				return "cheats used on this character";
			}
			if ((Object)(object)Player.m_localPlayer != (Object)null && ((Humanoid)Player.m_localPlayer).GetInventory().AnyCheatedItem())
			{
				return "a cheated item in your inventory";
			}
			return "the game's cheat check";
		}
	}
	internal static class ConsoleCommands
	{
		[Serializable]
		[CompilerGenerated]
		private sealed class <>c
		{
			public static readonly <>c <>9 = new <>c();

			public static ConsoleEvent <>9__0_0;

			internal void <Register>b__0_0(ConsoleEventArgs args)
			{
				if ((Object)(object)Achievements.m_instance == (Object)null || (Object)(object)Game.instance == (Object)null)
				{
					Say(args.Context, "Milestones: not in a game.");
					return;
				}
				string text = ((args.Length > 1) ? args[1] : "");
				string query = ((args.Length > 2) ? string.Join(" ", args.Args, 2, args.Length - 2) : "");
				string text2 = text.ToLowerInvariant();
				if (text2 == null || text2.Length != 0)
				{
					switch (text2)
					{
					case "ui":
						DumpUi(args.Context);
						break;
					case "pin":
					case "unpin":
						PinCommand(args.Context, text.ToLowerInvariant() == "pin", query);
						break;
					default:
						Detail(args.Context, string.Join(" ", args.Args, 1, args.Length - 1));
						break;
					}
				}
				else
				{
					Summary(args.Context);
				}
			}
		}

		public static void Register()
		{
			//IL_0033: 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_0029: Expected O, but got Unknown
			object obj = <>c.<>9__0_0;
			if (obj == null)
			{
				ConsoleEvent val = delegate(ConsoleEventArgs args)
				{
					if ((Object)(object)Achievements.m_instance == (Object)null || (Object)(object)Game.instance == (Object)null)
					{
						Say(args.Context, "Milestones: not in a game.");
					}
					else
					{
						string text = ((args.Length > 1) ? args[1] : "");
						string query = ((args.Length > 2) ? string.Join(" ", args.Args, 2, args.Length - 2) : "");
						string text2 = text.ToLowerInvariant();
						if (text2 == null || text2.Length != 0)
						{
							switch (text2)
							{
							case "ui":
								DumpUi(args.Context);
								break;
							case "pin":
							case "unpin":
								PinCommand(args.Context, text.ToLowerInvariant() == "pin", query);
								break;
							default:
								Detail(args.Context, string.Join(" ", args.Args, 1, args.Length - 1));
								break;
							}
						}
						else
						{
							Summary(args.Context);
						}
					}
				};
				<>c.<>9__0_0 = val;
				obj = (object)val;
			}
			new ConsoleCommand("milestones", "Milestones: progress (<id|name> | pin <id> | unpin <id> | ui)", (ConsoleEvent)obj, false, false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false);
		}

		internal static void Say(Terminal ctx, string line)
		{
			if ((Object)(object)ctx != (Object)null)
			{
				ctx.AddString(line);
			}
			MilestonesPlugin.Log.LogInfo((object)line);
		}

		private static void PinCommand(Terminal ctx, bool pin, string query)
		{
			Achievement val = AchievementReader.Find(query);
			if ((Object)(object)val == (Object)null)
			{
				Say(ctx, "Milestones: no achievement matches \"" + query + "\".");
				return;
			}
			bool flag = (pin ? Pins.Pin(val.m_id) : Pins.Unpin(val.m_id));
			Say(ctx, "Milestones: " + ((!flag) ? "unchanged " : (pin ? "pinned " : "unpinned ")) + val.m_id + " (" + Pins.Count + "/" + 3 + ")" + ((!flag && pin && Pins.IsFull) ? " — three pins already" : ""));
		}

		private static void Summary(Terminal ctx)
		{
			Say(ctx, "Milestones: pinned = " + ((Pins.Count == 0) ? "(none)" : string.Join(", ", Pins.Ids)));
			string text = CheatState.PausedReason();
			if (text != null)
			{
				Say(ctx, "Milestones: achievements paused: " + text);
			}
			int num = 0;
			foreach (Achievement item in AchievementReader.All())
			{
				AchievementProgress achievementProgress = ProgressCache.Get(item);
				if (achievementProgress != null)
				{
					num++;
					Say(ctx, string.Format("Milestones: {0} {1} \"{2}\" {3} ({4}/{5} met)", achievementProgress.Unlocked ? "[x]" : "[ ]", item.m_id, AchievementReader.Name(item), Labels.OverallText(achievementProgress), achievementProgress.MetCount, achievementProgress.Total));
				}
			}
			Say(ctx, "Milestones: " + num + " achievement(s).");
		}

		private static void Detail(Terminal ctx, string query)
		{
			//IL_0064: Unknown result type (might be due to invalid IL or missing references)
			Achievement val = AchievementReader.Find(query);
			if ((Object)(object)val == (Object)null)
			{
				Say(ctx, "Milestones: no achievement matches \"" + query + "\".");
				return;
			}
			ProgressCache.Recompute(val);
			AchievementProgress achievementProgress = ProgressCache.Get(val);
			if (achievementProgress == null)
			{
				Say(ctx, "Milestones: no profile loaded.");
				return;
			}
			Say(ctx, string.Format("Milestones: {0} \"{1}\" slot={2} lenient={3} overall={4} {5}", val.m_id, AchievementReader.Name(val), val.m_difficultyRequirement, val.m_lenientBuildAchievement, Labels.OverallText(achievementProgress), achievementProgress.Unlocked ? "UNLOCKED" : ""));
			foreach (Objective objective in achievementProgress.Objectives)
			{
				Say(ctx, string.Format("Milestones:   {0} {1} [{2} {3}] {4} {5}", objective.Met ? "[x]" : "[ ]", objective.Label, objective.Source, objective.Key, objective.Op, Labels.ProgressText(objective)));
			}
		}

		private static void DumpUi(Terminal ctx)
		{
			InventoryGui instance = InventoryGui.instance;
			if ((Object)(object)instance == (Object)null || (Object)(object)instance.m_achievementsPanel == (Object)null)
			{
				Say(ctx, "Milestones: no achievements panel.");
				return;
			}
			Dump(ctx, instance.m_achievementsPanel.m_achievementDetails.transform, 0, 3);
			if ((Object)(object)instance.m_achievementsPanel.m_achievementDetailsElementPrefab != (Object)null)
			{
				Dump(ctx, ((Component)instance.m_achievementsPanel.m_achievementDetailsElementPrefab).transform, 0, 3);
			}
		}

		private static void Dump(Terminal ctx, Transform t, int depth, int maxDepth)
		{
			//IL_009c: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00aa: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fa: Unknown result type (might be due to invalid IL or missing references)
			//IL_011d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0122: Unknown result type (might be due to invalid IL or missing references)
			//IL_0156: Unknown result type (might be due to invalid IL or missing references)
			//IL_015d: Expected O, but got Unknown
			RectTransform val = (RectTransform)(object)((t is RectTransform) ? t : null);
			StringBuilder stringBuilder = new StringBuilder();
			Component[] components = ((Component)t).GetComponents<Component>();
			foreach (Component val2 in components)
			{
				if ((Object)(object)val2 != (Object)null && !(val2 is Transform))
				{
					stringBuilder.Append(((object)val2).GetType().Name).Append(' ');
				}
			}
			object[] obj = new object[8]
			{
				new string(' ', depth * 2),
				((Object)t).name,
				((Component)t).gameObject.activeSelf,
				null,
				null,
				null,
				null,
				null
			};
			object obj2;
			if (!((Object)(object)val != (Object)null))
			{
				obj2 = "-";
			}
			else
			{
				Rect rect = val.rect;
				obj2 = ((object)((Rect)(ref rect)).size/*cast due to .constrained prefix*/).ToString();
			}
			obj[3] = obj2;
			obj[4] = (((Object)(object)val != (Object)null) ? ((object)val.anchorMin/*cast due to .constrained prefix*/).ToString() : "-");
			obj[5] = (((Object)(object)val != (Object)null) ? ((object)val.anchorMax/*cast due to .constrained prefix*/).ToString() : "-");
			obj[6] = (((Object)(object)val != (Object)null) ? ((object)val.anchoredPosition/*cast due to .constrained prefix*/).ToString() : "-");
			obj[7] = stringBuilder;
			Say(ctx, string.Format("Milestones: {0}{1} active={2} size={3} anchors={4}-{5} pos={6} [{7}]", obj));
			if (depth >= maxDepth)
			{
				return;
			}
			foreach (Transform item in t)
			{
				Transform t2 = item;
				Dump(ctx, t2, depth + 1, maxDepth);
			}
		}
	}
	internal static class Pins
	{
		private const string Key = "milestones.pins";

		private const float UnpinDelay = 10f;

		private static PinList _list;

		private static string _profile;

		private static Player _owner;

		private static readonly Dictionary<string, float> UnpinAt;

		public static IReadOnlyList<string> Ids => _list.Ids;

		public static int Count => _list.Count;

		public static bool IsFull => _list.IsFull;

		public static event Action Changed;

		static Pins()
		{
			_list = new PinList();
			UnpinAt = new Dictionary<string, float>();
			Runtime.PlayerSpawned += OnPlayerSpawned;
			StatEvents.Unlocked += OnUnlocked;
		}

		public static void Init()
		{
		}

		private static void OnPlayerSpawned(Player p)
		{
			string filename = Game.instance.GetPlayerProfile().GetFilename();
			if (filename != _profile)
			{
				p.m_customData.TryGetValue("milestones.pins", out var value);
				_list = PinList.Parse(value);
				_profile = filename;
				UnpinAt.Clear();
			}
			_owner = p;
			_list.RemoveWhere((string id) => (Object)(object)AchievementReader.ById(id) == (Object)null);
			Save();
			Raise();
		}

		public static bool Contains(string id)
		{
			return _list.Contains(id);
		}

		public static bool Toggle(Achievement a)
		{
			if (!Contains(a.m_id))
			{
				return Pin(a.m_id);
			}
			return Unpin(a.m_id);
		}

		public static bool Pin(string id)
		{
			if (!_list.Add(id))
			{
				return false;
			}
			Save();
			Raise();
			return true;
		}

		public static bool Unpin(string id)
		{
			UnpinAt.Remove(id);
			if (!_list.Remove(id))
			{
				return false;
			}
			Save();
			Raise();
			return true;
		}

		public static void Tick(float now)
		{
			if (UnpinAt.Count == 0)
			{
				return;
			}
			string text = null;
			foreach (KeyValuePair<string, float> item in UnpinAt)
			{
				if (now >= item.Value)
				{
					text = item.Key;
					break;
				}
			}
			if (text != null)
			{
				Unpin(text);
			}
		}

		private static void OnUnlocked(Achievement a)
		{
			if (PluginConfig.AutoUnpinCompleted.Value && Contains(a.m_id))
			{
				UnpinAt[a.m_id] = Runtime.Now + 10f;
			}
		}

		private static void Save()
		{
			if (!((Object)(object)_owner == (Object)null))
			{
				if (_list.Count == 0)
				{
					_owner.m_customData.Remove("milestones.pins");
				}
				else
				{
					_owner.m_customData["milestones.pins"] = _list.Serialize();
				}
			}
		}

		private static void Raise()
		{
			if (PluginConfig.Verbose.Value)
			{
				MilestonesPlugin.Log.LogInfo((object)("Milestones: pins = " + _list.Serialize()));
			}
			if (Pins.Changed != null)
			{
				Pins.Changed();
			}
		}
	}
	internal static class ProgressCache
	{
		private sealed class Index
		{
			public StatKind Kinds;

			public readonly HashSet<PlayerStatType> Stats = new HashSet<PlayerStatType>();
		}

		private static readonly Dictionary<string, AchievementProgress> ById = new Dictionary<string, AchievementProgress>();

		private static readonly Dictionary<string, Index> Indexes = new Dictionary<string, Index>();

		private static readonly HashSet<PlayerStatType> DirtyStats = new HashSet<PlayerStatType>();

		public static event Action<Achievement, AchievementProgress> Changed;

		public static void Clear()
		{
			ById.Clear();
			Indexes.Clear();
			AchievementReader.ClearLabels();
		}

		public static AchievementProgress Get(Achievement a)
		{
			if (ById.TryGetValue(a.m_id, out var value))
			{
				return value;
			}
			value = Compute(a);
			if (value != null)
			{
				ById[a.m_id] = value;
			}
			return value;
		}

		public static void Recompute(Achievement a)
		{
			AchievementProgress achievementProgress = Compute(a);
			if (achievementProgress == null)
			{
				return;
			}
			AchievementProgress value;
			bool num = ById.TryGetValue(a.m_id, out value);
			ById[a.m_id] = achievementProgress;
			if (num && !ProgressCalc.SameValues(value, achievementProgress) && ProgressCache.Changed != null)
			{
				if (PluginConfig.Verbose.Value)
				{
					MilestonesPlugin.Log.LogInfo((object)("Milestones: " + a.m_id + " → " + Labels.OverallText(achievementProgress)));
				}
				ProgressCache.Changed(a, achievementProgress);
			}
		}

		public static void ProcessDirty()
		{
			StatKind statKind = StatEvents.Take(DirtyStats);
			if (statKind == StatKind.None)
			{
				return;
			}
			foreach (Achievement item in AchievementReader.All())
			{
				if (ById.ContainsKey(item.m_id))
				{
					Index index = IndexOf(item);
					if ((statKind & StatKind.Unlock) != StatKind.None || (index.Kinds & statKind & ~StatKind.Player) != StatKind.None || ((statKind & StatKind.Player) != StatKind.None && index.Stats.Overlaps(DirtyStats)))
					{
						Recompute(item);
					}
				}
			}
		}

		public static void RefreshAll()
		{
			foreach (Achievement item in AchievementReader.All())
			{
				Recompute(item);
			}
		}

		private static AchievementProgress Compute(Achievement a)
		{
			PlayerProfile val = (((Object)(object)Game.instance != (Object)null) ? Game.instance.GetPlayerProfile() : null);
			if (val == null)
			{
				return null;
			}
			return ProgressCalc.Compute(a.m_id, AchievementReader.Read(a, val), a.m_unlocked, a.m_lenientBuildAchievement, PluginConfig.Overall.Value);
		}

		private static Index IndexOf(Achievement a)
		{
			//IL_002c: 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_0046: 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)
			if (Indexes.TryGetValue(a.m_id, out var value))
			{
				return value;
			}
			value = new Index();
			foreach (PlayerStatRequirement item in a.m_statTrigger)
			{
				value.Kinds |= StatKind.Player;
				value.Stats.Add(item.m_stat);
			}
			if (a.m_enemyStatsTriggers.Count > 0)
			{
				value.Kinds |= StatKind.Enemy;
			}
			if (a.m_itemPickupTriggers.Count > 0)
			{
				value.Kinds |= StatKind.Pickup;
			}
			if (a.m_itemCraftTriggers.Count > 0)
			{
				value.Kinds |= StatKind.Craft;
			}
			if (a.m_pickableTriggers.Count > 0)
			{
				value.Kinds |= StatKind.Pickable;
			}
			if (a.m_foodEatenTriggers.Count > 0)
			{
				value.Kinds |= StatKind.Food;
			}
			if (a.m_piecePlacedTriggers.Count > 0)
			{
				value.Kinds |= StatKind.Piece;
			}
			Indexes[a.m_id] = value;
			return value;
		}
	}
	internal static class Runtime
	{
		private const float DirtyInterval = 0.25f;

		private const float SweepInterval = 10f;

		private static Player _player;

		private static float _nextDirty;

		private static float _nextSweep;

		public static float Now => Time.unscaledTime;

		public static event Action<Player> PlayerSpawned;

		public static event Action Ticked;

		public static void Tick()
		{
			try
			{
				Player localPlayer = Player.m_localPlayer;
				if ((Object)(object)localPlayer != (Object)(object)_player && ((Object)(object)localPlayer == (Object)null || (Object)(object)Achievements.m_instance != (Object)null))
				{
					_player = localPlayer;
					ProgressCache.Clear();
					if ((Object)(object)localPlayer != (Object)null)
					{
						MilestonesPlugin.Log.LogInfo((object)("Milestones: player spawned; Game.isModded = " + Game.isModded));
						if (Runtime.PlayerSpawned != null)
						{
							Runtime.PlayerSpawned(localPlayer);
						}
					}
				}
				if ((Object)(object)_player == (Object)null || !PluginConfig.Enabled.Value)
				{
					return;
				}
				float now = Now;
				Pins.Tick(now);
				if (now >= _nextDirty)
				{
					_nextDirty = now + 0.25f;
					if (StatEvents.Any)
					{
						ProgressCache.ProcessDirty();
					}
				}
				if (now >= _nextSweep)
				{
					_nextSweep = now + 10f;
					ProgressCache.RefreshAll();
				}
				if (Runtime.Ticked != null)
				{
					Runtime.Ticked();
				}
			}
			catch (Exception e)
			{
				MilestonesPlugin.WarnOnce("tick", e);
			}
		}
	}
	[Flags]
	internal enum StatKind
	{
		None = 0,
		Player = 1,
		Enemy = 2,
		Pickup = 4,
		Craft = 8,
		Pickable = 0x10,
		Food = 0x20,
		Piece = 0x40,
		Unlock = 0x80,
		All = 0xFF
	}
	internal static class StatEvents
	{
		private static StatKind _kinds;

		private static readonly HashSet<PlayerStatType> Stats = new HashSet<PlayerStatType>();

		public static bool Any => _kinds != StatKind.None;

		public static event Action<Achievement> Unlocked;

		public static void Mark(StatKind kind)
		{
			_kinds |= kind;
		}

		public static void MarkStat(PlayerStatType stat)
		{
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			_kinds |= StatKind.Player;
			Stats.Add(stat);
		}

		public static void MarkUnlocked(Achievement a)
		{
			_kinds |= StatKind.All;
			if (StatEvents.Unlocked != null)
			{
				StatEvents.Unlocked(a);
			}
		}

		public static StatKind Take(HashSet<PlayerStatType> statsOut)
		{
			statsOut.Clear();
			statsOut.UnionWith(Stats);
			Stats.Clear();
			StatKind kinds = _kinds;
			_kinds = StatKind.None;
			return kinds;
		}
	}
}
namespace Milestones.Core.Model
{
	public static class Labels
	{
		public const string Check = "✓";

		public static string Humanize(string key)
		{
			if (string.IsNullOrEmpty(key))
			{
				return "";
			}
			string text = key.TrimStart(new char[1] { '$' });
			int num = text.IndexOf('_');
			if (key.StartsWith("$") && num >= 0 && num < text.Length - 1)
			{
				text = text.Substring(num + 1);
			}
			StringBuilder stringBuilder = new StringBuilder(text.Length + 8);
			for (int i = 0; i < text.Length; i++)
			{
				char c = text[i];
				if (c == '_')
				{
					stringBuilder.Append(' ');
					continue;
				}
				if (i > 0 && char.IsUpper(c) && char.IsLower(text[i - 1]))
				{
					stringBuilder.Append(' ');
				}
				stringBuilder.Append((stringBuilder.Length == 0) ? char.ToUpperInvariant(c) : char.ToLowerInvariant(c));
			}
			return stringBuilder.ToString();
		}

		public static string Amount(float v)
		{
			if (Math.Abs(v) >= 100f)
			{
				return Math.Round(v).ToString("0", CultureInfo.InvariantCulture);
			}
			return v.ToString("0.#", CultureInfo.InvariantCulture);
		}

		public static string Percent(float fraction)
		{
			return ((int)Math.Floor(((fraction < 0f) ? 0f : ((fraction > 1f) ? 1f : fraction)) * 100f + 0.0001f)).ToString(CultureInfo.InvariantCulture) + "%";
		}

		public static string ProgressText(Objective o)
		{
			if (!o.HasBar)
			{
				if (!o.Met)
				{
					return "not met";
				}
				return "met";
			}
			float v = Math.Min(o.Current, o.Target);
			return Amount(v) + " / " + Amount(o.Target) + "  " + Percent(o.Fraction);
		}

		public static string OverallText(AchievementProgress p)
		{
			if (!p.ShowAsCount)
			{
				return Percent(p.Overall);
			}
			return p.MetCount + " / " + p.Total;
		}
	}
	public enum Op
	{
		AtLeast,
		AtMost,
		Exactly,
		NotEqual
	}
	public enum Source
	{
		PlayerStat,
		Enemy,
		ItemPickup,
		ItemCraft,
		FoodEaten,
		Pickable,
		PiecePlaced,
		KnownWorld,
		KnownWorldKey,
		KnownCommand,
		OtherAchievement
	}
	public enum OverallMode
	{
		Auto,
		Average,
		Count
	}
	public enum PinnedToastMode
	{
		ObjectivesAndThresholds,
		ThresholdsOnly,
		Off
	}
	public enum UnpinnedToastMode
	{
		ThresholdsOnly,
		Off
	}
	public sealed class ObjectiveInput
	{
		public Source Source;

		public string Key;

		public string Label;

		public Op Op;

		public float Target;

		public bool HasValue;

		public float Value;
	}
	public sealed class Objective
	{
		public Source Source;

		public string Key;

		public string Label;

		public Op Op;

		public float Current;

		public float Target;

		public bool Met;

		public bool HasBar;

		public float Fraction;

		public bool Started
		{
			get
			{
				if (!Met)
				{
					return Current > 0f;
				}
				return true;
			}
		}
	}
	public sealed class AchievementProgress
	{
		public string Id;

		public List<Objective> Objectives = new List<Objective>();

		public int MetCount;

		public float Overall;

		public bool ShowAsCount;

		public bool Unlocked;

		public int Total => Objectives.Count;
	}
	public sealed class TrackerRows
	{
		public List<int> Indices = new List<int>();

		public int More;
	}
	public static class ObjectiveOrder
	{
		public static List<int> Sorted(AchievementProgress p)
		{
			List<int> list = new List<int>(p.Total);
			for (int i = 0; i < p.Total; i++)
			{
				list.Add(i);
			}
			list.Sort(delegate(int a, int b)
			{
				Objective objective = p.Objectives[a];
				Objective objective2 = p.Objectives[b];
				if (objective.Met != objective2.Met)
				{
					if (!objective.Met)
					{
						return -1;
					}
					return 1;
				}
				return (!objective.Met && objective.Fraction != objective2.Fraction) ? objective2.Fraction.CompareTo(objective.Fraction) : a.CompareTo(b);
			});
			return list;
		}

		public static TrackerRows Select(AchievementProgress p, int max)
		{
			TrackerRows trackerRows = new TrackerRows();
			if (p.Total <= max)
			{
				for (int i = 0; i < p.Total; i++)
				{
					trackerRows.Indices.Add(i);
				}
				return trackerRows;
			}
			int num = 0;
			foreach (int item in Sorted(p))
			{
				if (p.Objectives[item].Met)
				{
					break;
				}
				num++;
				if (trackerRows.Indices.Count < max)
				{
					trackerRows.Indices.Add(item);
				}
			}
			trackerRows.More = num - trackerRows.Indices.Count;
			return trackerRows;
		}
	}
	public sealed class PinList
	{
		public const int Max = 3;

		private readonly List<string> _ids = new List<string>(3);

		public IReadOnlyList<string> Ids => _ids;

		public int Count => _ids.Count;

		public bool IsFull => _ids.Count >= 3;

		public static PinList Parse(string raw)
		{
			PinList pinList = new PinList();
			if (string.IsNullOrEmpty(raw))
			{
				return pinList;
			}
			string[] array = raw.Split(new char[1] { ',' });
			foreach (string text in array)
			{
				pinList.Add(text.Trim());
			}
			return pinList;
		}

		public string Serialize()
		{
			return string.Join(",", _ids);
		}

		public bool Contains(string id)
		{
			return _ids.Contains(id);
		}

		public bool Add(string id)
		{
			if (string.IsNullOrEmpty(id) || id.IndexOf(',') >= 0 || IsFull || _ids.Contains(id))
			{
				return false;
			}
			_ids.Add(id);
			return true;
		}

		public bool Remove(string id)
		{
			return _ids.Remove(id);
		}

		public int RemoveWhere(Func<string, bool> drop)
		{
			return _ids.RemoveAll((string id) => drop(id));
		}
	}
	public static class ProgressCalc
	{
		public const float LenientFactor = 1.15f;

		public static Objective Evaluate(ObjectiveInput i)
		{
			float num = (i.HasValue ? i.Value : 0f);
			bool flag = i.HasValue && i.Op switch
			{
				Op.AtLeast => num >= i.Target, 
				Op.AtMost => num <= i.Target, 
				Op.Exactly => num == i.Target, 
				_ => num != i.Target, 
			};
			bool flag2 = i.Op == Op.AtLeast;
			float fraction = (flag ? 1f : ((!flag2 || !(i.Target > 0f)) ? 0f : Clamp01(num / i.Target)));
			return new Objective
			{
				Source = i.Source,
				Key = i.Key,
				Label = i.Label,
				Op = i.Op,
				Current = num,
				Target = i.Target,
				Met = flag,
				HasBar = flag2,
				Fraction = fraction
			};
		}

		public static AchievementProgress Compute(string id, IList<ObjectiveInput> inputs, bool unlocked, bool lenient, OverallMode mode)
		{
			AchievementProgress achievementProgress = new AchievementProgress
			{
				Id = id,
				Unlocked = unlocked
			};
			bool flag = inputs.Count > 0;
			float num = 0f;
			float num2 = 0f;
			float num3 = 0f;
			foreach (ObjectiveInput input in inputs)
			{
				Objective objective = Evaluate(input);
				achievementProgress.Objectives.Add(objective);
				if (objective.Met)
				{
					achievementProgress.MetCount++;
				}
				num += objective.Fraction;
				if (objective.Op != Op.AtLeast || objective.Target != 1f)
				{
					flag = false;
				}
				if (lenient && objective.Source == Source.PlayerStat)
				{
					num2 += objective.Target;
					num3 += Math.Min(objective.Current, objective.Target * 1.15f);
				}
			}
			int count = achievementProgress.Objectives.Count;
			if (lenient)
			{
				achievementProgress.ShowAsCount = false;
				achievementProgress.Overall = ((num2 > 0f) ? Clamp01(num3 / num2) : 0f);
			}
			else
			{
				achievementProgress.ShowAsCount = mode == OverallMode.Count || (mode == OverallMode.Auto && flag);
				if (count == 0)
				{
					achievementProgress.Overall = 0f;
				}
				else if (achievementProgress.ShowAsCount)
				{
					achievementProgress.Overall = (float)achievementProgress.MetCount / (float)count;
				}
				else
				{
					achievementProgress.Overall = num / (float)count;
				}
			}
			if (unlocked)
			{
				achievementProgress.Overall = 1f;
			}
			return achievementProgress;
		}

		public static bool SameValues(AchievementProgress a, AchievementProgress b)
		{
			if (a == null || b == null)
			{
				return false;
			}
			if (a.Unlocked != b.Unlocked || a.MetCount != b.MetCount || a.Overall != b.Overall || a.Total != b.Total)
			{
				return false;
			}
			for (int i = 0; i < a.Total; i++)
			{
				if (a.Objectives[i].Current != b.Objectives[i].Current || a.Objectives[i].Met != b.Objectives[i].Met)
				{
					return false;
				}
			}
			return true;
		}

		private static float Clamp01(float v)
		{
			if (!(v < 0f))
			{
				if (!(v > 1f))
				{
					return v;
				}
				return 1f;
			}
			return 0f;
		}
	}
	public sealed class Snapshot
	{
		public float Overall;

		public bool[] Met;

		public bool Unlocked;

		public static Snapshot Take(AchievementProgress p)
		{
			bool[] array = new bool[p.Total];
			for (int i = 0; i < array.Length; i++)
			{
				array[i] = p.Objectives[i].Met;
			}
			return new Snapshot
			{
				Overall = p.Overall,
				Met = array,
				Unlocked = p.Unlocked
			};
		}
	}
	public sealed class ToastRules
	{
		public PinnedToastMode Pinned;

		public UnpinnedToastMode Unpinned;

		public int[] Thresholds = new int[0];

		public static int[] ParseThresholds(string raw)
		{
			SortedSet<int> sortedSet = new SortedSet<int>();
			if (!string.IsNullOrEmpty(raw))
			{
				string[] array = raw.Split(new char[1] { ',' });
				for (int i = 0; i < array.Length; i++)
				{
					if (int.TryParse(array[i].Trim(), NumberStyles.Integer, CultureInfo.InvariantCulture, out var result) && result >= 1 && result <= 99)
					{
						sortedSet.Add(result);
					}
				}
			}
			int[] array2 = new int[sortedSet.Count];
			sortedSet.CopyTo(array2);
			return array2;
		}
	}
	public static class ToastDiff
	{
		private const string Prefix = "Milestones: ";

		public static string Diff(Snapshot before, AchievementProgress now, string name, bool pinned, ToastRules rules)
		{
			if (before == null || now.Unlocked || before.Unlocked)
			{
				return null;
			}
			bool flag = pinned && rules.Pinned == PinnedToastMode.ObjectivesAndThresholds;
			bool num = (pinned ? (rules.Pinned != PinnedToastMode.Off) : (rules.Unpinned == UnpinnedToastMode.ThresholdsOnly));
			int num2 = 0;
			if (num)
			{
				int num3 = Pct(before.Overall);
				int num4 = Pct(now.Overall);
				int[] thresholds = rules.Thresholds;
				foreach (int num5 in thresholds)
				{
					if (num3 < num5 && num4 >= num5)
					{
						num2 = num5;
					}
				}
			}
			List<int> list = new List<int>();
			if (flag && before.Met != null && before.Met.Length == now.Total)
			{
				for (int j = 0; j < now.Total; j++)
				{
					if (!before.Met[j] && now.Objectives[j].Met)
					{
						list.Add(j);
					}
				}
			}
			if (list.Count == 0)
			{
				if (num2 <= 0)
				{
					return null;
				}
				return "Milestones: " + name + " " + num2 + "%";
			}
			string text = ((list.Count == 1) ? now.Objectives[list[0]].Label : (list.Count + " objectives"));
			string text2 = "Milestones: " + text + " ✓ · " + name + " " + Labels.OverallText(now);
			if (num2 > 0 && now.ShowAsCount)
			{
				text2 = text2 + " · " + num2 + "%";
			}
			return text2;
		}

		private static int Pct(float f)
		{
			return (int)Math.Floor(Math.Max(0f, Math.Min(1f, f)) * 100f + 0.0001f);
		}
	}
}
namespace System.Runtime.CompilerServices
{
	[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
	internal sealed class IgnoresAccessChecksToAttribute : Attribute
	{
		public IgnoresAccessChecksToAttribute(string assemblyName)
		{
		}
	}
}